<commit_before>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "itkLinearInterpolateImageFunction.h"
#include "otbBCOInterpolateImageFunction.h"
#include "itkNearestNeighborInterpolateImageFunction.h"

//Transform
#include "otbCompositeTransform.h"
#include "itkScalableAffineTransform.h"
#include "itkTranslationTransform.h"
#include "itkIdentityTransform.h"
#include "itkScaleTransform.h"
#include "itkCenteredRigid2DTransform.h"

#include "otbStreamingResampleImageFilter.h"

namespace otb
{
enum
{
  Interpolator_NNeighbor,
  Interpolator_Linear,
  Interpolator_BCO
};

enum
{
  Transform_Translation,
  Transform_Rotation,
};

namespace Wrapper
{

class RigidTransformResample : public Application
{
public:
  /** Standard class typedefs. */
  typedef RigidTransformResample        Self;
  typedef Application                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  typedef itk::TranslationTransform<double, FloatVectorImageType::ImageDimension> TransformType;
  typedef otb::StreamingResampleImageFilter<FloatVectorImageType, FloatVectorImageType, double>    ResampleFilterType;

  //typedef itk::IdentityTransform<double, FloatVectorImageType::ImageDimension>      IdentityTransformType;

  typedef itk::ScalableAffineTransform<double, FloatVectorImageType::ImageDimension> ScalableTransformType;
  typedef ScalableTransformType::OutputVectorType                         OutputVectorType;

  /** Rotation transform */
  typedef itk::CenteredRigid2DTransform< double > RotationTransformType;
  typedef RotationTransformType::ScalarType              ScalarType;

  itkTypeMacro(RigidTransformResample, otb::Application);

private:

  void DoInit()
  {
    SetName("RigidTransformResample");
    SetDescription("Resample an image with a rigid transform");
    // Documentation
    SetDocName("Image resampling with a rigid transform");
    SetDocLongDescription("This application performs an translation on the input image.\n Parameters of the translation can be set with tx and ty options.");
    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso("Translation");

    AddDocTag("Conversion");
    AddDocTag(Tags::Geometry);

    AddParameter(ParameterType_InputImage,   "in",   "Input image");
    SetParameterDescription("in","The input image to translate.");
    AddParameter(ParameterType_OutputImage,  "out",  "Output image");
    SetParameterDescription("out","The translated output image.");

    //Transform
    AddParameter(ParameterType_Group,"transform","Transform parameters");
    SetParameterDescription("transform","This group of parameters allows to set the transformation to apply.");

    AddParameter(ParameterType_Choice, "transform.type", "Type of transformation");
    SetParameterDescription("transform.type","Type of transformation. Available transformations are translation and rotation with scaling factor");

    AddChoice("transform.type.translation", "translation");
    SetParameterDescription("transform.type.translation","translation");

    AddParameter(ParameterType_Float,"transform.type.translation.tx",   "The X translation (in physical units)");
    SetParameterDescription("transform.type.translation.tx","The translation value along X axis (in physical units).");
    SetDefaultParameterFloat("transform.type.translation.tx",0.);
    AddParameter(ParameterType_Float,"transform.type.translation.ty",   "The Y translation (in physical units)");
    SetParameterDescription("transform.type.translation.ty","The translation value along Y axis (in physical units)");
    SetDefaultParameterFloat("transform.type.translation.tx",0.);

    AddChoice("transform.type.rotation", "rotation");
    SetParameterDescription("transform.type.rotation","rotation");

    AddParameter(ParameterType_Float, "transform.type.rotation.angle", "Rotation angle");
    SetParameterDescription("transform.type.rotation.angle","The rotation angle in degree (values between -180 and 180)");
    SetDefaultParameterFloat("transform.type.rotation.angle",0.);

    AddParameter(ParameterType_Float, "transform.type.rotation.scalex", "X factor");
    SetParameterDescription("transform.type.rotation.scalex","X factor");
    SetDefaultParameterFloat("transform.type.rotation.scalex",1.);

    AddParameter(ParameterType_Float, "transform.type.rotation.scaley", "Y factor");
    SetParameterDescription("transform.type.rotation.scaley","Y factor");
    SetDefaultParameterFloat("transform.type.rotation.scaley",1.);

    // Interpolators
    AddParameter(ParameterType_Choice,   "interpolator", "Interpolation");
    SetParameterDescription("interpolator","This group of parameters allows to define how the input image will be interpolated during resampling.");
    AddChoice("interpolator.nn",     "Nearest Neighbor interpolation");
    SetParameterDescription("interpolator.nn","Nearest neighbor interpolation leads to poor image quality, but it is very fast.");
    AddChoice("interpolator.linear", "Linear interpolation");
    SetParameterDescription("interpolator.linear","Linear interpolation leads to average image quality but is quite fast");
    AddChoice("interpolator.bco",    "Bicubic interpolation");
    AddParameter(ParameterType_Radius, "interpolator.bco.radius", "Radius for bicubic interpolation");
    SetParameterDescription("interpolator.bco.radius","This parameter allows to control the size of the bicubic interpolation filter. If the target pixel size is higher than the input pixel size, increasing this parameter will reduce aliasing artefacts.");
    SetDefaultParameterInt("interpolator.bco.radius", 2);
    SetParameterString("interpolator","bco");

    // RAM available
    AddRAMParameter("ram");
    SetParameterDescription("ram","This allows to set the maximum amount of RAM available for processing. As the writing task is time consuming, it is better to write large pieces of data, which can be achieved by increasing this parameter (pay attention to your system capabilities)");

    // Doc example parameter settings
    SetDocExampleParameterValue("in", "qb_toulouse_sub.tif");
    SetDocExampleParameterValue("out", "rigitTransformImage.tif");
    SetDocExampleParameterValue("transform.type", "rotation");
    SetDocExampleParameterValue("transform.type.scalex", "4.");
    SetDocExampleParameterValue("transform.type.scaley", "4.");
  }

  void DoUpdateParameters()
  {
    // Nothing to do here : all parameters are independent
  }

  void DoExecute()
  {
    FloatVectorImageType* inputImage = GetParameterImage("in");

    m_Resampler = ResampleFilterType::New();
    m_Resampler->SetInput(inputImage);

    // Get Interpolator
    switch ( GetParameterInt("interpolator") )
      {
      case Interpolator_Linear:
      {
      typedef itk::LinearInterpolateImageFunction<FloatVectorImageType,
                                                  double>          LinearInterpolationType;
      LinearInterpolationType::Pointer interpolator = LinearInterpolationType::New();
      m_Resampler->SetInterpolator(interpolator);
      }
      break;
      case Interpolator_NNeighbor:
      {
      typedef itk::NearestNeighborInterpolateImageFunction<FloatVectorImageType,
                                                           double> NearestNeighborInterpolationType;
      NearestNeighborInterpolationType::Pointer interpolator = NearestNeighborInterpolationType::New();
      m_Resampler->SetInterpolator(interpolator);
      }
      break;
      case Interpolator_BCO:
      {
      typedef otb::BCOInterpolateImageFunction<FloatVectorImageType> BCOInterpolationType;
      BCOInterpolationType::Pointer interpolator = BCOInterpolationType::New();
      interpolator->SetRadius(GetParameterInt("interpolator.bco.radius"));
      m_Resampler->SetInterpolator(interpolator);
      }
      break;
      }

    // Get Transform
    switch ( GetParameterInt("transform.type") )
      {
      case Transform_Translation:
      {
      TransformType::Pointer transform = TransformType::New();
      TransformType::OutputVectorType offset;
      offset[0] = GetParameterFloat("transform.type.translation.tx");
      offset[1] = GetParameterFloat("transform.type.translation.ty");
      otbAppLogINFO( << "Offset : " << offset );
      transform->SetOffset(offset);
      m_Resampler->SetTransform(transform);
      m_Resampler->SetOutputParametersFromImage(inputImage);
      }
      break;

      case Transform_Rotation:
      {
      ScalableTransformType::Pointer transform = ScalableTransformType::New();

      FloatVectorImageType::IndexType origin = inputImage->GetLargestPossibleRegion().GetIndex();
      FloatVectorImageType::SpacingType spacing = inputImage->GetSpacing();

      FloatVectorImageType::IndexType center;
      center[0] = origin[0] + inputImage->GetLargestPossibleRegion().GetSize()[0] / 2.0;
      center[1] = origin[1] + inputImage->GetLargestPossibleRegion().GetSize()[1] / 2.0;

      FloatVectorImageType::PointType centerPoint;
      inputImage->TransformIndexToPhysicalPoint(center, centerPoint);

      //image boundary
      FloatVectorImageType::IndexType ULindex, URindex, LRindex, LLindex;

      ULindex[0]=origin[0];
      ULindex[1]=origin[1];
      URindex[0]=origin[0]+ inputImage->GetLargestPossibleRegion().GetSize()[0];
      URindex[1]=origin[1];
      LRindex[0]=origin[0]+ inputImage->GetLargestPossibleRegion().GetSize()[0];
      LRindex[1]=origin[1]+ inputImage->GetLargestPossibleRegion().GetSize()[1];
      LLindex[0]=origin[0];
      LLindex[1]=origin[1]+ inputImage->GetLargestPossibleRegion().GetSize()[1];

      FloatVectorImageType::PointType orig, ULpoint, URpoint, LRpoint, LLpoint;
      inputImage->TransformIndexToPhysicalPoint(ULindex, ULpoint);
      inputImage->TransformIndexToPhysicalPoint(URindex, URpoint);
      inputImage->TransformIndexToPhysicalPoint(LRindex, LRpoint);
      inputImage->TransformIndexToPhysicalPoint(LLindex, LLpoint);

      // Scale Transform
      OutputVectorType scale;
      scale[0] = 1.0 / GetParameterFloat("transform.type.rotation.scalex");
      scale[1] = 1.0 / GetParameterFloat("transform.type.rotation.scaley");

      //angle of rotation
      ScalarType rot_angle = GetParameterFloat("transform.type.rotation.angle");
      if (rot_angle < -180 || rot_angle > 180)
        {
        itkExceptionMacro(<<"The rotation angle must value  be between -180 and 180.");
        }

      transform->SetIdentity();
      if(spacing[0] > 0 && spacing[1] > 0) transform->Rotate2D( rot_angle * CONST_PI_180 );
      else transform->Rotate2D( - rot_angle * CONST_PI_180 );

      transform->SetCenter( centerPoint );
      transform->Scale( scale );

      //inverse transform
      ScalableTransformType::Pointer inverseTransform = ScalableTransformType::New();
      transform->GetInverse(inverseTransform);
      m_Resampler->SetTransform(transform);

      FloatVectorImageType::PointType ULpointTrans, URpointTrans, LRpointTrans, LLpointTrans, CenterPointTrans;

      ULpointTrans=inverseTransform->TransformPoint(ULpoint);
      URpointTrans=inverseTransform->TransformPoint(URpoint);
      LRpointTrans=inverseTransform->TransformPoint(LRpoint);
      LLpointTrans=inverseTransform->TransformPoint(LLpoint);
      CenterPointTrans=inverseTransform->TransformPoint(centerPoint);

      //compute min and max
      std::vector<FloatVectorImageType::PointType>   voutput;
      voutput.push_back(ULpointTrans);
      voutput.push_back(URpointTrans);
      voutput.push_back(LRpointTrans);
      voutput.push_back(LLpointTrans);

      double minX = voutput[0][0];
      double maxX = voutput[0][0];
      double minY = voutput[0][1];
      double maxY = voutput[0][1];

      for(unsigned int i = 0; i<voutput.size(); i++)
        {
        // Origins
        if ( minX > voutput[i][0] )
          {
          minX = voutput[i][0];
          }
        if ( minY > voutput[i][1] )
          {
          minY = voutput[i][1];
          }

        // Sizes
        if ( maxX < voutput[i][0] )
          {
          maxX = voutput[i][0];
          }
        if ( maxY < voutput[i][1] )
          {
          maxY = voutput[i][1];
          }
        }

      if( spacing[0] > 0 ) orig[0] = minX;
      else orig[0] = maxX;

      if( spacing[1] > 0 ) orig[1] = minY;
      else orig[1] = maxY;

      m_Resampler->SetOutputOrigin(orig);

      //size of output image
      ResampleFilterType::SizeType size;
      size[0]=vcl_abs(maxX-minX);
      size[1]=vcl_abs(maxY-minY);

      // Evaluate spacing
      FloatVectorImageType::SpacingType OutputSpacing;
      OutputSpacing=spacing;

      //FIXME Find a way to update the output image spacing after resampling
      //OutputSpacing[0] = scale[0] * spacing[0];
      //OutputSpacing[1] = scale[1] * spacing[1];

      m_Resampler->SetOutputSpacing(OutputSpacing);

      // Evaluate size
      ResampleFilterType::SizeType recomputedSize;
      recomputedSize[0] = static_cast<unsigned int>(vcl_floor(vcl_abs(size[0]/OutputSpacing[0])));
      recomputedSize[1] = static_cast<unsigned int>(vcl_floor(vcl_abs(size[1]/OutputSpacing[1])));
      m_Resampler->SetOutputSize( recomputedSize );
      otbAppLogINFO( << "Output image size : " << recomputedSize );
      }
      break;
      }

    FloatVectorImageType::PixelType defaultValue;
    itk::PixelBuilder<FloatVectorImageType::PixelType>::Zero(defaultValue,
                                                             inputImage->GetNumberOfComponentsPerPixel());
    m_Resampler->SetEdgePaddingValue(defaultValue);

    m_Resampler->UpdateOutputInformation();
    // Output Image
    SetParameterOutputImage("out", m_Resampler->GetOutput());
  }

  ResampleFilterType::Pointer m_Resampler;
}; //class

} //namespace wrapper
} //namespace otb

OTB_APPLICATION_EXPORT(otb::Wrapper::RigidTransformResample)
<commit_msg>ENH: add identity transform to allow zoom/dezoom with correct output spacing<commit_after>/*=========================================================================

 Program:   ORFEO Toolbox
 Language:  C++
 Date:      $Date$
 Version:   $Revision$

 Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
 See OTBCopyright.txt for details.

 This software is distributed WITHOUT ANY WARRANTY; without even
 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 PURPOSE.  See the above copyright notices for more information.

 =========================================================================*/
#include "otbWrapperApplication.h"
#include "otbWrapperApplicationFactory.h"

#include "itkLinearInterpolateImageFunction.h"
#include "otbBCOInterpolateImageFunction.h"
#include "itkNearestNeighborInterpolateImageFunction.h"

//Transform
#include "otbCompositeTransform.h"
#include "itkScalableAffineTransform.h"
#include "itkTranslationTransform.h"
#include "itkIdentityTransform.h"
#include "itkScaleTransform.h"
#include "itkCenteredRigid2DTransform.h"

#include "otbStreamingResampleImageFilter.h"

namespace otb
{
enum
{
  Interpolator_NNeighbor,
  Interpolator_Linear,
  Interpolator_BCO
};

enum
{
  Transform_Identity,
  Transform_Translation,
  Transform_Rotation,
};

namespace Wrapper
{

class RigidTransformResample : public Application
{
public:
  /** Standard class typedefs. */
  typedef RigidTransformResample        Self;
  typedef Application                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;

  /** Standard macro */
  itkNewMacro(Self);

  typedef itk::TranslationTransform<double, FloatVectorImageType::ImageDimension> TransformType;
  typedef otb::StreamingResampleImageFilter<FloatVectorImageType, FloatVectorImageType, double>    ResampleFilterType;

  typedef itk::IdentityTransform<double, FloatVectorImageType::ImageDimension>      IdentityTransformType;

  typedef itk::ScalableAffineTransform<double, FloatVectorImageType::ImageDimension> ScalableTransformType;
  typedef ScalableTransformType::OutputVectorType                         OutputVectorType;

  /** Rotation transform */
  typedef itk::CenteredRigid2DTransform< double > RotationTransformType;
  typedef RotationTransformType::ScalarType              ScalarType;

  itkTypeMacro(RigidTransformResample, otb::Application);

private:

  void DoInit()
  {
    SetName("RigidTransformResample");
    SetDescription("Resample an image with a rigid transform");
    // Documentation
    SetDocName("Image resampling with a rigid transform");
    SetDocLongDescription("This application performs an translation on the input image.\n Parameters of the translation can be set with tx and ty options.");
    SetDocLimitations("None");
    SetDocAuthors("OTB-Team");
    SetDocSeeAlso("Translation");

    AddDocTag("Conversion");
    AddDocTag(Tags::Geometry);

    AddParameter(ParameterType_InputImage,   "in",   "Input image");
    SetParameterDescription("in","The input image to translate.");
    AddParameter(ParameterType_OutputImage,  "out",  "Output image");
    SetParameterDescription("out","The translated output image.");

    //Transform
    AddParameter(ParameterType_Group,"transform","Transform parameters");
    SetParameterDescription("transform","This group of parameters allows to set the transformation to apply.");

    AddParameter(ParameterType_Choice, "transform.type", "Type of transformation");
    SetParameterDescription("transform.type","Type of transformation. Available transformations are translation and rotation with scaling factor");

    AddChoice("transform.type.id", "translation");
    SetParameterDescription("transform.type.id","translation");

    AddParameter(ParameterType_Float,"transform.type.id.scalex",   "The X translation (in physical units)");
    SetParameterDescription("transform.type.id.scalex","The translation value along X axis (in physical units).");
    SetDefaultParameterFloat("transform.type.id.scalex",1.);
    AddParameter(ParameterType_Float,"transform.type.id.scaley",   "The Y translation (in physical units)");
    SetParameterDescription("transform.type.id.scaley","The translation value along Y axis (in physical units)");
    SetDefaultParameterFloat("transform.type.id.scaley",1.);

    AddChoice("transform.type.translation", "translation");
    SetParameterDescription("transform.type.translation","translation");

    AddParameter(ParameterType_Float,"transform.type.translation.tx",   "The X translation (in physical units)");
    SetParameterDescription("transform.type.translation.tx","The translation value along X axis (in physical units).");
    SetDefaultParameterFloat("transform.type.translation.tx",0.);
    AddParameter(ParameterType_Float,"transform.type.translation.ty",   "The Y translation (in physical units)");
    SetParameterDescription("transform.type.translation.ty","The translation value along Y axis (in physical units)");
    SetDefaultParameterFloat("transform.type.translation.tx",0.);

    AddChoice("transform.type.rotation", "rotation");
    SetParameterDescription("transform.type.rotation","rotation");

    AddParameter(ParameterType_Float, "transform.type.rotation.angle", "Rotation angle");
    SetParameterDescription("transform.type.rotation.angle","The rotation angle in degree (values between -180 and 180)");
    SetDefaultParameterFloat("transform.type.rotation.angle",0.);

    AddParameter(ParameterType_Float, "transform.type.rotation.scalex", "X factor");
    SetParameterDescription("transform.type.rotation.scalex","X factor");
    SetDefaultParameterFloat("transform.type.rotation.scalex",1.);

    AddParameter(ParameterType_Float, "transform.type.rotation.scaley", "Y factor");
    SetParameterDescription("transform.type.rotation.scaley","Y factor");
    SetDefaultParameterFloat("transform.type.rotation.scaley",1.);

    // Interpolators
    AddParameter(ParameterType_Choice,   "interpolator", "Interpolation");
    SetParameterDescription("interpolator","This group of parameters allows to define how the input image will be interpolated during resampling.");
    AddChoice("interpolator.nn",     "Nearest Neighbor interpolation");
    SetParameterDescription("interpolator.nn","Nearest neighbor interpolation leads to poor image quality, but it is very fast.");
    AddChoice("interpolator.linear", "Linear interpolation");
    SetParameterDescription("interpolator.linear","Linear interpolation leads to average image quality but is quite fast");
    AddChoice("interpolator.bco",    "Bicubic interpolation");
    AddParameter(ParameterType_Radius, "interpolator.bco.radius", "Radius for bicubic interpolation");
    SetParameterDescription("interpolator.bco.radius","This parameter allows to control the size of the bicubic interpolation filter. If the target pixel size is higher than the input pixel size, increasing this parameter will reduce aliasing artefacts.");
    SetDefaultParameterInt("interpolator.bco.radius", 2);
    SetParameterString("interpolator","bco");

    // RAM available
    AddRAMParameter("ram");
    SetParameterDescription("ram","This allows to set the maximum amount of RAM available for processing. As the writing task is time consuming, it is better to write large pieces of data, which can be achieved by increasing this parameter (pay attention to your system capabilities)");

    // Doc example parameter settings
    SetDocExampleParameterValue("in", "qb_toulouse_sub.tif");
    SetDocExampleParameterValue("out", "rigitTransformImage.tif");
    SetDocExampleParameterValue("transform.type", "rotation");
    SetDocExampleParameterValue("transform.type.scalex", "4.");
    SetDocExampleParameterValue("transform.type.scaley", "4.");
  }

  void DoUpdateParameters()
  {
    // Nothing to do here : all parameters are independent
  }

  void DoExecute()
  {
    FloatVectorImageType* inputImage = GetParameterImage("in");

    m_Resampler = ResampleFilterType::New();
    m_Resampler->SetInput(inputImage);

    // Get Interpolator
    switch ( GetParameterInt("interpolator") )
      {
      case Interpolator_Linear:
      {
      typedef itk::LinearInterpolateImageFunction<FloatVectorImageType,
                                                  double>          LinearInterpolationType;
      LinearInterpolationType::Pointer interpolator = LinearInterpolationType::New();
      m_Resampler->SetInterpolator(interpolator);
      }
      break;
      case Interpolator_NNeighbor:
      {
      typedef itk::NearestNeighborInterpolateImageFunction<FloatVectorImageType,
                                                           double> NearestNeighborInterpolationType;
      NearestNeighborInterpolationType::Pointer interpolator = NearestNeighborInterpolationType::New();
      m_Resampler->SetInterpolator(interpolator);
      }
      break;
      case Interpolator_BCO:
      {
      typedef otb::BCOInterpolateImageFunction<FloatVectorImageType> BCOInterpolationType;
      BCOInterpolationType::Pointer interpolator = BCOInterpolationType::New();
      interpolator->SetRadius(GetParameterInt("interpolator.bco.radius"));
      m_Resampler->SetInterpolator(interpolator);
      }
      break;
      }

    // Get Transform
    switch ( GetParameterInt("transform.type") )
      {
      case Transform_Identity:
      {
      IdentityTransformType::Pointer transform = IdentityTransformType::New();

      // Scale Transform
      OutputVectorType scale;
      scale[0] = 1.0 / GetParameterFloat("transform.type.id.scalex");
      scale[1] = 1.0 / GetParameterFloat("transform.type.id.scaley");

      // Evaluate spacing
      FloatVectorImageType::SpacingType spacing = inputImage->GetSpacing();
      FloatVectorImageType::SpacingType OutputSpacing;
      OutputSpacing=spacing;

      //FIXME Find a way to update the output image spacing after resampling
      OutputSpacing[0] = spacing[0] * scale[0];
      OutputSpacing[1] = spacing[1] * scale[1];

      m_Resampler->SetOutputSpacing(OutputSpacing);
      m_Resampler->SetTransform(transform);

      // Evaluate size
      ResampleFilterType::SizeType recomputedSize;
      recomputedSize[0] = inputImage->GetLargestPossibleRegion().GetSize()[0] / scale[0];
      recomputedSize[1] = inputImage->GetLargestPossibleRegion().GetSize()[1] / scale[1];

      m_Resampler->SetOutputSize(recomputedSize);
      otbAppLogINFO( << "Output image size : " << recomputedSize );
      }
      break;

      case Transform_Translation:
      {
      TransformType::Pointer transform = TransformType::New();
      TransformType::OutputVectorType offset;
      offset[0] = GetParameterFloat("transform.type.translation.tx");
      offset[1] = GetParameterFloat("transform.type.translation.ty");
      otbAppLogINFO( << "Offset : " << offset );
      transform->SetOffset(offset);
      m_Resampler->SetTransform(transform);
      m_Resampler->SetOutputParametersFromImage(inputImage);
      }
      break;

      case Transform_Rotation:
      {
      ScalableTransformType::Pointer transform = ScalableTransformType::New();

      FloatVectorImageType::IndexType origin = inputImage->GetLargestPossibleRegion().GetIndex();
      FloatVectorImageType::SpacingType spacing = inputImage->GetSpacing();

      FloatVectorImageType::IndexType center;
      center[0] = origin[0] + inputImage->GetLargestPossibleRegion().GetSize()[0] / 2.0;
      center[1] = origin[1] + inputImage->GetLargestPossibleRegion().GetSize()[1] / 2.0;

      FloatVectorImageType::PointType centerPoint;
      inputImage->TransformIndexToPhysicalPoint(center, centerPoint);

      //image boundary
      FloatVectorImageType::IndexType ULindex, URindex, LRindex, LLindex;

      ULindex[0]=origin[0];
      ULindex[1]=origin[1];
      URindex[0]=origin[0]+ inputImage->GetLargestPossibleRegion().GetSize()[0];
      URindex[1]=origin[1];
      LRindex[0]=origin[0]+ inputImage->GetLargestPossibleRegion().GetSize()[0];
      LRindex[1]=origin[1]+ inputImage->GetLargestPossibleRegion().GetSize()[1];
      LLindex[0]=origin[0];
      LLindex[1]=origin[1]+ inputImage->GetLargestPossibleRegion().GetSize()[1];

      FloatVectorImageType::PointType orig, ULpoint, URpoint, LRpoint, LLpoint;
      inputImage->TransformIndexToPhysicalPoint(ULindex, ULpoint);
      inputImage->TransformIndexToPhysicalPoint(URindex, URpoint);
      inputImage->TransformIndexToPhysicalPoint(LRindex, LRpoint);
      inputImage->TransformIndexToPhysicalPoint(LLindex, LLpoint);

      // Scale Transform
      OutputVectorType scale;
      scale[0] = 1.0 / GetParameterFloat("transform.type.rotation.scalex");
      scale[1] = 1.0 / GetParameterFloat("transform.type.rotation.scaley");

      //angle of rotation
      ScalarType rot_angle = GetParameterFloat("transform.type.rotation.angle");
      if (rot_angle < -180 || rot_angle > 180)
        {
        itkExceptionMacro(<<"The rotation angle must value  be between -180 and 180.");
        }

      transform->SetIdentity();
      if(spacing[0] > 0 && spacing[1] > 0) transform->Rotate2D( rot_angle * CONST_PI_180 );
      else transform->Rotate2D( - rot_angle * CONST_PI_180 );

      transform->SetCenter( centerPoint );
      transform->Scale( scale );

      //inverse transform
      ScalableTransformType::Pointer inverseTransform = ScalableTransformType::New();
      transform->GetInverse(inverseTransform);
      m_Resampler->SetTransform(transform);

      FloatVectorImageType::PointType ULpointTrans, URpointTrans, LRpointTrans, LLpointTrans, CenterPointTrans;

      ULpointTrans=inverseTransform->TransformPoint(ULpoint);
      URpointTrans=inverseTransform->TransformPoint(URpoint);
      LRpointTrans=inverseTransform->TransformPoint(LRpoint);
      LLpointTrans=inverseTransform->TransformPoint(LLpoint);
      CenterPointTrans=inverseTransform->TransformPoint(centerPoint);

      //compute min and max
      std::vector<FloatVectorImageType::PointType>   voutput;
      voutput.push_back(ULpointTrans);
      voutput.push_back(URpointTrans);
      voutput.push_back(LRpointTrans);
      voutput.push_back(LLpointTrans);

      double minX = voutput[0][0];
      double maxX = voutput[0][0];
      double minY = voutput[0][1];
      double maxY = voutput[0][1];

      for(unsigned int i = 0; i<voutput.size(); i++)
        {
        // Origins
        if ( minX > voutput[i][0] )
          {
          minX = voutput[i][0];
          }
        if ( minY > voutput[i][1] )
          {
          minY = voutput[i][1];
          }

        // Sizes
        if ( maxX < voutput[i][0] )
          {
          maxX = voutput[i][0];
          }
        if ( maxY < voutput[i][1] )
          {
          maxY = voutput[i][1];
          }
        }

      if( spacing[0] > 0 ) orig[0] = minX;
      else orig[0] = maxX;

      if( spacing[1] > 0 ) orig[1] = minY;
      else orig[1] = maxY;

      m_Resampler->SetOutputOrigin(orig);

      //size of output image
      ResampleFilterType::SizeType size;
      size[0]=vcl_abs(maxX-minX);
      size[1]=vcl_abs(maxY-minY);

      // Evaluate spacing
      FloatVectorImageType::SpacingType OutputSpacing;
      OutputSpacing=spacing;

      //FIXME Find a way to update the output image spacing after resampling
      //OutputSpacing[0] = scale[0] * spacing[0];
      //OutputSpacing[1] = scale[1] * spacing[1];

      m_Resampler->SetOutputSpacing(OutputSpacing);

      // Evaluate size
      ResampleFilterType::SizeType recomputedSize;
      recomputedSize[0] = static_cast<unsigned int>(vcl_floor(vcl_abs(size[0]/OutputSpacing[0])));
      recomputedSize[1] = static_cast<unsigned int>(vcl_floor(vcl_abs(size[1]/OutputSpacing[1])));
      m_Resampler->SetOutputSize( recomputedSize );
      otbAppLogINFO( << "Output image size : " << recomputedSize );
      }
      break;
      }

    FloatVectorImageType::PixelType defaultValue;
    itk::PixelBuilder<FloatVectorImageType::PixelType>::Zero(defaultValue,
                                                             inputImage->GetNumberOfComponentsPerPixel());
    m_Resampler->SetEdgePaddingValue(defaultValue);

    m_Resampler->UpdateOutputInformation();
    // Output Image
    SetParameterOutputImage("out", m_Resampler->GetOutput());
  }

  ResampleFilterType::Pointer m_Resampler;
}; //class

} //namespace wrapper
} //namespace otb

OTB_APPLICATION_EXPORT(otb::Wrapper::RigidTransformResample)
<|endoftext|>


<commit_before>/* Copyright 2016 Stanford University, NVIDIA Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

////////////////////////////////////////////////////////////
// THIS EXAMPLE MUST BE BUILT WITH A VERSION
// OF GASNET CONFIGURED WITH MPI COMPATIBILITY
//
// NOTE THAT GASNET ONLY SUPPORTS MPI-COMPATIBILITY
// ON SOME CONDUITS. CURRENTLY THESE ARE IBV, GEMINI,
// ARIES, MXM, and OFI. IF YOU WOULD LIKE ADDITIONAL
// CONDUITS SUPPORTED PLEASE CONTACT THE MAINTAINERS
// OF GASNET.
//
// Note: there is a way to use this example with the
// MPI conduit, but you have to have a version of
// MPI that supports MPI_THREAD_MULTIPLE. See the
// macro GASNET_CONDUIT_MPI below.
////////////////////////////////////////////////////////////

#include <cstdio>
// Need MPI header file
#include <mpi.h>

#include "legion.h"

using namespace Legion;

enum TaskID
{
  TOP_LEVEL_TASK_ID,
  MPI_INTEROP_TASK_ID,
  WORKER_TASK_ID,
};

// Here is our global MPI-Legion handshake
// You can have as many of these as you
// want but the common case is just to
// have one per Legion-MPI rank pair
MPILegionHandshake handshake;

// Have a global static number of iterations for
// this example, but you can easily configure it
// from command line arguments which get passed
// to both MPI and Legion
const int total_iterations = 10;

void worker_task(const Task *task,
                 const std::vector<PhysicalRegion> &regions,
                 Context ctx, Runtime *runtime)
{
  printf("Legion Doing Work in Rank %lld\n",
          task->parent_task->index_point[0]);
}

void mpi_interop_task(const Task *task,
                      const std::vector<PhysicalRegion> &regions,
                      Context ctx, Runtime *runtime)
{
  printf("Hello from Legion MPI-Interop Task %lld\n", task->index_point[0]);
  for (int i = 0; i < total_iterations; i++)
  {
    // Legion can interop with MPI in blocking and non-blocking
    // ways. You can use the calls to 'legion_wait_on_mpi' and
    // 'legion_handoff_to_mpi' in the same way as the MPI thread
    // does. Alternatively, you can get a phase barrier associated
    // with a LegionMPIHandshake object which will allow you to
    // continue launching more sub-tasks without blocking.
    // For deferred execution we prefer the later style, but
    // both will work correctly.
    if (i < (total_iterations/2))
    {
      // This is the blocking way of using handshakes, it
      // is not the ideal way, but it works correctly
      // Wait for MPI to give us control to run our worker
      // This is a blocking call
      handshake.legion_wait_on_mpi();
      // Launch our worker task
      TaskLauncher worker_launcher(WORKER_TASK_ID, TaskArgument(NULL,0));
      Future f = runtime->execute_task(ctx, worker_launcher);
      // Have to wait for the result before signaling MPI
      f.get_void_result();
      // Perform a non-blocking call to signal
      // MPI that we are giving it control back
      handshake.legion_handoff_to_mpi();
    }
    else
    {
      // This is the preferred way of using handshakes in Legion
      TaskLauncher worker_launcher(WORKER_TASK_ID, TaskArgument(NULL,0));
      // We can user our handshake as a phase barrier
      // Record that we will wait on this handshake
      worker_launcher.add_wait_handshake(handshake);
      // Advance the handshake to the next version
      handshake.advance_legion_handshake();
      // Then record that we will arrive on this versions
      worker_launcher.add_arrival_handshake(handshake);
      // Launch our worker task
      // No need to wait for anything
      runtime->execute_task(ctx, worker_launcher);
    }
  }
}

void top_level_task(const Task *task,
                    const std::vector<PhysicalRegion> &regions,
                    Context ctx, Runtime *runtime)
{
  printf("Hello from Legion Top-Level Task\n");
  // Both the application and Legion mappers have access to
  // the mappings between MPI Ranks and Legion address spaces
  // The reverse mapping goes the other way
  const std::map<int,AddressSpace> &forward_mapping =
    runtime->find_forward_MPI_mapping();
  for (std::map<int,AddressSpace>::const_iterator it =
        forward_mapping.begin(); it != forward_mapping.end(); it++)
    printf("MPI Rank %d maps to Legion Address Space %d\n",
            it->first, it->second);

  int rank = -1, size = -1;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  // Do a must epoch launch to align with the number of MPI ranks
  MustEpochLauncher must_epoch_launcher;
  Rect<1> launch_bounds(Point<1>(0),Point<1>(size - 1));
  Domain launch_domain = Domain::from_rect<1>(launch_bounds);
  ArgumentMap args_map;
  IndexLauncher index_launcher(MPI_INTEROP_TASK_ID, launch_domain,
                               TaskArgument(NULL, 0), args_map);
  must_epoch_launcher.add_index_task(index_launcher);
  runtime->execute_must_epoch(ctx, must_epoch_launcher);
}

int main(int argc, char **argv)
{
#ifdef GASNET_CONDUIT_MPI
  // The GASNet MPI conduit requires special start-up
  // in order to handle MPI calls from multiple threads
  int provided;
  MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
  // If you fail this assertion, then your version of MPI
  // does not support calls from multiple threads and you
  // cannot use the GASNet MPI conduit
  if (provided != MPI_THREAD_MULTIPLE)
    printf("ERROR: MPI_THREAD_MULTIPLE not supported!\n");
  assert(provided == MPI_THREAD_MULTIPLE);
#else
  // Perform MPI start-up like normal for most GASNet conduits
  MPI_Init(&argc, &argv);
#endif

  int rank = -1, size = -1;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  printf("Hello from MPI process %d of %d\n", rank, size);

  // Configure the Legion runtime with the rank of this process
  Runtime::configure_MPI_interoperability(rank);
  // Register our task variants
  {
    TaskVariantRegistrar top_level_registrar(TOP_LEVEL_TASK_ID);
    top_level_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<top_level_task>(top_level_registrar,
                                                      "Top Level Task");
    Runtime::set_top_level_task_id(TOP_LEVEL_TASK_ID);
  }
  {
    TaskVariantRegistrar mpi_interop_registrar(MPI_INTEROP_TASK_ID);
    mpi_interop_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<mpi_interop_task>(mpi_interop_registrar,
                                                        "MPI Interop Task");
  }
  {
    TaskVariantRegistrar worker_task_registrar(WORKER_TASK_ID);
    worker_task_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<worker_task>(worker_task_registrar,
                                                   "Worker Task");
  }
  // Create a handshake for passing control between Legion and MPI
  // Indicate that MPI has initial control and that there is one
  // participant on each side
  handshake = Runtime::create_handshake(true/*MPI initial control*/,
                                        1/*MPI participants*/,
                                        1/*Legion participants*/);
  // Start the Legion runtime in background mode
  // This call will return immediately
  Runtime::start(argc, argv, true/*background*/);
  // Run your MPI program like normal
  // If you want strict bulk-synchronous execution include
  // the barriers protected by this variable, otherwise
  // you can elide them, they are not required for correctness
  const bool strict_bulk_synchronous_execution = true;
  for (int i = 0; i < total_iterations; i++)
  {
    printf("MPI Doing Work on rank %d\n", rank);
    if (strict_bulk_synchronous_execution)
      MPI_Barrier(MPI_COMM_WORLD);
    // Perform a handoff to Legion, this call is
    // asynchronous and will return immediately
    handshake.mpi_handoff_to_legion();
    // You can put additional work in here if you like
    // but it may interfere with Legion work

    // Wait for Legion to hand control back,
    // This call will block until a Legion task
    // running in this same process hands control back
    handshake.mpi_wait_on_legion();
    if (strict_bulk_synchronous_execution)
      MPI_Barrier(MPI_COMM_WORLD);
  }
  // When you're done wait for the Legion runtime to shutdown
  Runtime::wait_for_shutdown();
#ifndef GASNET_CONDUIT_MPI
  // Then finalize MPI like normal
  // Exception for the MPI conduit which does its own finalization
  MPI_Finalize();
#endif

  return 0;
}
<commit_msg>examples: more tweaks to mpi interop example<commit_after>/* Copyright 2016 Stanford University, NVIDIA Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

////////////////////////////////////////////////////////////
// THIS EXAMPLE MUST BE BUILT WITH A VERSION
// OF GASNET CONFIGURED WITH MPI COMPATIBILITY
//
// NOTE THAT GASNET ONLY SUPPORTS MPI-COMPATIBILITY
// ON SOME CONDUITS. CURRENTLY THESE ARE IBV, GEMINI,
// ARIES, MXM, and OFI. IF YOU WOULD LIKE ADDITIONAL
// CONDUITS SUPPORTED PLEASE CONTACT THE MAINTAINERS
// OF GASNET.
//
// Note: there is a way to use this example with the
// MPI conduit, but you have to have a version of
// MPI that supports MPI_THREAD_MULTIPLE. See the
// macro GASNET_CONDUIT_MPI below.
////////////////////////////////////////////////////////////

#include <cstdio>
// Need MPI header file
#include <mpi.h>

#include "legion.h"

using namespace Legion;

enum TaskID
{
  TOP_LEVEL_TASK_ID,
  MPI_INTEROP_TASK_ID,
  WORKER_TASK_ID,
};

// Here is our global MPI-Legion handshake
// You can have as many of these as you
// want but the common case is just to
// have one per Legion-MPI rank pair
MPILegionHandshake handshake;

// Have a global static number of iterations for
// this example, but you can easily configure it
// from command line arguments which get passed
// to both MPI and Legion
const int total_iterations = 10;

void worker_task(const Task *task,
                 const std::vector<PhysicalRegion> &regions,
                 Context ctx, Runtime *runtime)
{
  printf("Legion Doing Work in Rank %lld\n",
          task->parent_task->index_point[0]);
}

void mpi_interop_task(const Task *task,
                      const std::vector<PhysicalRegion> &regions,
                      Context ctx, Runtime *runtime)
{
  printf("Hello from Legion MPI-Interop Task %lld\n", task->index_point[0]);
  for (int i = 0; i < total_iterations; i++)
  {
    // Legion can interop with MPI in blocking and non-blocking
    // ways. You can use the calls to 'legion_wait_on_mpi' and
    // 'legion_handoff_to_mpi' in the same way as the MPI thread
    // does. Alternatively, you can get a phase barrier associated
    // with a LegionMPIHandshake object which will allow you to
    // continue launching more sub-tasks without blocking.
    // For deferred execution we prefer the later style, but
    // both will work correctly.
    if (i < (total_iterations/2))
    {
      // This is the blocking way of using handshakes, it
      // is not the ideal way, but it works correctly
      // Wait for MPI to give us control to run our worker
      // This is a blocking call
      handshake.legion_wait_on_mpi();
      // Launch our worker task
      TaskLauncher worker_launcher(WORKER_TASK_ID, TaskArgument(NULL,0));
      Future f = runtime->execute_task(ctx, worker_launcher);
      // Have to wait for the result before signaling MPI
      f.get_void_result();
      // Perform a non-blocking call to signal
      // MPI that we are giving it control back
      handshake.legion_handoff_to_mpi();
    }
    else
    {
      // This is the preferred way of using handshakes in Legion
      TaskLauncher worker_launcher(WORKER_TASK_ID, TaskArgument(NULL,0));
      // We can user our handshake as a phase barrier
      // Record that we will wait on this handshake
      worker_launcher.add_wait_handshake(handshake);
      // Advance the handshake to the next version
      handshake.advance_legion_handshake();
      // Then record that we will arrive on this versions
      worker_launcher.add_arrival_handshake(handshake);
      // Launch our worker task
      // No need to wait for anything
      runtime->execute_task(ctx, worker_launcher);
    }
  }
}

void top_level_task(const Task *task,
                    const std::vector<PhysicalRegion> &regions,
                    Context ctx, Runtime *runtime)
{
  printf("Hello from Legion Top-Level Task\n");
  // Both the application and Legion mappers have access to
  // the mappings between MPI Ranks and Legion address spaces
  // The reverse mapping goes the other way
  const std::map<int,AddressSpace> &forward_mapping =
    runtime->find_forward_MPI_mapping();
  for (std::map<int,AddressSpace>::const_iterator it =
        forward_mapping.begin(); it != forward_mapping.end(); it++)
    printf("MPI Rank %d maps to Legion Address Space %d\n",
            it->first, it->second);

  int rank = -1, size = -1;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  // Do a must epoch launch to align with the number of MPI ranks
  MustEpochLauncher must_epoch_launcher;
  Rect<1> launch_bounds(Point<1>(0),Point<1>(size - 1));
  Domain launch_domain = Domain::from_rect<1>(launch_bounds);
  ArgumentMap args_map;
  IndexLauncher index_launcher(MPI_INTEROP_TASK_ID, launch_domain,
                               TaskArgument(NULL, 0), args_map);
  must_epoch_launcher.add_index_task(index_launcher);
  runtime->execute_must_epoch(ctx, must_epoch_launcher);
}

int main(int argc, char **argv)
{
#ifdef GASNET_CONDUIT_MPI
  // The GASNet MPI conduit requires special start-up
  // in order to handle MPI calls from multiple threads
  int provided;
  MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided);
  // If you fail this assertion, then your version of MPI
  // does not support calls from multiple threads and you
  // cannot use the GASNet MPI conduit
  if (provided < MPI_THREAD_MULTIPLE)
    printf("ERROR: Your implementation of MPI does not support "
           "MPI_THREAD_MULTIPLE which is required for use of the "
           "GASNet MPI conduit with the Legion-MPI Interop!\n");
  assert(provided == MPI_THREAD_MULTIPLE);
#else
  // Perform MPI start-up like normal for most GASNet conduits
  MPI_Init(&argc, &argv);
#endif

  int rank = -1, size = -1;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  printf("Hello from MPI process %d of %d\n", rank, size);

  // Configure the Legion runtime with the rank of this process
  Runtime::configure_MPI_interoperability(rank);
  // Register our task variants
  {
    TaskVariantRegistrar top_level_registrar(TOP_LEVEL_TASK_ID);
    top_level_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<top_level_task>(top_level_registrar,
                                                      "Top Level Task");
    Runtime::set_top_level_task_id(TOP_LEVEL_TASK_ID);
  }
  {
    TaskVariantRegistrar mpi_interop_registrar(MPI_INTEROP_TASK_ID);
    mpi_interop_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<mpi_interop_task>(mpi_interop_registrar,
                                                        "MPI Interop Task");
  }
  {
    TaskVariantRegistrar worker_task_registrar(WORKER_TASK_ID);
    worker_task_registrar.add_constraint(ProcessorConstraint(Processor::LOC_PROC));
    Runtime::preregister_task_variant<worker_task>(worker_task_registrar,
                                                   "Worker Task");
  }
  // Create a handshake for passing control between Legion and MPI
  // Indicate that MPI has initial control and that there is one
  // participant on each side
  handshake = Runtime::create_handshake(true/*MPI initial control*/,
                                        1/*MPI participants*/,
                                        1/*Legion participants*/);
  // Start the Legion runtime in background mode
  // This call will return immediately
  Runtime::start(argc, argv, true/*background*/);
  // Run your MPI program like normal
  // If you want strict bulk-synchronous execution include
  // the barriers protected by this variable, otherwise
  // you can elide them, they are not required for correctness
  const bool strict_bulk_synchronous_execution = true;
  for (int i = 0; i < total_iterations; i++)
  {
    printf("MPI Doing Work on rank %d\n", rank);
    if (strict_bulk_synchronous_execution)
      MPI_Barrier(MPI_COMM_WORLD);
    // Perform a handoff to Legion, this call is
    // asynchronous and will return immediately
    handshake.mpi_handoff_to_legion();
    // You can put additional work in here if you like
    // but it may interfere with Legion work

    // Wait for Legion to hand control back,
    // This call will block until a Legion task
    // running in this same process hands control back
    handshake.mpi_wait_on_legion();
    if (strict_bulk_synchronous_execution)
      MPI_Barrier(MPI_COMM_WORLD);
  }
  // When you're done wait for the Legion runtime to shutdown
  Runtime::wait_for_shutdown();
#ifndef GASNET_CONDUIT_MPI
  // Then finalize MPI like normal
  // Exception for the MPI conduit which does its own finalization
  MPI_Finalize();
#endif

  return 0;
}
<|endoftext|>


<commit_before>//
// Copyright 2012 Francisco Jerez
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//

#include "core/compiler.hpp"

#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/TextDiagnosticBuffer.h>
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <clang/CodeGen/CodeGenAction.h>
#include <clang/Basic/TargetInfo.h>
#include <llvm/Bitcode/BitstreamWriter.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Linker.h>
#if HAVE_LLVM < 0x0303
#include <llvm/DerivedTypes.h>
#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#else
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/IRReader/IRReader.h>
#endif
#include <llvm/PassManager.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/MemoryBuffer.h>
#if HAVE_LLVM < 0x0303
#include <llvm/Support/PathV1.h>
#endif
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>

#if HAVE_LLVM < 0x0302
#include <llvm/Target/TargetData.h>
#elif HAVE_LLVM < 0x0303
#include <llvm/DataLayout.h>
#else
#include <llvm/IR/DataLayout.h>
#endif

#include "pipe/p_state.h"
#include "util/u_memory.h"

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstdio>
#include <sstream>

using namespace clover;

namespace {
#if 0
   void
   build_binary(const std::string &source, const std::string &target,
                const std::string &name) {
      clang::CompilerInstance c;
      clang::EmitObjAction act(&llvm::getGlobalContext());
      std::string log;
      llvm::raw_string_ostream s_log(log);

      LLVMInitializeTGSITarget();
      LLVMInitializeTGSITargetInfo();
      LLVMInitializeTGSITargetMC();
      LLVMInitializeTGSIAsmPrinter();

      c.getFrontendOpts().Inputs.push_back(
         std::make_pair(clang::IK_OpenCL, name));
      c.getHeaderSearchOpts().UseBuiltinIncludes = false;
      c.getHeaderSearchOpts().UseStandardIncludes = false;
      c.getLangOpts().NoBuiltin = true;
      c.getTargetOpts().Triple = target;
      c.getInvocation().setLangDefaults(clang::IK_OpenCL);
      c.createDiagnostics(0, NULL, new clang::TextDiagnosticPrinter(
                             s_log, c.getDiagnosticOpts()));

      c.getPreprocessorOpts().addRemappedFile(
         name, llvm::MemoryBuffer::getMemBuffer(source));

      if (!c.ExecuteAction(act))
         throw build_error(log);
   }

   module
   load_binary(const char *name) {
      std::ifstream fs((name));
      std::vector<unsigned char> str((std::istreambuf_iterator<char>(fs)),
                                     (std::istreambuf_iterator<char>()));
      compat::istream cs(str);
      return module::deserialize(cs);
   }
#endif

   llvm::Module *
   compile(const std::string &source, const std::string &name,
           const std::string &triple, const std::string &processor,
           const std::string &opts, clang::LangAS::Map& address_spaces) {

      clang::CompilerInstance c;
      clang::CompilerInvocation invocation;
      clang::EmitLLVMOnlyAction act(&llvm::getGlobalContext());
      std::string log;
      llvm::raw_string_ostream s_log(log);

      // Parse the compiler options:
      std::vector<std::string> opts_array;
      std::istringstream ss(opts);

      while (!ss.eof()) {
         std::string opt;
         getline(ss, opt, ' ');
         opts_array.push_back(opt);
      }

      opts_array.push_back(name);

      std::vector<const char *> opts_carray;
      for (unsigned i = 0; i < opts_array.size(); i++) {
         opts_carray.push_back(opts_array.at(i).c_str());
      }

      llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID;
      llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts;
      clang::TextDiagnosticBuffer *DiagsBuffer;

      DiagID = new clang::DiagnosticIDs();
      DiagOpts = new clang::DiagnosticOptions();
      DiagsBuffer = new clang::TextDiagnosticBuffer();

      clang::DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
      bool Success;

      Success = clang::CompilerInvocation::CreateFromArgs(c.getInvocation(),
                                        opts_carray.data(),
                                        opts_carray.data() + opts_carray.size(),
                                        Diags);
      if (!Success) {
         throw invalid_option_error();
      }
      c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;
      c.getHeaderSearchOpts().UseBuiltinIncludes = true;
      c.getHeaderSearchOpts().UseStandardSystemIncludes = true;
      c.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR;

      // Add libclc generic search path
      c.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR,
                                      clang::frontend::Angled,
                                      false, false
#if HAVE_LLVM < 0x0303
                                      , false
#endif
                                      );

      // Add libclc include
      c.getPreprocessorOpts().Includes.push_back("clc/clc.h");

      // clc.h requires that this macro be defined:
      c.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers");
      c.getPreprocessorOpts().addMacroDef("cl_khr_fp64");

      c.getLangOpts().NoBuiltin = true;
      c.getTargetOpts().Triple = triple;
      c.getTargetOpts().CPU = processor;
#if HAVE_LLVM <= 0x0301
      c.getInvocation().setLangDefaults(clang::IK_OpenCL);
#else
      c.getInvocation().setLangDefaults(c.getLangOpts(), clang::IK_OpenCL,
                                        clang::LangStandard::lang_opencl11);
#endif
      c.createDiagnostics(
#if HAVE_LLVM < 0x0303
                          0, NULL,
#endif
                          new clang::TextDiagnosticPrinter(
                                 s_log,
#if HAVE_LLVM <= 0x0301
                                 c.getDiagnosticOpts()));
#else
                                 &c.getDiagnosticOpts()));
#endif

      c.getPreprocessorOpts().addRemappedFile(name,
                                      llvm::MemoryBuffer::getMemBuffer(source));

      // Compile the code
      if (!c.ExecuteAction(act))
         throw build_error(log);

      // Get address spaces map to be able to find kernel argument address space
      memcpy(address_spaces, c.getTarget().getAddressSpaceMap(),
                                                        sizeof(address_spaces));

      return act.takeModule();
   }

   void
   find_kernels(llvm::Module *mod, std::vector<llvm::Function *> &kernels) {
      const llvm::NamedMDNode *kernel_node =
                                 mod->getNamedMetadata("opencl.kernels");
      // This means there are no kernels in the program.  The spec does not
      // require that we return an error here, but there will be an error if
      // the user tries to pass this program to a clCreateKernel() call.
      if (!kernel_node) {
         return;
      }

      for (unsigned i = 0; i < kernel_node->getNumOperands(); ++i) {
         kernels.push_back(llvm::dyn_cast<llvm::Function>(
                                    kernel_node->getOperand(i)->getOperand(0)));
      }
   }

   void
   link(llvm::Module *mod, const std::string &triple,
        const std::string &processor,
        const std::vector<llvm::Function *> &kernels) {

      llvm::PassManager PM;
      llvm::PassManagerBuilder Builder;
      std::string libclc_path = LIBCLC_LIBEXECDIR + processor + "-"
                                                  + triple + ".bc";
      // Link the kernel with libclc
#if HAVE_LLVM < 0x0303
      bool isNative;
      llvm::Linker linker("clover", mod);
      linker.LinkInFile(llvm::sys::Path(libclc_path), isNative);
      mod = linker.releaseModule();
#else
      std::string err_str;
      llvm::SMDiagnostic err;
      llvm::Module *libclc_mod = llvm::ParseIRFile(libclc_path, err,
                                                   mod->getContext());
      if (llvm::Linker::LinkModules(mod, libclc_mod,
                                    llvm::Linker::DestroySource,
                                    &err_str)) {
         throw build_error(err_str);
      }
#endif

      // Add a function internalizer pass.
      //
      // By default, the function internalizer pass will look for a function
      // called "main" and then mark all other functions as internal.  Marking
      // functions as internal enables the optimizer to perform optimizations
      // like function inlining and global dead-code elimination.
      //
      // When there is no "main" function in a module, the internalize pass will
      // treat the module like a library, and it won't internalize any functions.
      // Since there is no "main" function in our kernels, we need to tell
      // the internalizer pass that this module is not a library by passing a
      // list of kernel functions to the internalizer.  The internalizer will
      // treat the functions in the list as "main" functions and internalize
      // all of the other functions.
      std::vector<const char*> export_list;
      for (std::vector<llvm::Function *>::const_iterator I = kernels.begin(),
                                                         E = kernels.end();
                                                         I != E; ++I) {
         llvm::Function *kernel = *I;
         export_list.push_back(kernel->getName().data());
      }
      PM.add(llvm::createInternalizePass(export_list));

      // Run link time optimizations
      Builder.OptLevel = 2;
      Builder.populateLTOPassManager(PM, false, true);
      PM.run(*mod);
   }

   module
   build_module_llvm(llvm::Module *mod,
                     const std::vector<llvm::Function *> &kernels,
                     clang::LangAS::Map& address_spaces) {

      module m;
      struct pipe_llvm_program_header header;

      llvm::SmallVector<char, 1024> llvm_bitcode;
      llvm::raw_svector_ostream bitcode_ostream(llvm_bitcode);
      llvm::BitstreamWriter writer(llvm_bitcode);
      llvm::WriteBitcodeToFile(mod, bitcode_ostream);
      bitcode_ostream.flush();

      for (unsigned i = 0; i < kernels.size(); ++i) {
         llvm::Function *kernel_func;
         std::string kernel_name;
         compat::vector<module::argument> args;

         kernel_func = kernels[i];
         kernel_name = kernel_func->getName();

         for (llvm::Function::arg_iterator I = kernel_func->arg_begin(),
                                      E = kernel_func->arg_end(); I != E; ++I) {
            llvm::Argument &arg = *I;
#if HAVE_LLVM < 0x0302
            llvm::TargetData TD(kernel_func->getParent());
#else
            llvm::DataLayout TD(kernel_func->getParent()->getDataLayout());
#endif

            llvm::Type *arg_type = arg.getType();
            unsigned arg_size = TD.getTypeStoreSize(arg_type);

            llvm::Type *target_type = arg_type->isIntegerTy() ?
               TD.getSmallestLegalIntType(mod->getContext(), arg_size * 8) :
               arg_type;
            unsigned target_size = TD.getTypeStoreSize(target_type);
            unsigned target_align = TD.getABITypeAlignment(target_type);

            if (llvm::isa<llvm::PointerType>(arg_type) && arg.hasByValAttr()) {
               arg_type =
                  llvm::dyn_cast<llvm::PointerType>(arg_type)->getElementType();
            }

            if (arg_type->isPointerTy()) {
               unsigned address_space = llvm::cast<llvm::PointerType>(arg_type)->getAddressSpace();
               if (address_space == address_spaces[clang::LangAS::opencl_local
                                                     - clang::LangAS::Offset]) {
                  args.push_back(module::argument(module::argument::local,
                                                  arg_size, target_size,
                                                  target_align,
                                                  module::argument::zero_ext));
               } else {
                  // XXX: Correctly handle constant address space.  There is no
                  // way for r600g to pass a handle for constant buffers back
                  // to clover like it can for global buffers, so
                  // creating constant arguements will break r600g.  For now,
                  // continue treating constant buffers as global buffers
                  // until we can come up with a way to create handles for
                  // constant buffers.
                  args.push_back(module::argument(module::argument::global,
                                                  arg_size, target_size,
                                                  target_align,
                                                  module::argument::zero_ext));
              }

            } else {
               llvm::AttributeSet attrs = kernel_func->getAttributes();
               enum module::argument::ext_type ext_type =
                  (attrs.hasAttribute(arg.getArgNo() + 1,
                                     llvm::Attribute::SExt) ?
                   module::argument::sign_ext :
                   module::argument::zero_ext);

               args.push_back(
                  module::argument(module::argument::scalar, arg_size,
                                   target_size, target_align, ext_type));
            }
         }

         m.syms.push_back(module::symbol(kernel_name, 0, i, args ));
      }

      header.num_bytes = llvm_bitcode.size();
      std::string data;
      data.insert(0, (char*)(&header), sizeof(header));
      data.insert(data.end(), llvm_bitcode.begin(),
                                  llvm_bitcode.end());
      m.secs.push_back(module::section(0, module::section::text,
                                       header.num_bytes, data));

      return m;
   }
} // End anonymous namespace

module
clover::compile_program_llvm(const compat::string &source,
                             enum pipe_shader_ir ir,
                             const compat::string &target,
                             const compat::string &opts) {

   std::vector<llvm::Function *> kernels;
   size_t processor_str_len = std::string(target.begin()).find_first_of("-");
   std::string processor(target.begin(), 0, processor_str_len);
   std::string triple(target.begin(), processor_str_len + 1,
                      target.size() - processor_str_len - 1);
   clang::LangAS::Map address_spaces;

   // The input file name must have the .cl extension in order for the
   // CompilerInvocation class to recognize it as an OpenCL source file.
   llvm::Module *mod = compile(source, "input.cl", triple, processor, opts,
                                                                address_spaces);

   find_kernels(mod, kernels);

   link(mod, triple, processor, kernels);

   // Build the clover::module
   switch (ir) {
      case PIPE_SHADER_IR_TGSI:
         //XXX: Handle TGSI
         assert(0);
         return module();
      default:
         return build_module_llvm(mod, kernels, address_spaces);
   }
}
<commit_msg>clover: fix building with llvm-3.4 since rev191922<commit_after>//
// Copyright 2012 Francisco Jerez
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//

#include "core/compiler.hpp"

#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/TextDiagnosticBuffer.h>
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <clang/CodeGen/CodeGenAction.h>
#include <clang/Basic/TargetInfo.h>
#include <llvm/Bitcode/BitstreamWriter.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Linker.h>
#if HAVE_LLVM < 0x0303
#include <llvm/DerivedTypes.h>
#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#else
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/IRReader/IRReader.h>
#endif
#include <llvm/PassManager.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/MemoryBuffer.h>
#if HAVE_LLVM < 0x0303
#include <llvm/Support/PathV1.h>
#endif
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>

#if HAVE_LLVM < 0x0302
#include <llvm/Target/TargetData.h>
#elif HAVE_LLVM < 0x0303
#include <llvm/DataLayout.h>
#else
#include <llvm/IR/DataLayout.h>
#endif

#include "pipe/p_state.h"
#include "util/u_memory.h"

#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstdio>
#include <sstream>

using namespace clover;

namespace {
#if 0
   void
   build_binary(const std::string &source, const std::string &target,
                const std::string &name) {
      clang::CompilerInstance c;
      clang::EmitObjAction act(&llvm::getGlobalContext());
      std::string log;
      llvm::raw_string_ostream s_log(log);

      LLVMInitializeTGSITarget();
      LLVMInitializeTGSITargetInfo();
      LLVMInitializeTGSITargetMC();
      LLVMInitializeTGSIAsmPrinter();

      c.getFrontendOpts().Inputs.push_back(
         std::make_pair(clang::IK_OpenCL, name));
      c.getHeaderSearchOpts().UseBuiltinIncludes = false;
      c.getHeaderSearchOpts().UseStandardIncludes = false;
      c.getLangOpts().NoBuiltin = true;
      c.getTargetOpts().Triple = target;
      c.getInvocation().setLangDefaults(clang::IK_OpenCL);
      c.createDiagnostics(0, NULL, new clang::TextDiagnosticPrinter(
                             s_log, c.getDiagnosticOpts()));

      c.getPreprocessorOpts().addRemappedFile(
         name, llvm::MemoryBuffer::getMemBuffer(source));

      if (!c.ExecuteAction(act))
         throw build_error(log);
   }

   module
   load_binary(const char *name) {
      std::ifstream fs((name));
      std::vector<unsigned char> str((std::istreambuf_iterator<char>(fs)),
                                     (std::istreambuf_iterator<char>()));
      compat::istream cs(str);
      return module::deserialize(cs);
   }
#endif

   llvm::Module *
   compile(const std::string &source, const std::string &name,
           const std::string &triple, const std::string &processor,
           const std::string &opts, clang::LangAS::Map& address_spaces) {

      clang::CompilerInstance c;
      clang::CompilerInvocation invocation;
      clang::EmitLLVMOnlyAction act(&llvm::getGlobalContext());
      std::string log;
      llvm::raw_string_ostream s_log(log);

      // Parse the compiler options:
      std::vector<std::string> opts_array;
      std::istringstream ss(opts);

      while (!ss.eof()) {
         std::string opt;
         getline(ss, opt, ' ');
         opts_array.push_back(opt);
      }

      opts_array.push_back(name);

      std::vector<const char *> opts_carray;
      for (unsigned i = 0; i < opts_array.size(); i++) {
         opts_carray.push_back(opts_array.at(i).c_str());
      }

      llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID;
      llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts;
      clang::TextDiagnosticBuffer *DiagsBuffer;

      DiagID = new clang::DiagnosticIDs();
      DiagOpts = new clang::DiagnosticOptions();
      DiagsBuffer = new clang::TextDiagnosticBuffer();

      clang::DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
      bool Success;

      Success = clang::CompilerInvocation::CreateFromArgs(c.getInvocation(),
                                        opts_carray.data(),
                                        opts_carray.data() + opts_carray.size(),
                                        Diags);
      if (!Success) {
         throw invalid_option_error();
      }
      c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;
      c.getHeaderSearchOpts().UseBuiltinIncludes = true;
      c.getHeaderSearchOpts().UseStandardSystemIncludes = true;
      c.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR;

      // Add libclc generic search path
      c.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR,
                                      clang::frontend::Angled,
                                      false, false
#if HAVE_LLVM < 0x0303
                                      , false
#endif
                                      );

      // Add libclc include
      c.getPreprocessorOpts().Includes.push_back("clc/clc.h");

      // clc.h requires that this macro be defined:
      c.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers");
      c.getPreprocessorOpts().addMacroDef("cl_khr_fp64");

      c.getLangOpts().NoBuiltin = true;
      c.getTargetOpts().Triple = triple;
      c.getTargetOpts().CPU = processor;
#if HAVE_LLVM <= 0x0301
      c.getInvocation().setLangDefaults(clang::IK_OpenCL);
#else
      c.getInvocation().setLangDefaults(c.getLangOpts(), clang::IK_OpenCL,
                                        clang::LangStandard::lang_opencl11);
#endif
      c.createDiagnostics(
#if HAVE_LLVM < 0x0303
                          0, NULL,
#endif
                          new clang::TextDiagnosticPrinter(
                                 s_log,
#if HAVE_LLVM <= 0x0301
                                 c.getDiagnosticOpts()));
#else
                                 &c.getDiagnosticOpts()));
#endif

      c.getPreprocessorOpts().addRemappedFile(name,
                                      llvm::MemoryBuffer::getMemBuffer(source));

      // Compile the code
      if (!c.ExecuteAction(act))
         throw build_error(log);

      // Get address spaces map to be able to find kernel argument address space
      memcpy(address_spaces, c.getTarget().getAddressSpaceMap(),
                                                        sizeof(address_spaces));

      return act.takeModule();
   }

   void
   find_kernels(llvm::Module *mod, std::vector<llvm::Function *> &kernels) {
      const llvm::NamedMDNode *kernel_node =
                                 mod->getNamedMetadata("opencl.kernels");
      // This means there are no kernels in the program.  The spec does not
      // require that we return an error here, but there will be an error if
      // the user tries to pass this program to a clCreateKernel() call.
      if (!kernel_node) {
         return;
      }

      for (unsigned i = 0; i < kernel_node->getNumOperands(); ++i) {
         kernels.push_back(llvm::dyn_cast<llvm::Function>(
                                    kernel_node->getOperand(i)->getOperand(0)));
      }
   }

   void
   link(llvm::Module *mod, const std::string &triple,
        const std::string &processor,
        const std::vector<llvm::Function *> &kernels) {

      llvm::PassManager PM;
      llvm::PassManagerBuilder Builder;
      std::string libclc_path = LIBCLC_LIBEXECDIR + processor + "-"
                                                  + triple + ".bc";
      // Link the kernel with libclc
#if HAVE_LLVM < 0x0303
      bool isNative;
      llvm::Linker linker("clover", mod);
      linker.LinkInFile(llvm::sys::Path(libclc_path), isNative);
      mod = linker.releaseModule();
#else
      std::string err_str;
      llvm::SMDiagnostic err;
      llvm::Module *libclc_mod = llvm::ParseIRFile(libclc_path, err,
                                                   mod->getContext());
      if (llvm::Linker::LinkModules(mod, libclc_mod,
                                    llvm::Linker::DestroySource,
                                    &err_str)) {
         throw build_error(err_str);
      }
#endif

      // Add a function internalizer pass.
      //
      // By default, the function internalizer pass will look for a function
      // called "main" and then mark all other functions as internal.  Marking
      // functions as internal enables the optimizer to perform optimizations
      // like function inlining and global dead-code elimination.
      //
      // When there is no "main" function in a module, the internalize pass will
      // treat the module like a library, and it won't internalize any functions.
      // Since there is no "main" function in our kernels, we need to tell
      // the internalizer pass that this module is not a library by passing a
      // list of kernel functions to the internalizer.  The internalizer will
      // treat the functions in the list as "main" functions and internalize
      // all of the other functions.
      std::vector<const char*> export_list;
      for (std::vector<llvm::Function *>::const_iterator I = kernels.begin(),
                                                         E = kernels.end();
                                                         I != E; ++I) {
         llvm::Function *kernel = *I;
         export_list.push_back(kernel->getName().data());
      }
#if HAVE_LLVM < 0x0304
      PM.add(llvm::createInternalizePass(export_list));
#else
      std::vector<const char*> dso_list;
      PM.add(llvm::createInternalizePass(export_list, dso_list));
#endif
      // Run link time optimizations
      Builder.OptLevel = 2;
      Builder.populateLTOPassManager(PM, false, true);
      PM.run(*mod);
   }

   module
   build_module_llvm(llvm::Module *mod,
                     const std::vector<llvm::Function *> &kernels,
                     clang::LangAS::Map& address_spaces) {

      module m;
      struct pipe_llvm_program_header header;

      llvm::SmallVector<char, 1024> llvm_bitcode;
      llvm::raw_svector_ostream bitcode_ostream(llvm_bitcode);
      llvm::BitstreamWriter writer(llvm_bitcode);
      llvm::WriteBitcodeToFile(mod, bitcode_ostream);
      bitcode_ostream.flush();

      for (unsigned i = 0; i < kernels.size(); ++i) {
         llvm::Function *kernel_func;
         std::string kernel_name;
         compat::vector<module::argument> args;

         kernel_func = kernels[i];
         kernel_name = kernel_func->getName();

         for (llvm::Function::arg_iterator I = kernel_func->arg_begin(),
                                      E = kernel_func->arg_end(); I != E; ++I) {
            llvm::Argument &arg = *I;
#if HAVE_LLVM < 0x0302
            llvm::TargetData TD(kernel_func->getParent());
#else
            llvm::DataLayout TD(kernel_func->getParent()->getDataLayout());
#endif

            llvm::Type *arg_type = arg.getType();
            unsigned arg_size = TD.getTypeStoreSize(arg_type);

            llvm::Type *target_type = arg_type->isIntegerTy() ?
               TD.getSmallestLegalIntType(mod->getContext(), arg_size * 8) :
               arg_type;
            unsigned target_size = TD.getTypeStoreSize(target_type);
            unsigned target_align = TD.getABITypeAlignment(target_type);

            if (llvm::isa<llvm::PointerType>(arg_type) && arg.hasByValAttr()) {
               arg_type =
                  llvm::dyn_cast<llvm::PointerType>(arg_type)->getElementType();
            }

            if (arg_type->isPointerTy()) {
               unsigned address_space = llvm::cast<llvm::PointerType>(arg_type)->getAddressSpace();
               if (address_space == address_spaces[clang::LangAS::opencl_local
                                                     - clang::LangAS::Offset]) {
                  args.push_back(module::argument(module::argument::local,
                                                  arg_size, target_size,
                                                  target_align,
                                                  module::argument::zero_ext));
               } else {
                  // XXX: Correctly handle constant address space.  There is no
                  // way for r600g to pass a handle for constant buffers back
                  // to clover like it can for global buffers, so
                  // creating constant arguements will break r600g.  For now,
                  // continue treating constant buffers as global buffers
                  // until we can come up with a way to create handles for
                  // constant buffers.
                  args.push_back(module::argument(module::argument::global,
                                                  arg_size, target_size,
                                                  target_align,
                                                  module::argument::zero_ext));
              }

            } else {
               llvm::AttributeSet attrs = kernel_func->getAttributes();
               enum module::argument::ext_type ext_type =
                  (attrs.hasAttribute(arg.getArgNo() + 1,
                                     llvm::Attribute::SExt) ?
                   module::argument::sign_ext :
                   module::argument::zero_ext);

               args.push_back(
                  module::argument(module::argument::scalar, arg_size,
                                   target_size, target_align, ext_type));
            }
         }

         m.syms.push_back(module::symbol(kernel_name, 0, i, args ));
      }

      header.num_bytes = llvm_bitcode.size();
      std::string data;
      data.insert(0, (char*)(&header), sizeof(header));
      data.insert(data.end(), llvm_bitcode.begin(),
                                  llvm_bitcode.end());
      m.secs.push_back(module::section(0, module::section::text,
                                       header.num_bytes, data));

      return m;
   }
} // End anonymous namespace

module
clover::compile_program_llvm(const compat::string &source,
                             enum pipe_shader_ir ir,
                             const compat::string &target,
                             const compat::string &opts) {

   std::vector<llvm::Function *> kernels;
   size_t processor_str_len = std::string(target.begin()).find_first_of("-");
   std::string processor(target.begin(), 0, processor_str_len);
   std::string triple(target.begin(), processor_str_len + 1,
                      target.size() - processor_str_len - 1);
   clang::LangAS::Map address_spaces;

   // The input file name must have the .cl extension in order for the
   // CompilerInvocation class to recognize it as an OpenCL source file.
   llvm::Module *mod = compile(source, "input.cl", triple, processor, opts,
                                                                address_spaces);

   find_kernels(mod, kernels);

   link(mod, triple, processor, kernels);

   // Build the clover::module
   switch (ir) {
      case PIPE_SHADER_IR_TGSI:
         //XXX: Handle TGSI
         assert(0);
         return module();
      default:
         return build_module_llvm(mod, kernels, address_spaces);
   }
}
<|endoftext|>