<commit_before>/*
 * A Demo to OpenCV Implementation of SURF
 * Further Information Refer to "SURF: Speed-Up Robust Feature"
 * Author: Liu Liu
 * liuliu.1987+opencv@gmail.com
 */
#include "opencv2/core/core.hpp"
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include <opencv2/imgproc/imgproc_c.h>

#include <iostream>
#include <vector>

using namespace std;
using namespace cv;

void help()
{
    printf( "\n This program demonstrated the use of the SURF Detector and Descriptor using\n"
            "either FLANN (fast approx nearst neighbor classification) or brute force matching\n"
            "on planar objects.\n"
            "Usage: \n"
            "./find_obj [--object_filename]=<object_filename, box.png as default> \n"
            "       [--scene_filename]=<scene_filename box_in_scene.png as default>] \n"
            "Example: \n"
            "./find_obj --object_filename =box.png --scene_filename = box_in_scene.png \n\n"
            );
}

// define whether to use approximate nearest-neighbor search
#define USE_FLANN

IplImage* image = 0;

double
compareSURFDescriptors( const float* d1, const float* d2, double best, int length )
{
    double total_cost = 0;
    assert( length % 4 == 0 );
    for( int i = 0; i < length; i += 4 )
    {
        double t0 = d1[i  ] - d2[i  ];
        double t1 = d1[i+1] - d2[i+1];
        double t2 = d1[i+2] - d2[i+2];
        double t3 = d1[i+3] - d2[i+3];
        total_cost += t0*t0 + t1*t1 + t2*t2 + t3*t3;
        if( total_cost > best )
            break;
    }
    return total_cost;
}

int
naiveNearestNeighbor( const float* vec, int laplacian,
                      const CvSeq* model_keypoints,
                      const CvSeq* model_descriptors )
{
    int length = (int)(model_descriptors->elem_size/sizeof(float));
    int i, neighbor = -1;
    double d, dist1 = 1e6, dist2 = 1e6;
    CvSeqReader reader, kreader;
    cvStartReadSeq( model_keypoints, &kreader, 0 );
    cvStartReadSeq( model_descriptors, &reader, 0 );

    for( i = 0; i < model_descriptors->total; i++ )
    {
        const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
        const float* mvec = (const float*)reader.ptr;
    	CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
        CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
        if( laplacian != kp->laplacian )
            continue;
        d = compareSURFDescriptors( vec, mvec, dist2, length );
        if( d < dist1 )
        {
            dist2 = dist1;
            dist1 = d;
            neighbor = i;
        }
        else if ( d < dist2 )
            dist2 = d;
    }
    if ( dist1 < 0.6*dist2 )
        return neighbor;
    return -1;
}

void
findPairs( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
           const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, vector<int>& ptpairs )
{
    int i;
    CvSeqReader reader, kreader;
    cvStartReadSeq( objectKeypoints, &kreader );
    cvStartReadSeq( objectDescriptors, &reader );
    ptpairs.clear();

    for( i = 0; i < objectDescriptors->total; i++ )
    {
        const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
        const float* descriptor = (const float*)reader.ptr;
        CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
        CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
        int nearest_neighbor = naiveNearestNeighbor( descriptor, kp->laplacian, imageKeypoints, imageDescriptors );
        if( nearest_neighbor >= 0 )
        {
            ptpairs.push_back(i);
            ptpairs.push_back(nearest_neighbor);
        }
    }
}

void
flannFindPairs( const CvSeq*, const CvSeq* objectDescriptors,
           const CvSeq*, const CvSeq* imageDescriptors, vector<int>& ptpairs )
{
	int length = (int)(objectDescriptors->elem_size/sizeof(float));

    cv::Mat m_object(objectDescriptors->total, length, CV_32F);
	cv::Mat m_image(imageDescriptors->total, length, CV_32F);

	// copy descriptors
    CvSeqReader obj_reader;
	float* obj_ptr = m_object.ptr<float>(0);
    cvStartReadSeq( objectDescriptors, &obj_reader );
    for(int i = 0; i < objectDescriptors->total; i++ )
    {
        const float* descriptor = (const float*)obj_reader.ptr;
        CV_NEXT_SEQ_ELEM( obj_reader.seq->elem_size, obj_reader );
        memcpy(obj_ptr, descriptor, length*sizeof(float));
        obj_ptr += length;
    }
    CvSeqReader img_reader;
	float* img_ptr = m_image.ptr<float>(0);
    cvStartReadSeq( imageDescriptors, &img_reader );
    for(int i = 0; i < imageDescriptors->total; i++ )
    {
        const float* descriptor = (const float*)img_reader.ptr;
        CV_NEXT_SEQ_ELEM( img_reader.seq->elem_size, img_reader );
        memcpy(img_ptr, descriptor, length*sizeof(float));
        img_ptr += length;
    }

    // find nearest neighbors using FLANN
    cv::Mat m_indices(objectDescriptors->total, 2, CV_32S);
    cv::Mat m_dists(objectDescriptors->total, 2, CV_32F);
    cv::flann::Index flann_index(m_image, cv::flann::KDTreeIndexParams(4));  // using 4 randomized kdtrees
    flann_index.knnSearch(m_object, m_indices, m_dists, 2, cv::flann::SearchParams(64) ); // maximum number of leafs checked

    int* indices_ptr = m_indices.ptr<int>(0);
    float* dists_ptr = m_dists.ptr<float>(0);
    for (int i=0;i<m_indices.rows;++i) {
    	if (dists_ptr[2*i]<0.6*dists_ptr[2*i+1]) {
    		ptpairs.push_back(i);
    		ptpairs.push_back(indices_ptr[2*i]);
    	}
    }
}

/* a rough implementation for object location */
int
locatePlanarObject( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
                    const CvSeq* imageKeypoints, const CvSeq* imageDescriptors,
                    const CvPoint src_corners[4], CvPoint dst_corners[4] )
{
    double h[9];
    CvMat _h = cvMat(3, 3, CV_64F, h);
    vector<int> ptpairs;
    vector<CvPoint2D32f> pt1, pt2;
    CvMat _pt1, _pt2;
    int i, n;

#ifdef USE_FLANN
    flannFindPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#else
    findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#endif

    n = (int)(ptpairs.size()/2);
    if( n < 4 )
        return 0;

    pt1.resize(n);
    pt2.resize(n);
    for( i = 0; i < n; i++ )
    {
        pt1[i] = ((CvSURFPoint*)cvGetSeqElem(objectKeypoints,ptpairs[i*2]))->pt;
        pt2[i] = ((CvSURFPoint*)cvGetSeqElem(imageKeypoints,ptpairs[i*2+1]))->pt;
    }

    _pt1 = cvMat(1, n, CV_32FC2, &pt1[0] );
    _pt2 = cvMat(1, n, CV_32FC2, &pt2[0] );
    if( !cvFindHomography( &_pt1, &_pt2, &_h, CV_RANSAC, 5 ))
        return 0;

    for( i = 0; i < 4; i++ )
    {
        double x = src_corners[i].x, y = src_corners[i].y;
        double Z = 1./(h[6]*x + h[7]*y + h[8]);
        double X = (h[0]*x + h[1]*y + h[2])*Z;
        double Y = (h[3]*x + h[4]*y + h[5])*Z;
        dst_corners[i] = cvPoint(cvRound(X), cvRound(Y));
    }

    return 1;
}

int main(int argc, const char** argv)
{
    help();

    CommandLineParser parser(argc, argv);

    string objectFileName = parser.get<string>("object_filename", "box.png");
    string sceneFileName = parser.get<string>("scene_filename", "box_in_scene.png");

    CvMemStorage* storage = cvCreateMemStorage(0);
    cvNamedWindow("Object", 1);
    cvNamedWindow("Object Correspond", 1);

    static CvScalar colors[] =
    {
        {{0,0,255}},
        {{0,128,255}},
        {{0,255,255}},
        {{0,255,0}},
        {{255,128,0}},
        {{255,255,0}},
        {{255,0,0}},
        {{255,0,255}},
        {{255,255,255}}
    };

    IplImage* object = cvLoadImage( objectFileName.c_str(), CV_LOAD_IMAGE_GRAYSCALE );
    IplImage* image = cvLoadImage( sceneFileName.c_str(), CV_LOAD_IMAGE_GRAYSCALE );
    if( !object || !image )
    {
        fprintf( stderr, "Can not load %s and/or %s\n", objectFileName.c_str(), sceneFileName.c_str() );
        exit(-1);
    }
    IplImage* object_color = cvCreateImage(cvGetSize(object), 8, 3);
    cvCvtColor( object, object_color, CV_GRAY2BGR );

    CvSeq *objectKeypoints = 0, *objectDescriptors = 0;
    CvSeq *imageKeypoints = 0, *imageDescriptors = 0;
    int i;
    CvSURFParams params = cvSURFParams(500, 1);

    double tt = (double)cvGetTickCount();
    cvExtractSURF( object, 0, &objectKeypoints, &objectDescriptors, storage, params );
    printf("Object Descriptors: %d\n", objectDescriptors->total);
    cvExtractSURF( image, 0, &imageKeypoints, &imageDescriptors, storage, params );
    printf("Image Descriptors: %d\n", imageDescriptors->total);
    tt = (double)cvGetTickCount() - tt;
    printf( "Extraction time = %gms\n", tt/(cvGetTickFrequency()*1000.));
    CvPoint src_corners[4] = {{0,0}, {object->width,0}, {object->width, object->height}, {0, object->height}};
    CvPoint dst_corners[4];
    IplImage* correspond = cvCreateImage( cvSize(image->width, object->height+image->height), 8, 1 );
    cvSetImageROI( correspond, cvRect( 0, 0, object->width, object->height ) );
    cvCopy( object, correspond );
    cvSetImageROI( correspond, cvRect( 0, object->height, correspond->width, correspond->height ) );
    cvCopy( image, correspond );
    cvResetImageROI( correspond );

#ifdef USE_FLANN
    printf("Using approximate nearest neighbor search\n");
#endif

    if( locatePlanarObject( objectKeypoints, objectDescriptors, imageKeypoints,
        imageDescriptors, src_corners, dst_corners ))
    {
        for( i = 0; i < 4; i++ )
        {
            CvPoint r1 = dst_corners[i%4];
            CvPoint r2 = dst_corners[(i+1)%4];
            cvLine( correspond, cvPoint(r1.x, r1.y+object->height ),
                cvPoint(r2.x, r2.y+object->height ), colors[8] );
        }
    }
    vector<int> ptpairs;
#ifdef USE_FLANN
    flannFindPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#else
    findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#endif
    for( i = 0; i < (int)ptpairs.size(); i += 2 )
    {
        CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, ptpairs[i] );
        CvSURFPoint* r2 = (CvSURFPoint*)cvGetSeqElem( imageKeypoints, ptpairs[i+1] );
        cvLine( correspond, cvPointFrom32f(r1->pt),
            cvPoint(cvRound(r2->pt.x), cvRound(r2->pt.y+object->height)), colors[8] );
    }

    cvShowImage( "Object Correspond", correspond );
    for( i = 0; i < objectKeypoints->total; i++ )
    {
        CvSURFPoint* r = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
        CvPoint center;
        int radius;
        center.x = cvRound(r->pt.x);
        center.y = cvRound(r->pt.y);
        radius = cvRound(r->size*1.2/9.*2);
        cvCircle( object_color, center, radius, colors[0], 1, 8, 0 );
    }
    cvShowImage( "Object", object_color );

    cvWaitKey(0);

    cvDestroyWindow("Object");
    cvDestroyWindow("Object Correspond");

    return 0;
}
<commit_msg>Fixed mistakes in help<commit_after>/*
 * A Demo to OpenCV Implementation of SURF
 * Further Information Refer to "SURF: Speed-Up Robust Feature"
 * Author: Liu Liu
 * liuliu.1987+opencv@gmail.com
 */
#include "opencv2/core/core.hpp"
#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include <opencv2/imgproc/imgproc_c.h>

#include <iostream>
#include <vector>

using namespace std;
using namespace cv;

void help()
{
    printf( "\n This program demonstrated the use of the SURF Detector and Descriptor using\n"
            "either FLANN (fast approx nearst neighbor classification) or brute force matching\n"
            "on planar objects.\n"
            "Usage: \n"
            "./find_obj [--object_filename]=<object_filename, box.png as default> \n"
            "       [--scene_filename]=<scene_filename box_in_scene.png as default>] \n"
            "Example: \n"
            "./find_obj --object_filename=box.png --scene_filename=box_in_scene.png \n\n"
            );
}

// define whether to use approximate nearest-neighbor search
#define USE_FLANN

IplImage* image = 0;

double
compareSURFDescriptors( const float* d1, const float* d2, double best, int length )
{
    double total_cost = 0;
    assert( length % 4 == 0 );
    for( int i = 0; i < length; i += 4 )
    {
        double t0 = d1[i  ] - d2[i  ];
        double t1 = d1[i+1] - d2[i+1];
        double t2 = d1[i+2] - d2[i+2];
        double t3 = d1[i+3] - d2[i+3];
        total_cost += t0*t0 + t1*t1 + t2*t2 + t3*t3;
        if( total_cost > best )
            break;
    }
    return total_cost;
}

int
naiveNearestNeighbor( const float* vec, int laplacian,
                      const CvSeq* model_keypoints,
                      const CvSeq* model_descriptors )
{
    int length = (int)(model_descriptors->elem_size/sizeof(float));
    int i, neighbor = -1;
    double d, dist1 = 1e6, dist2 = 1e6;
    CvSeqReader reader, kreader;
    cvStartReadSeq( model_keypoints, &kreader, 0 );
    cvStartReadSeq( model_descriptors, &reader, 0 );

    for( i = 0; i < model_descriptors->total; i++ )
    {
        const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
        const float* mvec = (const float*)reader.ptr;
    	CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
        CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
        if( laplacian != kp->laplacian )
            continue;
        d = compareSURFDescriptors( vec, mvec, dist2, length );
        if( d < dist1 )
        {
            dist2 = dist1;
            dist1 = d;
            neighbor = i;
        }
        else if ( d < dist2 )
            dist2 = d;
    }
    if ( dist1 < 0.6*dist2 )
        return neighbor;
    return -1;
}

void
findPairs( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
           const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, vector<int>& ptpairs )
{
    int i;
    CvSeqReader reader, kreader;
    cvStartReadSeq( objectKeypoints, &kreader );
    cvStartReadSeq( objectDescriptors, &reader );
    ptpairs.clear();

    for( i = 0; i < objectDescriptors->total; i++ )
    {
        const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr;
        const float* descriptor = (const float*)reader.ptr;
        CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader );
        CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
        int nearest_neighbor = naiveNearestNeighbor( descriptor, kp->laplacian, imageKeypoints, imageDescriptors );
        if( nearest_neighbor >= 0 )
        {
            ptpairs.push_back(i);
            ptpairs.push_back(nearest_neighbor);
        }
    }
}

void
flannFindPairs( const CvSeq*, const CvSeq* objectDescriptors,
           const CvSeq*, const CvSeq* imageDescriptors, vector<int>& ptpairs )
{
	int length = (int)(objectDescriptors->elem_size/sizeof(float));

    cv::Mat m_object(objectDescriptors->total, length, CV_32F);
	cv::Mat m_image(imageDescriptors->total, length, CV_32F);

	// copy descriptors
    CvSeqReader obj_reader;
	float* obj_ptr = m_object.ptr<float>(0);
    cvStartReadSeq( objectDescriptors, &obj_reader );
    for(int i = 0; i < objectDescriptors->total; i++ )
    {
        const float* descriptor = (const float*)obj_reader.ptr;
        CV_NEXT_SEQ_ELEM( obj_reader.seq->elem_size, obj_reader );
        memcpy(obj_ptr, descriptor, length*sizeof(float));
        obj_ptr += length;
    }
    CvSeqReader img_reader;
	float* img_ptr = m_image.ptr<float>(0);
    cvStartReadSeq( imageDescriptors, &img_reader );
    for(int i = 0; i < imageDescriptors->total; i++ )
    {
        const float* descriptor = (const float*)img_reader.ptr;
        CV_NEXT_SEQ_ELEM( img_reader.seq->elem_size, img_reader );
        memcpy(img_ptr, descriptor, length*sizeof(float));
        img_ptr += length;
    }

    // find nearest neighbors using FLANN
    cv::Mat m_indices(objectDescriptors->total, 2, CV_32S);
    cv::Mat m_dists(objectDescriptors->total, 2, CV_32F);
    cv::flann::Index flann_index(m_image, cv::flann::KDTreeIndexParams(4));  // using 4 randomized kdtrees
    flann_index.knnSearch(m_object, m_indices, m_dists, 2, cv::flann::SearchParams(64) ); // maximum number of leafs checked

    int* indices_ptr = m_indices.ptr<int>(0);
    float* dists_ptr = m_dists.ptr<float>(0);
    for (int i=0;i<m_indices.rows;++i) {
    	if (dists_ptr[2*i]<0.6*dists_ptr[2*i+1]) {
    		ptpairs.push_back(i);
    		ptpairs.push_back(indices_ptr[2*i]);
    	}
    }
}

/* a rough implementation for object location */
int
locatePlanarObject( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors,
                    const CvSeq* imageKeypoints, const CvSeq* imageDescriptors,
                    const CvPoint src_corners[4], CvPoint dst_corners[4] )
{
    double h[9];
    CvMat _h = cvMat(3, 3, CV_64F, h);
    vector<int> ptpairs;
    vector<CvPoint2D32f> pt1, pt2;
    CvMat _pt1, _pt2;
    int i, n;

#ifdef USE_FLANN
    flannFindPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#else
    findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#endif

    n = (int)(ptpairs.size()/2);
    if( n < 4 )
        return 0;

    pt1.resize(n);
    pt2.resize(n);
    for( i = 0; i < n; i++ )
    {
        pt1[i] = ((CvSURFPoint*)cvGetSeqElem(objectKeypoints,ptpairs[i*2]))->pt;
        pt2[i] = ((CvSURFPoint*)cvGetSeqElem(imageKeypoints,ptpairs[i*2+1]))->pt;
    }

    _pt1 = cvMat(1, n, CV_32FC2, &pt1[0] );
    _pt2 = cvMat(1, n, CV_32FC2, &pt2[0] );
    if( !cvFindHomography( &_pt1, &_pt2, &_h, CV_RANSAC, 5 ))
        return 0;

    for( i = 0; i < 4; i++ )
    {
        double x = src_corners[i].x, y = src_corners[i].y;
        double Z = 1./(h[6]*x + h[7]*y + h[8]);
        double X = (h[0]*x + h[1]*y + h[2])*Z;
        double Y = (h[3]*x + h[4]*y + h[5])*Z;
        dst_corners[i] = cvPoint(cvRound(X), cvRound(Y));
    }

    return 1;
}

int main(int argc, const char** argv)
{
    help();

    CommandLineParser parser(argc, argv);

    string objectFileName = parser.get<string>("object_filename", "box.png");
    string sceneFileName = parser.get<string>("scene_filename", "box_in_scene.png");

    CvMemStorage* storage = cvCreateMemStorage(0);
    cvNamedWindow("Object", 1);
    cvNamedWindow("Object Correspond", 1);

    static CvScalar colors[] =
    {
        {{0,0,255}},
        {{0,128,255}},
        {{0,255,255}},
        {{0,255,0}},
        {{255,128,0}},
        {{255,255,0}},
        {{255,0,0}},
        {{255,0,255}},
        {{255,255,255}}
    };

    IplImage* object = cvLoadImage( objectFileName.c_str(), CV_LOAD_IMAGE_GRAYSCALE );
    IplImage* image = cvLoadImage( sceneFileName.c_str(), CV_LOAD_IMAGE_GRAYSCALE );
    if( !object || !image )
    {
        fprintf( stderr, "Can not load %s and/or %s\n", objectFileName.c_str(), sceneFileName.c_str() );
        exit(-1);
    }
    IplImage* object_color = cvCreateImage(cvGetSize(object), 8, 3);
    cvCvtColor( object, object_color, CV_GRAY2BGR );

    CvSeq *objectKeypoints = 0, *objectDescriptors = 0;
    CvSeq *imageKeypoints = 0, *imageDescriptors = 0;
    int i;
    CvSURFParams params = cvSURFParams(500, 1);

    double tt = (double)cvGetTickCount();
    cvExtractSURF( object, 0, &objectKeypoints, &objectDescriptors, storage, params );
    printf("Object Descriptors: %d\n", objectDescriptors->total);
    cvExtractSURF( image, 0, &imageKeypoints, &imageDescriptors, storage, params );
    printf("Image Descriptors: %d\n", imageDescriptors->total);
    tt = (double)cvGetTickCount() - tt;
    printf( "Extraction time = %gms\n", tt/(cvGetTickFrequency()*1000.));
    CvPoint src_corners[4] = {{0,0}, {object->width,0}, {object->width, object->height}, {0, object->height}};
    CvPoint dst_corners[4];
    IplImage* correspond = cvCreateImage( cvSize(image->width, object->height+image->height), 8, 1 );
    cvSetImageROI( correspond, cvRect( 0, 0, object->width, object->height ) );
    cvCopy( object, correspond );
    cvSetImageROI( correspond, cvRect( 0, object->height, correspond->width, correspond->height ) );
    cvCopy( image, correspond );
    cvResetImageROI( correspond );

#ifdef USE_FLANN
    printf("Using approximate nearest neighbor search\n");
#endif

    if( locatePlanarObject( objectKeypoints, objectDescriptors, imageKeypoints,
        imageDescriptors, src_corners, dst_corners ))
    {
        for( i = 0; i < 4; i++ )
        {
            CvPoint r1 = dst_corners[i%4];
            CvPoint r2 = dst_corners[(i+1)%4];
            cvLine( correspond, cvPoint(r1.x, r1.y+object->height ),
                cvPoint(r2.x, r2.y+object->height ), colors[8] );
        }
    }
    vector<int> ptpairs;
#ifdef USE_FLANN
    flannFindPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#else
    findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs );
#endif
    for( i = 0; i < (int)ptpairs.size(); i += 2 )
    {
        CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, ptpairs[i] );
        CvSURFPoint* r2 = (CvSURFPoint*)cvGetSeqElem( imageKeypoints, ptpairs[i+1] );
        cvLine( correspond, cvPointFrom32f(r1->pt),
            cvPoint(cvRound(r2->pt.x), cvRound(r2->pt.y+object->height)), colors[8] );
    }

    cvShowImage( "Object Correspond", correspond );
    for( i = 0; i < objectKeypoints->total; i++ )
    {
        CvSURFPoint* r = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
        CvPoint center;
        int radius;
        center.x = cvRound(r->pt.x);
        center.y = cvRound(r->pt.y);
        radius = cvRound(r->size*1.2/9.*2);
        cvCircle( object_color, center, radius, colors[0], 1, 8, 0 );
    }
    cvShowImage( "Object", object_color );

    cvWaitKey(0);

    cvDestroyWindow("Object");
    cvDestroyWindow("Object Correspond");

    return 0;
}
<|endoftext|>


<commit_before>// Module:  Log4CPLUS
// File:    threads.cxx
// Created: 6/2001
// Author:  Tad E. Smith
//
//
// Copyright (C) The Apache Software Foundation. All rights reserved.
//
// This software is published under the terms of the Apache Software
// License version 1.1, a copy of which has been included with this
// distribution in the LICENSE.APL file.
//

#include <log4cplus/helpers/loglog.h>
#include <log4cplus/helpers/threads.h>
#include <exception>
#include <sstream>
#include <stdexcept>
#include <errno.h>
#include <time.h>

#ifdef LOG4CPLUS_USE_PTHREADS
#    include <sched.h>
#endif

using namespace log4cplus::helpers;

#ifndef LOG4CPLUS_SINGLE_THREADED

LOG4CPLUS_MUTEX_PTR_DECLARE
log4cplus::thread::createNewMutex()
{
#ifdef LOG4CPLUS_USE_PTHREADS
    pthread_mutex_t* m = new pthread_mutex_t();
    pthread_mutex_init(m, NULL);
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    CRITICAL_SECTION* m = new CRITICAL_SECTION();
    InitializeCriticalSection(m);
#endif
    return m;
}

void
log4cplus::thread::deleteMutex(LOG4CPLUS_MUTEX_PTR_DECLARE m)
{
#ifdef LOG4CPLUS_USE_PTHREADS
    pthread_mutex_destroy(m);
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    DeleteCriticalSection(m);
#endif
    delete m;
}

#ifdef LOG4CPLUS_USE_PTHREADS
pthread_key_t*
log4cplus::thread::createPthreadKey()
{
    pthread_key_t* key = new pthread_key_t();
    pthread_key_create(key, NULL);
    return key;
}
#endif

log4cplus::thread::Guard::~Guard()
{
    try {
        LOG4CPLUS_MUTEX_UNLOCK( _mutex );
    }
    catch(exception& e) {
        helpers::getLogLog().error(std::string("**** Guard::~Guard() exception: ") + e.what());
        throw;
    }
    catch(...) {
        helpers::getLogLog().error("**** Guard::~Guard() exception!!!!");
        throw;
    }
}

void
log4cplus::thread::yield()
{
#ifdef LOG4CPLUS_USE_PTHREADS
    sched_yield();
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    Sleep(0);
#endif
}

#define MILLIS_TO_NANOS 1000000
#define SEC_TO_MILLIS 1000
#define MAX_SECS_HELD_IN_DWORD (DWORD)4294966        // 2**32/1000

void
log4cplus::thread::sleep(unsigned long secs, unsigned long nanosecs)
{
#ifdef LOG4CPLUS_USE_PTHREADS
    timespec sleeptime = { secs, nanosecs };
    timespec remain;
    while (nanosleep(&sleeptime, &remain)) {
        if (errno == EINTR) {
            sleeptime.tv_sec  = remain.tv_sec;
            sleeptime.tv_nsec = remain.tv_nsec;
            continue;
        }
        else {
            return;
        }
    }
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    // Sleep for the specified number of nano seconds
    Sleep(nanosecs / static_cast<unsigned long>(MILLIS_TO_NANOS));

    // Now sleep for the specified number of seconds.  This may mean we
    // have to enter a loop if secs converted to milliseconds is greater
    // than what a DWORD can hold
    DWORD numSleepLoops = secs / MAX_SECS_HELD_IN_DWORD;
    for(DWORD i=0; i<numSleepLoops; ++i) {
        Sleep(MAX_SECS_HELD_IN_DWORD * SEC_TO_MILLIS);
    }

    Sleep((secs % MAX_SECS_HELD_IN_DWORD) * SEC_TO_MILLIS);
#endif
}

#ifdef LOG4CPLUS_USE_PTHREADS
void*
log4cplus::thread::threadStartFunc(void* arg)
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
DWORD WINAPI
log4cplus::thread::threadStartFunc(LPVOID arg)
#endif
{
    if(arg == NULL) {
        getLogLog().error("log4cplus::thread::threadStartFunc()- arg is NULL");
    }
    else {
        AbstractThread* thread = static_cast<AbstractThread*>(arg);
        try {
            log4cplus::helpers::SharedObjectPtr<AbstractThread> ptr(thread);
            thread->run();
        }
        catch(...) {
            // TODO --> Log
        }
        thread->running = false;
    }

#ifdef LOG4CPLUS_USE_PTHREADS
    pthread_exit(NULL);
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    return NULL;
#endif
}

log4cplus::thread::AbstractThread::AbstractThread()
: running(false)
{
}

log4cplus::thread::AbstractThread::~AbstractThread()
{
}

void
log4cplus::thread::AbstractThread::start()
{
    running = true;
#ifdef LOG4CPLUS_USE_PTHREADS
    if( pthread_create(&threadId, NULL, threadStartFunc, this) ) {
        throw std::runtime_error("Thread creation was not successful");
    }
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    HANDLE h = CreateThread(NULL, 0, threadStartFunc, (LPVOID)this, 0, &threadId);
    CloseHandle(h);
#endif
}

#endif // LOG4CPLUS_SINGLE_THREADED

<commit_msg>Fixed sleep() method.<commit_after>// Module:  Log4CPLUS
// File:    threads.cxx
// Created: 6/2001
// Author:  Tad E. Smith
//
//
// Copyright (C) The Apache Software Foundation. All rights reserved.
//
// This software is published under the terms of the Apache Software
// License version 1.1, a copy of which has been included with this
// distribution in the LICENSE.APL file.
//

#include <log4cplus/helpers/loglog.h>
#include <log4cplus/helpers/threads.h>
#include <exception>
#include <sstream>
#include <stdexcept>
#include <errno.h>
#include <time.h>

#ifdef LOG4CPLUS_USE_PTHREADS
#    include <sched.h>
#endif

using namespace log4cplus::helpers;

#ifndef LOG4CPLUS_SINGLE_THREADED

LOG4CPLUS_MUTEX_PTR_DECLARE
log4cplus::thread::createNewMutex()
{
#ifdef LOG4CPLUS_USE_PTHREADS
    pthread_mutex_t* m = new pthread_mutex_t();
    pthread_mutex_init(m, NULL);
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    CRITICAL_SECTION* m = new CRITICAL_SECTION();
    InitializeCriticalSection(m);
#endif
    return m;
}

void
log4cplus::thread::deleteMutex(LOG4CPLUS_MUTEX_PTR_DECLARE m)
{
#ifdef LOG4CPLUS_USE_PTHREADS
    pthread_mutex_destroy(m);
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    DeleteCriticalSection(m);
#endif
    delete m;
}

#ifdef LOG4CPLUS_USE_PTHREADS
pthread_key_t*
log4cplus::thread::createPthreadKey()
{
    pthread_key_t* key = new pthread_key_t();
    pthread_key_create(key, NULL);
    return key;
}
#endif

log4cplus::thread::Guard::~Guard()
{
    try {
        LOG4CPLUS_MUTEX_UNLOCK( _mutex );
    }
    catch(exception& e) {
        helpers::getLogLog().error(std::string("**** Guard::~Guard() exception: ") + e.what());
        throw;
    }
    catch(...) {
        helpers::getLogLog().error("**** Guard::~Guard() exception!!!!");
        throw;
    }
}

void
log4cplus::thread::yield()
{
#ifdef LOG4CPLUS_USE_PTHREADS
    sched_yield();
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    Sleep(0);
#endif
}

#define MILLIS_TO_NANOS 1000000
#define SEC_TO_MILLIS 1000
#define MAX_SLEEP_SECONDS (DWORD)4294966        // (2**32-2)/1000

void
log4cplus::thread::sleep(unsigned long secs, unsigned long nanosecs)
{
#ifdef LOG4CPLUS_USE_PTHREADS
    timespec sleep_time = { secs, nanosecs };
    timespec remain;
    while (nanosleep(&sleep_time, &remain)) {
        if (errno == EINTR) {
            sleep_time.tv_sec  = remain.tv_sec;
            sleep_time.tv_nsec = remain.tv_nsec;
            continue;
        }
        else {
            return;
        }
    }
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    DWORD nano_millis = nanosecs / static_cast<unsigned long>(MILLIS_TO_NANOS);
    if (secs <= MAX_SLEEP_SECONDS) {
        Sleep((secs * SEC_TO_MILLIS) + nano_millis);
        return;
    }

    DWORD no_of_max_sleeps = secs / MAX_SLEEP_SECONDS;

    for(DWORD i = 0; i < no_of_max_sleeps; i++) {
        Sleep(MAX_SLEEP_SECONDS * 1000);
    }

    Sleep((secs % MAX_SLEEP_SECONDS) * SEC_TO_MILLIS + nano_millis);
#endif
}

#ifdef LOG4CPLUS_USE_PTHREADS
void*
log4cplus::thread::threadStartFunc(void* arg)
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
DWORD WINAPI
log4cplus::thread::threadStartFunc(LPVOID arg)
#endif
{
    if(arg == NULL) {
        getLogLog().error("log4cplus::thread::threadStartFunc()- arg is NULL");
    }
    else {
        AbstractThread* thread = static_cast<AbstractThread*>(arg);
        try {
            log4cplus::helpers::SharedObjectPtr<AbstractThread> ptr(thread);
            thread->run();
        }
        catch(...) {
            // TODO --> Log
        }
        thread->running = false;
    }

#ifdef LOG4CPLUS_USE_PTHREADS
    pthread_exit(NULL);
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    return NULL;
#endif
}

log4cplus::thread::AbstractThread::AbstractThread()
: running(false)
{
}

log4cplus::thread::AbstractThread::~AbstractThread()
{
}

void
log4cplus::thread::AbstractThread::start()
{
    running = true;
#ifdef LOG4CPLUS_USE_PTHREADS
    if( pthread_create(&threadId, NULL, threadStartFunc, this) ) {
        throw std::runtime_error("Thread creation was not successful");
    }
#endif
#ifdef LOG4CPLUS_USE_WIN32_THREADS
    HANDLE h = CreateThread(NULL, 0, threadStartFunc, (LPVOID)this, 0, &threadId);
    CloseHandle(h);
#endif
}

#endif // LOG4CPLUS_SINGLE_THREADED

<|endoftext|>


<commit_before>// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/anonymous-fields1.cpp
// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/anonymous-fields2.cpp
// RUN: %clang_cc1 -emit-obj -o /dev/null -ast-merge %t.1.ast -ast-merge %t.2.ast %s
// expected-no-diagnostics
<commit_msg>XFAIL the test that is not supported on the branch<commit_after>// XFAIL: *
// RUN: %clang_cc1 -emit-pch -o %t.1.ast %S/Inputs/anonymous-fields1.cpp
// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/anonymous-fields2.cpp
// RUN: %clang_cc1 -emit-obj -o /dev/null -ast-merge %t.1.ast -ast-merge %t.2.ast %s
// expected-no-diagnostics
<|endoftext|>


<commit_before>// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s | FileCheck %s

// CHECK: @_ZZNK7PR12917IJiiEE1nMUlvE_clEvE1n = linkonce_odr global i32 0
// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd_N1nE = linkonce_odr global i32 0
// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd0_N1nE = linkonce_odr global i32 0
// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd1_N1nE = linkonce_odr global i32 0

// CHECK: define linkonce_odr void @_Z11inline_funci
inline void inline_func(int n) {
  // CHECK: call i32 @_ZZ11inline_funciENKUlvE_clEv
  int i = []{ return 1; }();

  // CHECK: call i32 @_ZZ11inline_funciENKUlvE0_clEv
  int j = [=] { return n + i; }();

  // CHECK: call double @_ZZ11inline_funciENKUlvE1_clEv
  int k = [=] () -> double { return n + i; }();

  // CHECK: call i32 @_ZZ11inline_funciENKUliE_clEi
  int l = [=] (int x) -> int { return x + i; }(n);

  int inner(int i = []{ return 17; }());
  // CHECK: call i32 @_ZZ11inline_funciENKUlvE2_clEv
  // CHECK-NEXT: call i32 @_Z5inneri
  inner();

  // CHECK-NEXT: ret void
}

void call_inline_func() {
  inline_func(17);
}

struct S {
  void f(int = []{return 1;}()
             + []{return 2;}(),
         int = []{return 3;}());
  void g(int, int);
};

void S::g(int i = []{return 1;}(),
          int j = []{return 2; }()) {}

// CHECK: define void @_Z6test_S1S
void test_S(S s) {
  // CHECK: call i32 @_ZZN1S1fEiiEd0_NKUlvE_clEv
  // CHECK-NEXT: call i32 @_ZZN1S1fEiiEd0_NKUlvE0_clEv
  // CHECK-NEXT: add nsw i32
  // CHECK-NEXT: call i32 @_ZZN1S1fEiiEd_NKUlvE_clEv
  // CHECK-NEXT: call void @_ZN1S1fEii
  s.f();

  // NOTE: These manglings don't actually matter that much, because
  // the lambdas in the default arguments of g() won't be seen by
  // multiple translation units. We check them mainly to ensure that they don't
  // get the special mangling for lambdas in in-class default arguments.
  // CHECK: call i32 @"_ZNK1S3$_0clEv"
  // CHECK-NEXT: call i32 @"_ZNK1S3$_1clEv"
  // CHECK-NEXT: call void @_ZN1S1gEi
  s.g();

  // CHECK-NEXT: ret void
}

// Check the linkage of the lambda call operators used in test_S.
// CHECK: define linkonce_odr i32 @_ZZN1S1fEiiEd0_NKUlvE_clEv
// CHECK: ret i32 1
// CHECK: define linkonce_odr i32 @_ZZN1S1fEiiEd0_NKUlvE0_clEv
// CHECK: ret i32 2
// CHECK: define linkonce_odr i32 @_ZZN1S1fEiiEd_NKUlvE_clEv
// CHECK: ret i32 3
// CHECK: define internal i32 @"_ZNK1S3$_0clEv"
// CHECK: ret i32 1
// CHECK: define internal i32 @"_ZNK1S3$_1clEv"
// CHECK: ret i32 2

template<typename T>
struct ST {
  void f(T = []{return T() + 1;}()
           + []{return T() + 2;}(),
         T = []{return T(3);}());
};

// CHECK: define void @_Z7test_ST2STIdE
void test_ST(ST<double> st) {
  // CHECK: call double @_ZZN2STIdE1fEddEd0_NKUlvE_clEv
  // CHECK-NEXT: call double @_ZZN2STIdE1fEddEd0_NKUlvE0_clEv
  // CHECK-NEXT: fadd double
  // CHECK-NEXT: call double @_ZZN2STIdE1fEddEd_NKUlvE_clEv
  // CHECK-NEXT: call void @_ZN2STIdE1fEdd
  st.f();

  // CHECK-NEXT: ret void
}

// Check the linkage of the lambda call operators used in test_ST.
// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd0_NKUlvE_clEv
// CHECK: ret double 1
// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd0_NKUlvE0_clEv
// CHECK: ret double 2
// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd_NKUlvE_clEv
// CHECK: ret double 3

template<typename T>
struct StaticMembers {
  static T x;
  static T y;
  static T z;
};

template<typename T> int accept_lambda(T);

template<typename T>
T StaticMembers<T>::x = []{return 1;}() + []{return 2;}();

template<typename T>
T StaticMembers<T>::y = []{return 3;}();

template<typename T>
T StaticMembers<T>::z = accept_lambda([]{return 4;});

// CHECK: define internal void @__cxx_global_var_init()
// CHECK: call i32 @_ZNK13StaticMembersIfE1xMUlvE_clEv
// CHECK-NEXT: call i32 @_ZNK13StaticMembersIfE1xMUlvE0_clEv
// CHECK-NEXT: add nsw
// CHECK: define linkonce_odr i32 @_ZNK13StaticMembersIfE1xMUlvE_clEv
// CHECK: ret i32 1
// CHECK: define linkonce_odr i32 @_ZNK13StaticMembersIfE1xMUlvE0_clEv
// CHECK: ret i32 2
template float StaticMembers<float>::x;

// CHECK: define internal void @__cxx_global_var_init1()
// CHECK: call i32 @_ZNK13StaticMembersIfE1yMUlvE_clEv
// CHECK: define linkonce_odr i32 @_ZNK13StaticMembersIfE1yMUlvE_clEv
// CHECK: ret i32 3
template float StaticMembers<float>::y;

// CHECK: define internal void @__cxx_global_var_init2()
// CHECK: call i32 @_Z13accept_lambdaIN13StaticMembersIfE1zMUlvE_EEiT_
// CHECK: declare i32 @_Z13accept_lambdaIN13StaticMembersIfE1zMUlvE_EEiT_()
template float StaticMembers<float>::z;

// CHECK: define internal void @__cxx_global_var_init3
// CHECK: call i32 @"_ZNK13StaticMembersIdE3$_2clEv"
// CHECK: define internal i32 @"_ZNK13StaticMembersIdE3$_2clEv"
// CHECK: ret i32 42
template<> double StaticMembers<double>::z = []{return 42; }();

template<typename T>
void func_template(T = []{ return T(); }());

// CHECK: define void @_Z17use_func_templatev()
void use_func_template() {
  // CHECK: call i32 @"_ZZ13func_templateIiEvT_ENKS_IiE3$_3clEv"
  func_template<int>();
}

template<typename...T> struct PR12917 {
  PR12917(T ...t = []{ static int n = 0; return ++n; }());

  static int n[3];
};
template<typename...T> int PR12917<T...>::n[3] = {
  []{ static int n = 0; return ++n; }()
};

// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd1_NKUlvE_clEv(
// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd0_NKUlvE_clEv(
// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd_NKUlvE_clEv(
// CHECK: call void @_ZN7PR12917IJicdEEC1Eicd(
PR12917<int, char, double> pr12917;
int *pr12917_p = PR12917<int, int>::n;

// CHECK: define linkonce_odr void @_Z1fIZZNK23TestNestedInstantiationclEvENKUlvE_clEvEUlvE_EvT_

struct Members {
  int x = [] { return 1; }() + [] { return 2; }();
  int y = [] { return 3; }();
};

void test_Members() {
  // CHECK: define linkonce_odr void @_ZN7MembersC2Ev
  // CHECK: call i32 @_ZNK7Members1xMUlvE_clEv
  // CHECK-NEXT: call i32 @_ZNK7Members1xMUlvE0_clE
  // CHECK-NEXT: add nsw i32
  // CHECK: call i32 @_ZNK7Members1yMUlvE_clEv
  Members members;
  // CHECK: ret void
}

template<typename P> void f(P) { }

struct TestNestedInstantiation {
   void operator()() const {
     []() -> void {
       return f([]{});
     }();
   }
};

void test_NestedInstantiation() {
  TestNestedInstantiation()();
}

// Check the linkage of the lambdas used in test_Members.
// CHECK: define linkonce_odr i32 @_ZNK7Members1xMUlvE_clEv
// CHECK: ret i32 1
// CHECK: define linkonce_odr i32 @_ZNK7Members1xMUlvE0_clEv
// CHECK: ret i32 2
// CHECK: define linkonce_odr i32 @_ZNK7Members1yMUlvE_clEv
// CHECK: ret i32 3

// Check linkage of the various lambdas.
// CHECK: define linkonce_odr i32 @_ZZ11inline_funciENKUlvE_clEv
// CHECK: ret i32 1
// CHECK: define linkonce_odr i32 @_ZZ11inline_funciENKUlvE0_clEv
// CHECK: ret i32
// CHECK: define linkonce_odr double @_ZZ11inline_funciENKUlvE1_clEv
// CHECK: ret double
// CHECK: define linkonce_odr i32 @_ZZ11inline_funciENKUliE_clEi
// CHECK: ret i32
// CHECK: define linkonce_odr i32 @_ZZ11inline_funciENKUlvE2_clEv
// CHECK: ret i32 17
<commit_msg>Test for r164186.<commit_after>// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s | FileCheck %s

// CHECK: @_ZZNK7PR12917IJiiEE1nMUlvE_clEvE1n = linkonce_odr global i32 0
// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd_N1nE = linkonce_odr global i32 0
// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd0_N1nE = linkonce_odr global i32 0
// CHECK: @_ZZN7PR12917IJicdEEC1EicdEd1_N1nE = linkonce_odr global i32 0

// CHECK: define linkonce_odr void @_Z11inline_funci
inline void inline_func(int n) {
  // CHECK: call i32 @_ZZ11inline_funciENKUlvE_clEv
  int i = []{ return 1; }();

  // CHECK: call i32 @_ZZ11inline_funciENKUlvE0_clEv
  int j = [=] { return n + i; }();

  // CHECK: call double @_ZZ11inline_funciENKUlvE1_clEv
  int k = [=] () -> double { return n + i; }();

  // CHECK: call i32 @_ZZ11inline_funciENKUliE_clEi
  int l = [=] (int x) -> int { return x + i; }(n);

  int inner(int i = []{ return 17; }());
  // CHECK: call i32 @_ZZ11inline_funciENKUlvE2_clEv
  // CHECK-NEXT: call i32 @_Z5inneri
  inner();

  // CHECK-NEXT: ret void
}

void call_inline_func() {
  inline_func(17);
}

struct S {
  void f(int = []{return 1;}()
             + []{return 2;}(),
         int = []{return 3;}());
  void g(int, int);
};

void S::g(int i = []{return 1;}(),
          int j = []{return 2; }()) {}

// CHECK: define void @_Z6test_S1S
void test_S(S s) {
  // CHECK: call i32 @_ZZN1S1fEiiEd0_NKUlvE_clEv
  // CHECK-NEXT: call i32 @_ZZN1S1fEiiEd0_NKUlvE0_clEv
  // CHECK-NEXT: add nsw i32
  // CHECK-NEXT: call i32 @_ZZN1S1fEiiEd_NKUlvE_clEv
  // CHECK-NEXT: call void @_ZN1S1fEii
  s.f();

  // NOTE: These manglings don't actually matter that much, because
  // the lambdas in the default arguments of g() won't be seen by
  // multiple translation units. We check them mainly to ensure that they don't
  // get the special mangling for lambdas in in-class default arguments.
  // CHECK: call i32 @"_ZNK1S3$_0clEv"
  // CHECK-NEXT: call i32 @"_ZNK1S3$_1clEv"
  // CHECK-NEXT: call void @_ZN1S1gEi
  s.g();

  // CHECK-NEXT: ret void
}

// Check the linkage of the lambda call operators used in test_S.
// CHECK: define linkonce_odr i32 @_ZZN1S1fEiiEd0_NKUlvE_clEv
// CHECK: ret i32 1
// CHECK: define linkonce_odr i32 @_ZZN1S1fEiiEd0_NKUlvE0_clEv
// CHECK: ret i32 2
// CHECK: define linkonce_odr i32 @_ZZN1S1fEiiEd_NKUlvE_clEv
// CHECK: ret i32 3
// CHECK: define internal i32 @"_ZNK1S3$_0clEv"
// CHECK: ret i32 1
// CHECK: define internal i32 @"_ZNK1S3$_1clEv"
// CHECK: ret i32 2

template<typename T>
struct ST {
  void f(T = []{return T() + 1;}()
           + []{return T() + 2;}(),
         T = []{return T(3);}());
};

// CHECK: define void @_Z7test_ST2STIdE
void test_ST(ST<double> st) {
  // CHECK: call double @_ZZN2STIdE1fEddEd0_NKUlvE_clEv
  // CHECK-NEXT: call double @_ZZN2STIdE1fEddEd0_NKUlvE0_clEv
  // CHECK-NEXT: fadd double
  // CHECK-NEXT: call double @_ZZN2STIdE1fEddEd_NKUlvE_clEv
  // CHECK-NEXT: call void @_ZN2STIdE1fEdd
  st.f();

  // CHECK-NEXT: ret void
}

// Check the linkage of the lambda call operators used in test_ST.
// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd0_NKUlvE_clEv
// CHECK: ret double 1
// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd0_NKUlvE0_clEv
// CHECK: ret double 2
// CHECK: define linkonce_odr double @_ZZN2STIdE1fEddEd_NKUlvE_clEv
// CHECK: ret double 3

template<typename T>
struct StaticMembers {
  static T x;
  static T y;
  static T z;
};

template<typename T> int accept_lambda(T);

template<typename T>
T StaticMembers<T>::x = []{return 1;}() + []{return 2;}();

template<typename T>
T StaticMembers<T>::y = []{return 3;}();

template<typename T>
T StaticMembers<T>::z = accept_lambda([]{return 4;});

// CHECK: define internal void @__cxx_global_var_init()
// CHECK: call i32 @_ZNK13StaticMembersIfE1xMUlvE_clEv
// CHECK-NEXT: call i32 @_ZNK13StaticMembersIfE1xMUlvE0_clEv
// CHECK-NEXT: add nsw
// CHECK: define linkonce_odr i32 @_ZNK13StaticMembersIfE1xMUlvE_clEv
// CHECK: ret i32 1
// CHECK: define linkonce_odr i32 @_ZNK13StaticMembersIfE1xMUlvE0_clEv
// CHECK: ret i32 2
template float StaticMembers<float>::x;

// CHECK: define internal void @__cxx_global_var_init1()
// CHECK: call i32 @_ZNK13StaticMembersIfE1yMUlvE_clEv
// CHECK: define linkonce_odr i32 @_ZNK13StaticMembersIfE1yMUlvE_clEv
// CHECK: ret i32 3
template float StaticMembers<float>::y;

// CHECK: define internal void @__cxx_global_var_init2()
// CHECK: call i32 @_Z13accept_lambdaIN13StaticMembersIfE1zMUlvE_EEiT_
// CHECK: declare i32 @_Z13accept_lambdaIN13StaticMembersIfE1zMUlvE_EEiT_()
template float StaticMembers<float>::z;

// CHECK: define internal void @__cxx_global_var_init3
// CHECK: call i32 @"_ZNK13StaticMembersIdE3$_2clEv"
// CHECK: define internal i32 @"_ZNK13StaticMembersIdE3$_2clEv"
// CHECK: ret i32 42
template<> double StaticMembers<double>::z = []{return 42; }();

template<typename T>
void func_template(T = []{ return T(); }());

// CHECK: define void @_Z17use_func_templatev()
void use_func_template() {
  // CHECK: call i32 @"_ZZ13func_templateIiEvT_ENKS_IiE3$_3clEv"
  func_template<int>();
}

template<typename...T> struct PR12917 {
  PR12917(T ...t = []{ static int n = 0; return ++n; }());

  static int n[3];
};
template<typename...T> int PR12917<T...>::n[3] = {
  []{ static int n = 0; return ++n; }()
};

// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd1_NKUlvE_clEv(
// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd0_NKUlvE_clEv(
// CHECK: call i32 @_ZZN7PR12917IJicdEEC1EicdEd_NKUlvE_clEv(
// CHECK: call void @_ZN7PR12917IJicdEEC1Eicd(
PR12917<int, char, double> pr12917;
int *pr12917_p = PR12917<int, int>::n;

namespace PR12808 {
  template <typename> struct B {
    int a;
    template <typename L> constexpr B(L&& x) : a(x()) { }
  };
  template <typename> void b(int) {
    [&]{ (void)B<int>([&]{ return 1; }); }();
  }
  void f() {
    b<int>(1);
  }
  // CHECK: define linkonce_odr void @_ZZN7PR128081bIiEEviENKS0_IiEUlvE_clEv
  // CHECK: define linkonce_odr i32 @_ZZZN7PR128081bIiEEviENKS0_IiEUlvE_clEvENKUlvE_clEv
}

// CHECK: define linkonce_odr void @_Z1fIZZNK23TestNestedInstantiationclEvENKUlvE_clEvEUlvE_EvT_

struct Members {
  int x = [] { return 1; }() + [] { return 2; }();
  int y = [] { return 3; }();
};

void test_Members() {
  // CHECK: define linkonce_odr void @_ZN7MembersC2Ev
  // CHECK: call i32 @_ZNK7Members1xMUlvE_clEv
  // CHECK-NEXT: call i32 @_ZNK7Members1xMUlvE0_clE
  // CHECK-NEXT: add nsw i32
  // CHECK: call i32 @_ZNK7Members1yMUlvE_clEv
  Members members;
  // CHECK: ret void
}

template<typename P> void f(P) { }

struct TestNestedInstantiation {
   void operator()() const {
     []() -> void {
       return f([]{});
     }();
   }
};

void test_NestedInstantiation() {
  TestNestedInstantiation()();
}

// Check the linkage of the lambdas used in test_Members.
// CHECK: define linkonce_odr i32 @_ZNK7Members1xMUlvE_clEv
// CHECK: ret i32 1
// CHECK: define linkonce_odr i32 @_ZNK7Members1xMUlvE0_clEv
// CHECK: ret i32 2
// CHECK: define linkonce_odr i32 @_ZNK7Members1yMUlvE_clEv
// CHECK: ret i32 3

// Check linkage of the various lambdas.
// CHECK: define linkonce_odr i32 @_ZZ11inline_funciENKUlvE_clEv
// CHECK: ret i32 1
// CHECK: define linkonce_odr i32 @_ZZ11inline_funciENKUlvE0_clEv
// CHECK: ret i32
// CHECK: define linkonce_odr double @_ZZ11inline_funciENKUlvE1_clEv
// CHECK: ret double
// CHECK: define linkonce_odr i32 @_ZZ11inline_funciENKUliE_clEi
// CHECK: ret i32
// CHECK: define linkonce_odr i32 @_ZZ11inline_funciENKUlvE2_clEv
// CHECK: ret i32 17
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-present ScyllaDB
 */

/*
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <seastar/core/distributed.hh>
#include <seastar/core/app-template.hh>
#include <seastar/core/sstring.hh>
#include <seastar/core/thread.hh>
#include <seastar/core/reactor.hh>

#include "utils/managed_bytes.hh"
#include "utils/logalloc.hh"
#include "utils/UUID_gen.hh"
#include "row_cache.hh"
#include "log.hh"
#include "schema_builder.hh"
#include "readers/combined.hh"
#include "readers/mutation_fragment_v1_stream.hh"
#include "replica/memtable.hh"
#include "test/perf/perf.hh"
#include "test/lib/reader_concurrency_semaphore.hh"

static const int update_iterations = 16;
static const int cell_size = 128;
static bool cancelled = false;

template<typename MutationGenerator>
void run_test(const sstring& name, schema_ptr s, MutationGenerator&& gen) {
    tests::reader_concurrency_semaphore_wrapper semaphore;
    cache_tracker tracker;
    row_cache cache(s, make_empty_snapshot_source(), tracker, is_continuous::yes);

    size_t memtable_size = seastar::memory::stats().total_memory() / 4;

    std::cout << name << ":\n";

    for (int i = 0; i < update_iterations; ++i) {
        auto MB = 1024 * 1024;
        auto prefill_compacted = logalloc::memory_compacted();
        auto prefill_allocated = logalloc::memory_allocated();

        auto mt = make_lw_shared<replica::memtable>(s);
        while (mt->occupancy().total_space() < memtable_size) {
            mutation m = gen();
            mt->apply(m);
            seastar::thread::maybe_yield();
            if (cancelled) {
                return;
            }
        }

        auto prev_compacted = logalloc::memory_compacted();
        auto prev_allocated = logalloc::memory_allocated();
        auto prev_rows_processed_from_memtable = tracker.get_stats().rows_processed_from_memtable;
        auto prev_rows_merged_from_memtable = tracker.get_stats().rows_merged_from_memtable;
        auto prev_rows_dropped_from_memtable = tracker.get_stats().rows_dropped_from_memtable;

        std::cout << format("cache: {:d}/{:d} [MB], memtable: {:d}/{:d} [MB], alloc/comp: {:d}/{:d} [MB] (amp: {:.3f})\n",
            tracker.region().occupancy().used_space() / MB,
            tracker.region().occupancy().total_space() / MB,
            mt->occupancy().used_space() / MB,
            mt->occupancy().total_space() / MB,
            (prev_allocated - prefill_allocated) / MB,
            (prev_compacted - prefill_compacted) / MB,
            float((prev_compacted - prefill_compacted)) / (prev_allocated - prefill_allocated)
        );

        auto permit = semaphore.make_permit();
        // Create a reader which tests the case of memtable snapshots
        // going away after memtable was merged to cache.
        auto rd = std::make_unique<mutation_fragment_v1_stream>(
            mutation_fragment_v1_stream(make_combined_reader(s, permit, cache.make_reader(s, permit), mt->make_flat_reader(s, permit))));
        auto close_rd = defer([&rd] { rd->close().get(); });
        rd->set_max_buffer_size(1);
        rd->fill_buffer().get();

        scheduling_latency_measurer slm;
        slm.start();
        auto d = duration_in_seconds([&] {
            cache.update(row_cache::external_updater([] {}), *mt).get();
        });

        rd->set_max_buffer_size(1024*1024);
        rd->consume_pausable([] (mutation_fragment) {
            return stop_iteration::no;
        }).get();

        mt = {};

        close_rd.cancel();
        rd->close().get();
        rd = {};

        slm.stop();

        auto compacted = logalloc::memory_compacted() - prev_compacted;
        auto allocated = logalloc::memory_allocated() - prev_allocated;

        std::cout << format("update: {:.6f} [ms], preemption: {}, cache: {:d}/{:d} [MB], alloc/comp: {:d}/{:d} [MB] (amp: {:.3f}), pr/me/dr {:d}/{:d}/{:d}\n",
            d.count() * 1000,
            slm,
            tracker.region().occupancy().used_space() / MB,
            tracker.region().occupancy().total_space() / MB,
            allocated / MB, compacted / MB, float(compacted)/allocated,
            tracker.get_stats().rows_processed_from_memtable - prev_rows_processed_from_memtable,
            tracker.get_stats().rows_merged_from_memtable - prev_rows_merged_from_memtable,
            tracker.get_stats().rows_dropped_from_memtable - prev_rows_dropped_from_memtable);
    }

    scheduling_latency_measurer invalidate_slm;
    invalidate_slm.start();
    auto d = duration_in_seconds([&] {
        cache.invalidate(row_cache::external_updater([] {})).get();
    });
    invalidate_slm.stop();

    std::cout << format("invalidation: {:.6f} [ms], preemption: {}", d.count() * 1000, invalidate_slm) << "\n";
}

void test_small_partitions() {
    auto s = schema_builder("ks", "cf")
        .with_column("pk", uuid_type, column_kind::partition_key)
        .with_column("v1", bytes_type, column_kind::regular_column)
        .with_column("v2", bytes_type, column_kind::regular_column)
        .with_column("v3", bytes_type, column_kind::regular_column)
        .build();

    run_test("Small partitions, no overwrites", s, [&] {
        auto pk = dht::decorate_key(*s, partition_key::from_single_value(*s,
            serialized(utils::UUID_gen::get_time_UUID())));
        mutation m(s, pk);
        auto val = data_value(bytes(bytes::initialized_later(), cell_size));
        m.set_clustered_cell(clustering_key::make_empty(), "v1", val, api::new_timestamp());
        m.set_clustered_cell(clustering_key::make_empty(), "v2", val, api::new_timestamp());
        m.set_clustered_cell(clustering_key::make_empty(), "v3", val, api::new_timestamp());
        return m;
    });
}

void test_partition_with_lots_of_small_rows() {
    auto s = schema_builder("ks", "cf")
        .with_column("pk", uuid_type, column_kind::partition_key)
        .with_column("ck", reversed_type_impl::get_instance(int32_type), column_kind::clustering_key)
        .with_column("v1", bytes_type, column_kind::regular_column)
        .with_column("v2", bytes_type, column_kind::regular_column)
        .with_column("v3", bytes_type, column_kind::regular_column)
        .build();

    auto pk = dht::decorate_key(*s, partition_key::from_single_value(*s,
        serialized(utils::UUID_gen::get_time_UUID())));
    int ck_idx = 0;

    run_test("Large partition, lots of small rows", s, [&] {
        mutation m(s, pk);
        auto val = data_value(bytes(bytes::initialized_later(), cell_size));
        auto ck = clustering_key::from_single_value(*s, serialized(ck_idx++));
        m.set_clustered_cell(ck, "v1", val, api::new_timestamp());
        m.set_clustered_cell(ck, "v2", val, api::new_timestamp());
        m.set_clustered_cell(ck, "v3", val, api::new_timestamp());
        return m;
    });
}

void test_partition_with_few_small_rows() {
    auto s = schema_builder("ks", "cf")
        .with_column("pk", uuid_type, column_kind::partition_key)
        .with_column("ck", reversed_type_impl::get_instance(int32_type), column_kind::clustering_key)
        .with_column("v1", bytes_type, column_kind::regular_column)
        .with_column("v2", bytes_type, column_kind::regular_column)
        .with_column("v3", bytes_type, column_kind::regular_column)
        .build();

    run_test("Small partition with a few rows", s, [&] {
        auto pk = dht::decorate_key(*s, partition_key::from_single_value(*s,
            serialized(utils::UUID_gen::get_time_UUID())));

        mutation m(s, pk);
        auto val = data_value(bytes(bytes::initialized_later(), cell_size));

        for (int i = 0; i < 3; ++i) {
            auto ck = clustering_key::from_single_value(*s, serialized(i));
            m.set_clustered_cell(ck, "v1", val, api::new_timestamp());
            m.set_clustered_cell(ck, "v2", val, api::new_timestamp());
            m.set_clustered_cell(ck, "v3", val, api::new_timestamp());
        }
        return m;
    });
}

void test_partition_with_lots_of_range_tombstones() {
    auto s = schema_builder("ks", "cf")
        .with_column("pk", uuid_type, column_kind::partition_key)
        .with_column("ck", reversed_type_impl::get_instance(int32_type), column_kind::clustering_key)
        .with_column("v1", bytes_type, column_kind::regular_column)
        .with_column("v2", bytes_type, column_kind::regular_column)
        .with_column("v3", bytes_type, column_kind::regular_column)
        .build();

    auto pk = dht::decorate_key(*s, partition_key::from_single_value(*s,
        serialized(utils::UUID_gen::get_time_UUID())));
    int ck_idx = 0;

    run_test("Large partition, lots of range tombstones", s, [&] {
        mutation m(s, pk);
        auto val = data_value(bytes(bytes::initialized_later(), cell_size));
        auto ck = clustering_key::from_single_value(*s, serialized(ck_idx++));
        auto r = query::clustering_range::make({ck}, {ck});
        tombstone tomb(api::new_timestamp(), gc_clock::now());
        m.partition().apply_row_tombstone(*s, range_tombstone(bound_view::from_range_start(r), bound_view::from_range_end(r), tomb));
        return m;
    });
}

int main(int argc, char** argv) {
    app_template app;
    return app.run(argc, argv, [&app] {
        return seastar::async([&] {
            engine().at_exit([] {
                cancelled = true;
                return make_ready_future();
            });
            logalloc::prime_segment_pool(memory::stats().total_memory(), memory::min_free_memory()).get();
            test_small_partitions();
            test_partition_with_few_small_rows();
            test_partition_with_lots_of_small_rows();
            test_partition_with_lots_of_range_tombstones();
        });
    });
}
<commit_msg>test: perf_row_cache_update: Measure memtable filling time<commit_after>/*
 * Copyright (C) 2015-present ScyllaDB
 */

/*
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <seastar/core/distributed.hh>
#include <seastar/core/app-template.hh>
#include <seastar/core/sstring.hh>
#include <seastar/core/thread.hh>
#include <seastar/core/reactor.hh>

#include "utils/managed_bytes.hh"
#include "utils/logalloc.hh"
#include "utils/UUID_gen.hh"
#include "row_cache.hh"
#include "log.hh"
#include "schema_builder.hh"
#include "readers/combined.hh"
#include "readers/mutation_fragment_v1_stream.hh"
#include "replica/memtable.hh"
#include "test/perf/perf.hh"
#include "test/lib/reader_concurrency_semaphore.hh"

static const int update_iterations = 16;
static const int cell_size = 128;
static bool cancelled = false;

template<typename MutationGenerator>
void run_test(const sstring& name, schema_ptr s, MutationGenerator&& gen) {
    tests::reader_concurrency_semaphore_wrapper semaphore;
    cache_tracker tracker;
    row_cache cache(s, make_empty_snapshot_source(), tracker, is_continuous::yes);

    size_t memtable_size = seastar::memory::stats().total_memory() / 4;

    std::cout << name << ":\n";

    for (int i = 0; i < update_iterations; ++i) {
        auto MB = 1024 * 1024;
        auto prefill_compacted = logalloc::memory_compacted();
        auto prefill_allocated = logalloc::memory_allocated();

        auto mt = make_lw_shared<replica::memtable>(s);
        auto fill_d = duration_in_seconds([&] {
            while (mt->occupancy().total_space() < memtable_size) {
                mutation m = gen();
                mt->apply(m);
                seastar::thread::maybe_yield();
                if (cancelled) {
                    return;
                }
            }
        });
        std::cout << format("Memtable fill took {:.6f} [ms]", fill_d.count() * 1000) << std::endl;

        auto prev_compacted = logalloc::memory_compacted();
        auto prev_allocated = logalloc::memory_allocated();
        auto prev_rows_processed_from_memtable = tracker.get_stats().rows_processed_from_memtable;
        auto prev_rows_merged_from_memtable = tracker.get_stats().rows_merged_from_memtable;
        auto prev_rows_dropped_from_memtable = tracker.get_stats().rows_dropped_from_memtable;

        std::cout << format("cache: {:d}/{:d} [MB], memtable: {:d}/{:d} [MB], alloc/comp: {:d}/{:d} [MB] (amp: {:.3f})\n",
            tracker.region().occupancy().used_space() / MB,
            tracker.region().occupancy().total_space() / MB,
            mt->occupancy().used_space() / MB,
            mt->occupancy().total_space() / MB,
            (prev_allocated - prefill_allocated) / MB,
            (prev_compacted - prefill_compacted) / MB,
            float((prev_compacted - prefill_compacted)) / (prev_allocated - prefill_allocated)
        );

        auto permit = semaphore.make_permit();
        // Create a reader which tests the case of memtable snapshots
        // going away after memtable was merged to cache.
        auto rd = std::make_unique<mutation_fragment_v1_stream>(
            mutation_fragment_v1_stream(make_combined_reader(s, permit, cache.make_reader(s, permit), mt->make_flat_reader(s, permit))));
        auto close_rd = defer([&rd] { rd->close().get(); });
        rd->set_max_buffer_size(1);
        rd->fill_buffer().get();

        scheduling_latency_measurer slm;
        slm.start();
        auto d = duration_in_seconds([&] {
            cache.update(row_cache::external_updater([] {}), *mt).get();
        });

        rd->set_max_buffer_size(1024*1024);
        rd->consume_pausable([] (mutation_fragment) {
            return stop_iteration::no;
        }).get();

        mt = {};

        close_rd.cancel();
        rd->close().get();
        rd = {};

        slm.stop();

        auto compacted = logalloc::memory_compacted() - prev_compacted;
        auto allocated = logalloc::memory_allocated() - prev_allocated;

        std::cout << format("update: {:.6f} [ms], preemption: {}, cache: {:d}/{:d} [MB], alloc/comp: {:d}/{:d} [MB] (amp: {:.3f}), pr/me/dr {:d}/{:d}/{:d}\n",
            d.count() * 1000,
            slm,
            tracker.region().occupancy().used_space() / MB,
            tracker.region().occupancy().total_space() / MB,
            allocated / MB, compacted / MB, float(compacted)/allocated,
            tracker.get_stats().rows_processed_from_memtable - prev_rows_processed_from_memtable,
            tracker.get_stats().rows_merged_from_memtable - prev_rows_merged_from_memtable,
            tracker.get_stats().rows_dropped_from_memtable - prev_rows_dropped_from_memtable);
    }

    scheduling_latency_measurer invalidate_slm;
    invalidate_slm.start();
    auto d = duration_in_seconds([&] {
        cache.invalidate(row_cache::external_updater([] {})).get();
    });
    invalidate_slm.stop();

    std::cout << format("invalidation: {:.6f} [ms], preemption: {}", d.count() * 1000, invalidate_slm) << "\n";
}

void test_small_partitions() {
    auto s = schema_builder("ks", "cf")
        .with_column("pk", uuid_type, column_kind::partition_key)
        .with_column("v1", bytes_type, column_kind::regular_column)
        .with_column("v2", bytes_type, column_kind::regular_column)
        .with_column("v3", bytes_type, column_kind::regular_column)
        .build();

    run_test("Small partitions, no overwrites", s, [&] {
        auto pk = dht::decorate_key(*s, partition_key::from_single_value(*s,
            serialized(utils::UUID_gen::get_time_UUID())));
        mutation m(s, pk);
        auto val = data_value(bytes(bytes::initialized_later(), cell_size));
        m.set_clustered_cell(clustering_key::make_empty(), "v1", val, api::new_timestamp());
        m.set_clustered_cell(clustering_key::make_empty(), "v2", val, api::new_timestamp());
        m.set_clustered_cell(clustering_key::make_empty(), "v3", val, api::new_timestamp());
        return m;
    });
}

void test_partition_with_lots_of_small_rows() {
    auto s = schema_builder("ks", "cf")
        .with_column("pk", uuid_type, column_kind::partition_key)
        .with_column("ck", reversed_type_impl::get_instance(int32_type), column_kind::clustering_key)
        .with_column("v1", bytes_type, column_kind::regular_column)
        .with_column("v2", bytes_type, column_kind::regular_column)
        .with_column("v3", bytes_type, column_kind::regular_column)
        .build();

    auto pk = dht::decorate_key(*s, partition_key::from_single_value(*s,
        serialized(utils::UUID_gen::get_time_UUID())));
    int ck_idx = 0;

    run_test("Large partition, lots of small rows", s, [&] {
        mutation m(s, pk);
        auto val = data_value(bytes(bytes::initialized_later(), cell_size));
        auto ck = clustering_key::from_single_value(*s, serialized(ck_idx++));
        m.set_clustered_cell(ck, "v1", val, api::new_timestamp());
        m.set_clustered_cell(ck, "v2", val, api::new_timestamp());
        m.set_clustered_cell(ck, "v3", val, api::new_timestamp());
        return m;
    });
}

void test_partition_with_few_small_rows() {
    auto s = schema_builder("ks", "cf")
        .with_column("pk", uuid_type, column_kind::partition_key)
        .with_column("ck", reversed_type_impl::get_instance(int32_type), column_kind::clustering_key)
        .with_column("v1", bytes_type, column_kind::regular_column)
        .with_column("v2", bytes_type, column_kind::regular_column)
        .with_column("v3", bytes_type, column_kind::regular_column)
        .build();

    run_test("Small partition with a few rows", s, [&] {
        auto pk = dht::decorate_key(*s, partition_key::from_single_value(*s,
            serialized(utils::UUID_gen::get_time_UUID())));

        mutation m(s, pk);
        auto val = data_value(bytes(bytes::initialized_later(), cell_size));

        for (int i = 0; i < 3; ++i) {
            auto ck = clustering_key::from_single_value(*s, serialized(i));
            m.set_clustered_cell(ck, "v1", val, api::new_timestamp());
            m.set_clustered_cell(ck, "v2", val, api::new_timestamp());
            m.set_clustered_cell(ck, "v3", val, api::new_timestamp());
        }
        return m;
    });
}

void test_partition_with_lots_of_range_tombstones() {
    auto s = schema_builder("ks", "cf")
        .with_column("pk", uuid_type, column_kind::partition_key)
        .with_column("ck", reversed_type_impl::get_instance(int32_type), column_kind::clustering_key)
        .with_column("v1", bytes_type, column_kind::regular_column)
        .with_column("v2", bytes_type, column_kind::regular_column)
        .with_column("v3", bytes_type, column_kind::regular_column)
        .build();

    auto pk = dht::decorate_key(*s, partition_key::from_single_value(*s,
        serialized(utils::UUID_gen::get_time_UUID())));
    int ck_idx = 0;

    run_test("Large partition, lots of range tombstones", s, [&] {
        mutation m(s, pk);
        auto val = data_value(bytes(bytes::initialized_later(), cell_size));
        auto ck = clustering_key::from_single_value(*s, serialized(ck_idx++));
        auto r = query::clustering_range::make({ck}, {ck});
        tombstone tomb(api::new_timestamp(), gc_clock::now());
        m.partition().apply_row_tombstone(*s, range_tombstone(bound_view::from_range_start(r), bound_view::from_range_end(r), tomb));
        return m;
    });
}

int main(int argc, char** argv) {
    app_template app;
    return app.run(argc, argv, [&app] {
        return seastar::async([&] {
            engine().at_exit([] {
                cancelled = true;
                return make_ready_future();
            });
            logalloc::prime_segment_pool(memory::stats().total_memory(), memory::min_free_memory()).get();
            test_small_partitions();
            test_partition_with_few_small_rows();
            test_partition_with_lots_of_small_rows();
            test_partition_with_lots_of_range_tombstones();
        });
    });
}
<|endoftext|>


<commit_before>//===-- PeepholeOpts.cpp --------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Support for performing several peephole opts in one or a few passes over the
// machine code of a method.
//
//===----------------------------------------------------------------------===//

#include "SparcInternals.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/BasicBlock.h"
#include "llvm/Pass.h"

namespace llvm {

//************************* Internal Functions *****************************/

static inline void
DeleteInstruction(MachineBasicBlock& mvec,
                  MachineBasicBlock::iterator& BBI,
                  const TargetMachine& target) {
  // Check if this instruction is in a delay slot of its predecessor.
  if (BBI != mvec.begin()) {
      const TargetInstrInfo& mii = target.getInstrInfo();
      MachineInstr* predMI = *(BBI-1);
      if (unsigned ndelay = mii.getNumDelaySlots(predMI->getOpCode())) {
        // This instruction is in a delay slot of its predecessor, so
        // replace it with a nop. By replacing in place, we save having
        // to update the I-I maps.
        //
        assert(ndelay == 1 && "Not yet handling multiple-delay-slot targets");
        (*BBI)->replace(mii.getNOPOpCode(), 0);
        return;
      }
  }

  // The instruction is not in a delay slot, so we can simply erase it.
  mvec.erase(BBI);
  BBI = mvec.end();
}

//******************* Individual Peephole Optimizations ********************/

//----------------------------------------------------------------------------
// Function: IsUselessCopy
// Decide whether a machine instruction is a redundant copy:
// -- ADD    with g0 and result and operand are identical, or
// -- OR     with g0 and result and operand are identical, or
// -- FMOVS or FMOVD and result and operand are identical.
// Other cases are possible but very rare that they would be useless copies,
// so it's not worth analyzing them.
//----------------------------------------------------------------------------

static bool IsUselessCopy(const TargetMachine &target, const MachineInstr* MI) {
  if (MI->getOpCode() == V9::FMOVS || MI->getOpCode() == V9::FMOVD) {
    return (/* both operands are allocated to the same register */
            MI->getOperand(0).getAllocatedRegNum() ==
            MI->getOperand(1).getAllocatedRegNum());
  } else if (MI->getOpCode() == V9::ADDr || MI->getOpCode() == V9::ORr ||
             MI->getOpCode() == V9::ADDi || MI->getOpCode() == V9::ORi) {
    unsigned srcWithDestReg;

    for (srcWithDestReg = 0; srcWithDestReg < 2; ++srcWithDestReg)
      if (MI->getOperand(srcWithDestReg).hasAllocatedReg() &&
          MI->getOperand(srcWithDestReg).getAllocatedRegNum()
          == MI->getOperand(2).getAllocatedRegNum())
        break;

    if (srcWithDestReg == 2)
      return false;
    else {
      /* else source and dest are allocated to the same register */
      unsigned otherOp = 1 - srcWithDestReg;
      return (/* either operand otherOp is register %g0 */
              (MI->getOperand(otherOp).hasAllocatedReg() &&
               MI->getOperand(otherOp).getAllocatedRegNum() ==
               target.getRegInfo().getZeroRegNum()) ||

              /* or operand otherOp == 0 */
              (MI->getOperand(otherOp).getType()
               == MachineOperand::MO_SignExtendedImmed &&
               MI->getOperand(otherOp).getImmedValue() == 0));
    }
  }
  else
    return false;
}

inline bool
RemoveUselessCopies(MachineBasicBlock& mvec,
                    MachineBasicBlock::iterator& BBI,
                    const TargetMachine& target) {
  if (IsUselessCopy(target, *BBI)) {
    DeleteInstruction(mvec, BBI, target);
    return true;
  }
  return false;
}

//************************ Class Implementations **************************/

class PeepholeOpts: public BasicBlockPass {
  const TargetMachine ⌖
  bool visit(MachineBasicBlock& mvec,
             MachineBasicBlock::iterator BBI) const;
public:
  PeepholeOpts(const TargetMachine &TM): target(TM) { }
  bool runOnBasicBlock(BasicBlock &BB); // apply this pass to each BB
  virtual const char *getPassName() const { return "Peephole Optimization"; }
};

// Apply a list of peephole optimizations to this machine instruction
// within its local context.  They are allowed to delete MI or any
// instruction before MI, but not
//
bool PeepholeOpts::visit(MachineBasicBlock& mvec,
                         MachineBasicBlock::iterator BBI) const {
  /* Remove redundant copy instructions */
  return RemoveUselessCopies(mvec, BBI, target);
}

bool PeepholeOpts::runOnBasicBlock(BasicBlock &BB) {
  // Get the machine instructions for this BB
  // FIXME: MachineBasicBlock::get() is deprecated, hence inlining the function
  const Function *F = BB.getParent();
  MachineFunction &MF = MachineFunction::get(F);
  MachineBasicBlock *MBB = NULL;
  for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I)
    if (I->getBasicBlock() == &BB)
      MBB = I;

  assert(MBB && "MachineBasicBlock object not found for specified block!");
  MachineBasicBlock &mvec = *MBB;

  // Iterate over all machine instructions in the BB
  // Use a reverse iterator to allow deletion of MI or any instruction after it.
  // Insertions or deletions *before* MI are not safe.
  //
  for (MachineBasicBlock::reverse_iterator RI=mvec.rbegin(),
         RE=mvec.rend(); RI != RE; ) {
    MachineBasicBlock::iterator BBI = RI.base()-1; // save before incr
    ++RI;             // pre-increment to delete MI or after it
    visit(mvec, BBI);
  }

  return true;
}

//===----------------------------------------------------------------------===//
// createPeepholeOptsPass - Public entrypoint for peephole optimization
// and this file as a whole...
//
FunctionPass* createPeepholeOptsPass(const TargetMachine &TM) {
  return new PeepholeOpts(TM);
}

} // End llvm namespace
<commit_msg>* Converted C-style comments to C++ * Doxygenified comments * Reordered #includes<commit_after>//===-- PeepholeOpts.cpp --------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Support for performing several peephole opts in one or a few passes over the
// machine code of a method.
//
//===----------------------------------------------------------------------===//

#include "SparcInternals.h"
#include "llvm/BasicBlock.h"
#include "llvm/Pass.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Target/TargetMachine.h"

namespace llvm {

//************************* Internal Functions *****************************/

static inline void
DeleteInstruction(MachineBasicBlock& mvec,
                  MachineBasicBlock::iterator& BBI,
                  const TargetMachine& target) {
  // Check if this instruction is in a delay slot of its predecessor.
  if (BBI != mvec.begin()) {
      const TargetInstrInfo& mii = target.getInstrInfo();
      MachineInstr* predMI = *(BBI-1);
      if (unsigned ndelay = mii.getNumDelaySlots(predMI->getOpCode())) {
        // This instruction is in a delay slot of its predecessor, so
        // replace it with a nop. By replacing in place, we save having
        // to update the I-I maps.
        //
        assert(ndelay == 1 && "Not yet handling multiple-delay-slot targets");
        (*BBI)->replace(mii.getNOPOpCode(), 0);
        return;
      }
  }

  // The instruction is not in a delay slot, so we can simply erase it.
  mvec.erase(BBI);
  BBI = mvec.end();
}

//******************* Individual Peephole Optimizations ********************/

//----------------------------------------------------------------------------
// Function: IsUselessCopy
// Decide whether a machine instruction is a redundant copy:
// -- ADD    with g0 and result and operand are identical, or
// -- OR     with g0 and result and operand are identical, or
// -- FMOVS or FMOVD and result and operand are identical.
// Other cases are possible but very rare that they would be useless copies,
// so it's not worth analyzing them.
//----------------------------------------------------------------------------

static bool IsUselessCopy(const TargetMachine &target, const MachineInstr* MI) {
  if (MI->getOpCode() == V9::FMOVS || MI->getOpCode() == V9::FMOVD) {
    return (// both operands are allocated to the same register
            MI->getOperand(0).getAllocatedRegNum() ==
            MI->getOperand(1).getAllocatedRegNum());
  } else if (MI->getOpCode() == V9::ADDr || MI->getOpCode() == V9::ORr ||
             MI->getOpCode() == V9::ADDi || MI->getOpCode() == V9::ORi) {
    unsigned srcWithDestReg;

    for (srcWithDestReg = 0; srcWithDestReg < 2; ++srcWithDestReg)
      if (MI->getOperand(srcWithDestReg).hasAllocatedReg() &&
          MI->getOperand(srcWithDestReg).getAllocatedRegNum()
          == MI->getOperand(2).getAllocatedRegNum())
        break;

    if (srcWithDestReg == 2)
      return false;
    else {
      // else source and dest are allocated to the same register
      unsigned otherOp = 1 - srcWithDestReg;
      return (// either operand otherOp is register %g0
              (MI->getOperand(otherOp).hasAllocatedReg() &&
               MI->getOperand(otherOp).getAllocatedRegNum() ==
               target.getRegInfo().getZeroRegNum()) ||

              // or operand otherOp == 0
              (MI->getOperand(otherOp).getType()
               == MachineOperand::MO_SignExtendedImmed &&
               MI->getOperand(otherOp).getImmedValue() == 0));
    }
  }
  else
    return false;
}

inline bool
RemoveUselessCopies(MachineBasicBlock& mvec,
                    MachineBasicBlock::iterator& BBI,
                    const TargetMachine& target) {
  if (IsUselessCopy(target, *BBI)) {
    DeleteInstruction(mvec, BBI, target);
    return true;
  }
  return false;
}

//************************ Class Implementations **************************/

class PeepholeOpts: public BasicBlockPass {
  const TargetMachine ⌖
  bool visit(MachineBasicBlock& mvec,
             MachineBasicBlock::iterator BBI) const;
public:
  PeepholeOpts(const TargetMachine &TM): target(TM) { }
  bool runOnBasicBlock(BasicBlock &BB); // apply this pass to each BB
  virtual const char *getPassName() const { return "Peephole Optimization"; }

  // getAnalysisUsage - this pass preserves the CFG
  void getAnalysisUsage(AnalysisUsage &AU) const {
    AU.setPreservesCFG();
  }
};

// Apply a list of peephole optimizations to this machine instruction
// within its local context.  They are allowed to delete MI or any
// instruction before MI, but not
//
bool PeepholeOpts::visit(MachineBasicBlock& mvec,
                         MachineBasicBlock::iterator BBI) const {
  // Remove redundant copy instructions
  return RemoveUselessCopies(mvec, BBI, target);
}

bool PeepholeOpts::runOnBasicBlock(BasicBlock &BB) {
  // Get the machine instructions for this BB
  // FIXME: MachineBasicBlock::get() is deprecated, hence inlining the function
  const Function *F = BB.getParent();
  MachineFunction &MF = MachineFunction::get(F);
  MachineBasicBlock *MBB = NULL;
  for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I)
    if (I->getBasicBlock() == &BB)
      MBB = I;

  assert(MBB && "MachineBasicBlock object not found for specified block!");
  MachineBasicBlock &mvec = *MBB;

  // Iterate over all machine instructions in the BB
  // Use a reverse iterator to allow deletion of MI or any instruction after it.
  // Insertions or deletions *before* MI are not safe.
  //
  for (MachineBasicBlock::reverse_iterator RI=mvec.rbegin(),
         RE=mvec.rend(); RI != RE; ) {
    MachineBasicBlock::iterator BBI = RI.base()-1; // save before incr
    ++RI;             // pre-increment to delete MI or after it
    visit(mvec, BBI);
  }

  return true;
}

/// createPeepholeOptsPass - Public entry point for peephole optimization
///
FunctionPass* createPeepholeOptsPass(const TargetMachine &TM) {
  return new PeepholeOpts(TM);
}

} // End llvm namespace
<|endoftext|>


<commit_before>//===- LoopInstSimplify.cpp - Loop Instruction Simplification Pass --------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass performs lightweight instruction simplification on loop bodies.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "loop-instsimplify"
#include "llvm/Instructions.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Support/Debug.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/ADT/Statistic.h"
using namespace llvm;

STATISTIC(NumSimplified, "Number of redundant instructions simplified");

namespace {
  class LoopInstSimplify : public LoopPass {
  public:
    static char ID; // Pass ID, replacement for typeid
    LoopInstSimplify() : LoopPass(ID) {
      initializeLoopInstSimplifyPass(*PassRegistry::getPassRegistry());
    }

    bool runOnLoop(Loop*, LPPassManager&);

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesCFG();
      AU.addRequired<LoopInfo>();
      AU.addRequiredID(LoopSimplifyID);
      AU.addPreservedID(LoopSimplifyID);
      AU.addPreservedID(LCSSAID);
    }
  };
}

char LoopInstSimplify::ID = 0;
INITIALIZE_PASS_BEGIN(LoopInstSimplify, "loop-instsimplify",
                "Simplify instructions in loops", false, false)
INITIALIZE_PASS_DEPENDENCY(DominatorTree)
INITIALIZE_PASS_DEPENDENCY(LoopInfo)
INITIALIZE_PASS_DEPENDENCY(LCSSA)
INITIALIZE_PASS_END(LoopInstSimplify, "loop-instsimplify",
                "Simplify instructions in loops", false, false)

Pass *llvm::createLoopInstSimplifyPass() {
  return new LoopInstSimplify();
}

bool LoopInstSimplify::runOnLoop(Loop *L, LPPassManager &LPM) {
  DominatorTree *DT = getAnalysisIfAvailable<DominatorTree>();
  LoopInfo *LI = &getAnalysis<LoopInfo>();
  const TargetData *TD = getAnalysisIfAvailable<TargetData>();

  SmallVector<BasicBlock*, 8> ExitBlocks;
  L->getUniqueExitBlocks(ExitBlocks);
  array_pod_sort(ExitBlocks.begin(), ExitBlocks.end());

  SmallPtrSet<const Instruction*, 8> S1, S2, *ToSimplify = &S1, *Next = &S2;

  // The bit we are stealing from the pointer represents whether this basic
  // block is the header of a subloop, in which case we only process its phis.
  typedef PointerIntPair<BasicBlock*, 1> WorklistItem;
  SmallVector<WorklistItem, 16> VisitStack;
  SmallPtrSet<BasicBlock*, 32> Visited;

  bool Changed = false;
  bool LocalChanged;
  do {
    LocalChanged = false;

    VisitStack.clear();
    Visited.clear();

    VisitStack.push_back(WorklistItem(L->getHeader(), false));

    while (!VisitStack.empty()) {
      WorklistItem Item = VisitStack.pop_back_val();
      BasicBlock *BB = Item.getPointer();
      bool IsSubloopHeader = Item.getInt();

      // Simplify instructions in the current basic block.
      for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) {
        Instruction *I = BI++;

        // The first time through the loop ToSimplify is empty and we try to
        // simplify all instructions. On later iterations ToSimplify is not
        // empty and we only bother simplifying instructions that are in it.
        if (!ToSimplify->empty() && !ToSimplify->count(I))
          continue;

        // Don't bother simplifying unused instructions.
        if (!I->use_empty()) {
          Value *V = SimplifyInstruction(I, TD, DT);
          if (V && LI->replacementPreservesLCSSAForm(I, V)) {
            // Mark all uses for resimplification next time round the loop.
            for (Value::use_iterator UI = I->use_begin(), UE = I->use_end();
                 UI != UE; ++UI)
              Next->insert(cast<Instruction>(*UI));

            I->replaceAllUsesWith(V);
            LocalChanged = true;
            ++NumSimplified;
          }
        }
        LocalChanged |= RecursivelyDeleteTriviallyDeadInstructions(I);

        if (IsSubloopHeader && !isa<PHINode>(I))
          break;
      }

      // Add all successors to the worklist, except for loop exit blocks and the
      // bodies of subloops. We visit the headers of loops so that we can process
      // their phis, but we contract the rest of the subloop body and only follow
      // edges leading back to the original loop.
      for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE;
           ++SI) {
        BasicBlock *SuccBB = *SI;
        if (!Visited.insert(SuccBB))
          continue;

        const Loop *SuccLoop = LI->getLoopFor(SuccBB);
        if (SuccLoop && SuccLoop->getHeader() == SuccBB
                     && L->contains(SuccLoop)) {
          VisitStack.push_back(WorklistItem(SuccBB, true));

          SmallVector<BasicBlock*, 8> SubLoopExitBlocks;
          SuccLoop->getExitBlocks(SubLoopExitBlocks);

          for (unsigned i = 0; i < SubLoopExitBlocks.size(); ++i) {
            BasicBlock *ExitBB = SubLoopExitBlocks[i];
            if (LI->getLoopFor(ExitBB) == L && Visited.insert(ExitBB))
              VisitStack.push_back(WorklistItem(ExitBB, false));
          }

          continue;
        }

        bool IsExitBlock = std::binary_search(ExitBlocks.begin(),
                                              ExitBlocks.end(), SuccBB);
        if (IsExitBlock)
          continue;

        VisitStack.push_back(WorklistItem(SuccBB, false));
      }
    }

    // Place the list of instructions to simplify on the next loop iteration
    // into ToSimplify.
    std::swap(ToSimplify, Next);
    Next->clear();

    Changed |= LocalChanged;
  } while (LocalChanged);

  return Changed;
}
<commit_msg>LoopInstSimplify preserves ScalarEvolution.<commit_after>//===- LoopInstSimplify.cpp - Loop Instruction Simplification Pass --------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass performs lightweight instruction simplification on loop bodies.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "loop-instsimplify"
#include "llvm/Instructions.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/LoopPass.h"
#include "llvm/Support/Debug.h"
#include "llvm/Target/TargetData.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/ADT/Statistic.h"
using namespace llvm;

STATISTIC(NumSimplified, "Number of redundant instructions simplified");

namespace {
  class LoopInstSimplify : public LoopPass {
  public:
    static char ID; // Pass ID, replacement for typeid
    LoopInstSimplify() : LoopPass(ID) {
      initializeLoopInstSimplifyPass(*PassRegistry::getPassRegistry());
    }

    bool runOnLoop(Loop*, LPPassManager&);

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesCFG();
      AU.addRequired<LoopInfo>();
      AU.addRequiredID(LoopSimplifyID);
      AU.addPreservedID(LoopSimplifyID);
      AU.addPreservedID(LCSSAID);
      AU.addPreserved("scalar-evolution");
    }
  };
}

char LoopInstSimplify::ID = 0;
INITIALIZE_PASS_BEGIN(LoopInstSimplify, "loop-instsimplify",
                "Simplify instructions in loops", false, false)
INITIALIZE_PASS_DEPENDENCY(DominatorTree)
INITIALIZE_PASS_DEPENDENCY(LoopInfo)
INITIALIZE_PASS_DEPENDENCY(LCSSA)
INITIALIZE_PASS_END(LoopInstSimplify, "loop-instsimplify",
                "Simplify instructions in loops", false, false)

Pass *llvm::createLoopInstSimplifyPass() {
  return new LoopInstSimplify();
}

bool LoopInstSimplify::runOnLoop(Loop *L, LPPassManager &LPM) {
  DominatorTree *DT = getAnalysisIfAvailable<DominatorTree>();
  LoopInfo *LI = &getAnalysis<LoopInfo>();
  const TargetData *TD = getAnalysisIfAvailable<TargetData>();

  SmallVector<BasicBlock*, 8> ExitBlocks;
  L->getUniqueExitBlocks(ExitBlocks);
  array_pod_sort(ExitBlocks.begin(), ExitBlocks.end());

  SmallPtrSet<const Instruction*, 8> S1, S2, *ToSimplify = &S1, *Next = &S2;

  // The bit we are stealing from the pointer represents whether this basic
  // block is the header of a subloop, in which case we only process its phis.
  typedef PointerIntPair<BasicBlock*, 1> WorklistItem;
  SmallVector<WorklistItem, 16> VisitStack;
  SmallPtrSet<BasicBlock*, 32> Visited;

  bool Changed = false;
  bool LocalChanged;
  do {
    LocalChanged = false;

    VisitStack.clear();
    Visited.clear();

    VisitStack.push_back(WorklistItem(L->getHeader(), false));

    while (!VisitStack.empty()) {
      WorklistItem Item = VisitStack.pop_back_val();
      BasicBlock *BB = Item.getPointer();
      bool IsSubloopHeader = Item.getInt();

      // Simplify instructions in the current basic block.
      for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) {
        Instruction *I = BI++;

        // The first time through the loop ToSimplify is empty and we try to
        // simplify all instructions. On later iterations ToSimplify is not
        // empty and we only bother simplifying instructions that are in it.
        if (!ToSimplify->empty() && !ToSimplify->count(I))
          continue;

        // Don't bother simplifying unused instructions.
        if (!I->use_empty()) {
          Value *V = SimplifyInstruction(I, TD, DT);
          if (V && LI->replacementPreservesLCSSAForm(I, V)) {
            // Mark all uses for resimplification next time round the loop.
            for (Value::use_iterator UI = I->use_begin(), UE = I->use_end();
                 UI != UE; ++UI)
              Next->insert(cast<Instruction>(*UI));

            I->replaceAllUsesWith(V);
            LocalChanged = true;
            ++NumSimplified;
          }
        }
        LocalChanged |= RecursivelyDeleteTriviallyDeadInstructions(I);

        if (IsSubloopHeader && !isa<PHINode>(I))
          break;
      }

      // Add all successors to the worklist, except for loop exit blocks and the
      // bodies of subloops. We visit the headers of loops so that we can process
      // their phis, but we contract the rest of the subloop body and only follow
      // edges leading back to the original loop.
      for (succ_iterator SI = succ_begin(BB), SE = succ_end(BB); SI != SE;
           ++SI) {
        BasicBlock *SuccBB = *SI;
        if (!Visited.insert(SuccBB))
          continue;

        const Loop *SuccLoop = LI->getLoopFor(SuccBB);
        if (SuccLoop && SuccLoop->getHeader() == SuccBB
                     && L->contains(SuccLoop)) {
          VisitStack.push_back(WorklistItem(SuccBB, true));

          SmallVector<BasicBlock*, 8> SubLoopExitBlocks;
          SuccLoop->getExitBlocks(SubLoopExitBlocks);

          for (unsigned i = 0; i < SubLoopExitBlocks.size(); ++i) {
            BasicBlock *ExitBB = SubLoopExitBlocks[i];
            if (LI->getLoopFor(ExitBB) == L && Visited.insert(ExitBB))
              VisitStack.push_back(WorklistItem(ExitBB, false));
          }

          continue;
        }

        bool IsExitBlock = std::binary_search(ExitBlocks.begin(),
                                              ExitBlocks.end(), SuccBB);
        if (IsExitBlock)
          continue;

        VisitStack.push_back(WorklistItem(SuccBB, false));
      }
    }

    // Place the list of instructions to simplify on the next loop iteration
    // into ToSimplify.
    std::swap(ToSimplify, Next);
    Next->clear();

    Changed |= LocalChanged;
  } while (LocalChanged);

  return Changed;
}
<|endoftext|>