<commit_before>//===-- StructRetPromotion.cpp - Promote sret arguments ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass finds functions that return a struct (using a pointer to the struct
// as the first argument of the function, marked with the 'sret' attribute) and
// replaces them with a new function that simply returns each of the elements of
// that struct (using multiple return values).
//
// This pass works under a number of conditions:
//  1. The returned struct must not contain other structs
//  2. The returned struct must only be used to load values from
//  3. The placeholder struct passed in is the result of an alloca
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sretpromotion"
#include "llvm/Transforms/IPO.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/CallGraphSCCPass.h"
#include "llvm/Instructions.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/CFG.h"
#include "llvm/Support/Debug.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

STATISTIC(NumRejectedSRETUses , "Number of sret rejected due to unexpected uses");
STATISTIC(NumSRET , "Number of sret promoted");
namespace {
  /// SRETPromotion - This pass removes sret parameter and updates
  /// function to use multiple return value.
  ///
  struct SRETPromotion : public CallGraphSCCPass {
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      CallGraphSCCPass::getAnalysisUsage(AU);
    }

    virtual bool runOnSCC(CallGraphSCC &SCC);
    static char ID; // Pass identification, replacement for typeid
    SRETPromotion() : CallGraphSCCPass(&ID) {}

  private:
    CallGraphNode *PromoteReturn(CallGraphNode *CGN);
    bool isSafeToUpdateAllCallers(Function *F);
    Function *cloneFunctionBody(Function *F, const StructType *STy);
    CallGraphNode *updateCallSites(Function *F, Function *NF);
    bool nestedStructType(const StructType *STy);
  };
}

char SRETPromotion::ID = 0;
static RegisterPass<SRETPromotion>
X("sretpromotion", "Promote sret arguments to multiple ret values");

Pass *llvm::createStructRetPromotionPass() {
  return new SRETPromotion();
}

bool SRETPromotion::runOnSCC(CallGraphSCC &SCC) {
  bool Changed = false;

  for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I)
    if (CallGraphNode *NewNode = PromoteReturn(*I)) {
      SCC.ReplaceNode(*I, NewNode);
      Changed = true;
    }

  return Changed;
}

/// PromoteReturn - This method promotes function that uses StructRet paramater
/// into a function that uses multiple return values.
CallGraphNode *SRETPromotion::PromoteReturn(CallGraphNode *CGN) {
  Function *F = CGN->getFunction();

  if (!F || F->isDeclaration() || !F->hasLocalLinkage())
    return 0;

  // Make sure that function returns struct.
  if (F->arg_size() == 0 || !F->hasStructRetAttr() || F->doesNotReturn())
    return 0;

  DEBUG(dbgs() << "SretPromotion: Looking at sret function "
        << F->getName() << "\n");

  assert(F->getReturnType()->isVoidTy() && "Invalid function return type");
  Function::arg_iterator AI = F->arg_begin();
  const llvm::PointerType *FArgType = dyn_cast<PointerType>(AI->getType());
  assert(FArgType && "Invalid sret parameter type");
  const llvm::StructType *STy =
    dyn_cast<StructType>(FArgType->getElementType());
  assert(STy && "Invalid sret parameter element type");

  // Check if it is ok to perform this promotion.
  if (isSafeToUpdateAllCallers(F) == false) {
    DEBUG(dbgs() << "SretPromotion: Not all callers can be updated\n");
    ++NumRejectedSRETUses;
    return 0;
  }

  DEBUG(dbgs() << "SretPromotion: sret argument will be promoted\n");
  ++NumSRET;
  // [1] Replace use of sret parameter
  AllocaInst *TheAlloca = new AllocaInst(STy, NULL, "mrv",
                                         F->getEntryBlock().begin());
  Value *NFirstArg = F->arg_begin();
  NFirstArg->replaceAllUsesWith(TheAlloca);

  // [2] Find and replace ret instructions
  for (Function::iterator FI = F->begin(), FE = F->end();  FI != FE; ++FI)
    for(BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ) {
      Instruction *I = BI;
      ++BI;
      if (isa<ReturnInst>(I)) {
        Value *NV = new LoadInst(TheAlloca, "mrv.ld", I);
        ReturnInst *NR = ReturnInst::Create(F->getContext(), NV, I);
        I->replaceAllUsesWith(NR);
        I->eraseFromParent();
      }
    }

  // [3] Create the new function body and insert it into the module.
  Function *NF = cloneFunctionBody(F, STy);

  // [4] Update all call sites to use new function
  CallGraphNode *NF_CFN = updateCallSites(F, NF);

  CallGraph &CG = getAnalysis<CallGraph>();
  NF_CFN->stealCalledFunctionsFrom(CG[F]);

  delete CG.removeFunctionFromModule(F);
  return NF_CFN;
}

// Check if it is ok to perform this promotion.
bool SRETPromotion::isSafeToUpdateAllCallers(Function *F) {

  if (F->use_empty())
    // No users. OK to modify signature.
    return true;

  for (Value::use_iterator FnUseI = F->use_begin(), FnUseE = F->use_end();
       FnUseI != FnUseE; ++FnUseI) {
    // The function is passed in as an argument to (possibly) another function,
    // we can't change it!
    CallSite CS = CallSite::get(*FnUseI);
    Instruction *Call = CS.getInstruction();
    // The function is used by something else than a call or invoke instruction,
    // we can't change it!
    if (!Call || !CS.isCallee(FnUseI))
      return false;
    CallSite::arg_iterator AI = CS.arg_begin();
    Value *FirstArg = *AI;

    if (!isa<AllocaInst>(FirstArg))
      return false;

    // Check FirstArg's users.
    for (Value::use_iterator ArgI = FirstArg->use_begin(),
           ArgE = FirstArg->use_end(); ArgI != ArgE; ++ArgI) {

      // If FirstArg user is a CallInst that does not correspond to current
      // call site then this function F is not suitable for sret promotion.
      if (CallInst *CI = dyn_cast<CallInst>(ArgI)) {
        if (CI != Call)
          return false;
      }
      // If FirstArg user is a GEP whose all users are not LoadInst then
      // this function F is not suitable for sret promotion.
      else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(ArgI)) {
        // TODO : Use dom info and insert PHINodes to collect get results
        // from multiple call sites for this GEP.
        if (GEP->getParent() != Call->getParent())
          return false;
        for (Value::use_iterator GEPI = GEP->use_begin(), GEPE = GEP->use_end();
             GEPI != GEPE; ++GEPI)
          if (!isa<LoadInst>(GEPI))
            return false;
      }
      // Any other FirstArg users make this function unsuitable for sret
      // promotion.
      else
        return false;
    }
  }

  return true;
}

/// cloneFunctionBody - Create a new function based on F and
/// insert it into module. Remove first argument. Use STy as
/// the return type for new function.
Function *SRETPromotion::cloneFunctionBody(Function *F,
                                           const StructType *STy) {

  const FunctionType *FTy = F->getFunctionType();
  std::vector<const Type*> Params;

  // Attributes - Keep track of the parameter attributes for the arguments.
  SmallVector<AttributeWithIndex, 8> AttributesVec;
  const AttrListPtr &PAL = F->getAttributes();

  // Add any return attributes.
  if (Attributes attrs = PAL.getRetAttributes())
    AttributesVec.push_back(AttributeWithIndex::get(0, attrs));

  // Skip first argument.
  Function::arg_iterator I = F->arg_begin(), E = F->arg_end();
  ++I;
  // 0th parameter attribute is reserved for return type.
  // 1th parameter attribute is for first 1st sret argument.
  unsigned ParamIndex = 2;
  while (I != E) {
    Params.push_back(I->getType());
    if (Attributes Attrs = PAL.getParamAttributes(ParamIndex))
      AttributesVec.push_back(AttributeWithIndex::get(ParamIndex - 1, Attrs));
    ++I;
    ++ParamIndex;
  }

  // Add any fn attributes.
  if (Attributes attrs = PAL.getFnAttributes())
    AttributesVec.push_back(AttributeWithIndex::get(~0, attrs));

  FunctionType *NFTy = FunctionType::get(STy, Params, FTy->isVarArg());
  Function *NF = Function::Create(NFTy, F->getLinkage());
  NF->takeName(F);
  NF->copyAttributesFrom(F);
  NF->setAttributes(AttrListPtr::get(AttributesVec.begin(), AttributesVec.end()));
  F->getParent()->getFunctionList().insert(F, NF);
  NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());

  // Replace arguments
  I = F->arg_begin();
  E = F->arg_end();
  Function::arg_iterator NI = NF->arg_begin();
  ++I;
  while (I != E) {
    I->replaceAllUsesWith(NI);
    NI->takeName(I);
    ++I;
    ++NI;
  }

  return NF;
}

/// updateCallSites - Update all sites that call F to use NF.
CallGraphNode *SRETPromotion::updateCallSites(Function *F, Function *NF) {
  CallGraph &CG = getAnalysis<CallGraph>();
  SmallVector<Value*, 16> Args;

  // Attributes - Keep track of the parameter attributes for the arguments.
  SmallVector<AttributeWithIndex, 8> ArgAttrsVec;

  // Get a new callgraph node for NF.
  CallGraphNode *NF_CGN = CG.getOrInsertFunction(NF);

  while (!F->use_empty()) {
    CallSite CS = CallSite::get(*F->use_begin());
    Instruction *Call = CS.getInstruction();

    const AttrListPtr &PAL = F->getAttributes();
    // Add any return attributes.
    if (Attributes attrs = PAL.getRetAttributes())
      ArgAttrsVec.push_back(AttributeWithIndex::get(0, attrs));

    // Copy arguments, however skip first one.
    CallSite::arg_iterator AI = CS.arg_begin(), AE = CS.arg_end();
    Value *FirstCArg = *AI;
    ++AI;
    // 0th parameter attribute is reserved for return type.
    // 1th parameter attribute is for first 1st sret argument.
    unsigned ParamIndex = 2;
    while (AI != AE) {
      Args.push_back(*AI);
      if (Attributes Attrs = PAL.getParamAttributes(ParamIndex))
        ArgAttrsVec.push_back(AttributeWithIndex::get(ParamIndex - 1, Attrs));
      ++ParamIndex;
      ++AI;
    }

    // Add any function attributes.
    if (Attributes attrs = PAL.getFnAttributes())
      ArgAttrsVec.push_back(AttributeWithIndex::get(~0, attrs));

    AttrListPtr NewPAL = AttrListPtr::get(ArgAttrsVec.begin(), ArgAttrsVec.end());

    // Build new call instruction.
    Instruction *New;
    if (InvokeInst *II = dyn_cast<InvokeInst>(Call)) {
      New = InvokeInst::Create(NF, II->getNormalDest(), II->getUnwindDest(),
                               Args.begin(), Args.end(), "", Call);
      cast<InvokeInst>(New)->setCallingConv(CS.getCallingConv());
      cast<InvokeInst>(New)->setAttributes(NewPAL);
    } else {
      New = CallInst::Create(NF, Args.begin(), Args.end(), "", Call);
      cast<CallInst>(New)->setCallingConv(CS.getCallingConv());
      cast<CallInst>(New)->setAttributes(NewPAL);
      if (cast<CallInst>(Call)->isTailCall())
        cast<CallInst>(New)->setTailCall();
    }
    Args.clear();
    ArgAttrsVec.clear();
    New->takeName(Call);

    // Update the callgraph to know that the callsite has been transformed.
    CallGraphNode *CalleeNode = CG[Call->getParent()->getParent()];
    CalleeNode->removeCallEdgeFor(Call);
    CalleeNode->addCalledFunction(New, NF_CGN);

    // Update all users of sret parameter to extract value using extractvalue.
    for (Value::use_iterator UI = FirstCArg->use_begin(),
           UE = FirstCArg->use_end(); UI != UE; ) {
      User *U2 = *UI++;
      CallInst *C2 = dyn_cast<CallInst>(U2);
      if (C2 && (C2 == Call))
        continue;

      GetElementPtrInst *UGEP = cast<GetElementPtrInst>(U2);
      ConstantInt *Idx = cast<ConstantInt>(UGEP->getOperand(2));
      Value *GR = ExtractValueInst::Create(New, Idx->getZExtValue(),
                                           "evi", UGEP);
      while(!UGEP->use_empty()) {
        // isSafeToUpdateAllCallers has checked that all GEP uses are
        // LoadInsts
        LoadInst *L = cast<LoadInst>(*UGEP->use_begin());
        L->replaceAllUsesWith(GR);
        L->eraseFromParent();
      }
      UGEP->eraseFromParent();
      continue;
    }
    Call->eraseFromParent();
  }

  return NF_CGN;
}

/// nestedStructType - Return true if STy includes any
/// other aggregate types
bool SRETPromotion::nestedStructType(const StructType *STy) {
  unsigned Num = STy->getNumElements();
  for (unsigned i = 0; i < Num; i++) {
    const Type *Ty = STy->getElementType(i);
    if (!Ty->isSingleValueType() && !Ty->isVoidTy())
      return true;
  }
  return false;
}
<commit_msg>cache dereferenced iterators<commit_after>//===-- StructRetPromotion.cpp - Promote sret arguments ------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass finds functions that return a struct (using a pointer to the struct
// as the first argument of the function, marked with the 'sret' attribute) and
// replaces them with a new function that simply returns each of the elements of
// that struct (using multiple return values).
//
// This pass works under a number of conditions:
//  1. The returned struct must not contain other structs
//  2. The returned struct must only be used to load values from
//  3. The placeholder struct passed in is the result of an alloca
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sretpromotion"
#include "llvm/Transforms/IPO.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/CallGraphSCCPass.h"
#include "llvm/Instructions.h"
#include "llvm/Analysis/CallGraph.h"
#include "llvm/Support/CallSite.h"
#include "llvm/Support/CFG.h"
#include "llvm/Support/Debug.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

STATISTIC(NumRejectedSRETUses , "Number of sret rejected due to unexpected uses");
STATISTIC(NumSRET , "Number of sret promoted");
namespace {
  /// SRETPromotion - This pass removes sret parameter and updates
  /// function to use multiple return value.
  ///
  struct SRETPromotion : public CallGraphSCCPass {
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      CallGraphSCCPass::getAnalysisUsage(AU);
    }

    virtual bool runOnSCC(CallGraphSCC &SCC);
    static char ID; // Pass identification, replacement for typeid
    SRETPromotion() : CallGraphSCCPass(&ID) {}

  private:
    CallGraphNode *PromoteReturn(CallGraphNode *CGN);
    bool isSafeToUpdateAllCallers(Function *F);
    Function *cloneFunctionBody(Function *F, const StructType *STy);
    CallGraphNode *updateCallSites(Function *F, Function *NF);
    bool nestedStructType(const StructType *STy);
  };
}

char SRETPromotion::ID = 0;
static RegisterPass<SRETPromotion>
X("sretpromotion", "Promote sret arguments to multiple ret values");

Pass *llvm::createStructRetPromotionPass() {
  return new SRETPromotion();
}

bool SRETPromotion::runOnSCC(CallGraphSCC &SCC) {
  bool Changed = false;

  for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I)
    if (CallGraphNode *NewNode = PromoteReturn(*I)) {
      SCC.ReplaceNode(*I, NewNode);
      Changed = true;
    }

  return Changed;
}

/// PromoteReturn - This method promotes function that uses StructRet paramater
/// into a function that uses multiple return values.
CallGraphNode *SRETPromotion::PromoteReturn(CallGraphNode *CGN) {
  Function *F = CGN->getFunction();

  if (!F || F->isDeclaration() || !F->hasLocalLinkage())
    return 0;

  // Make sure that function returns struct.
  if (F->arg_size() == 0 || !F->hasStructRetAttr() || F->doesNotReturn())
    return 0;

  DEBUG(dbgs() << "SretPromotion: Looking at sret function "
        << F->getName() << "\n");

  assert(F->getReturnType()->isVoidTy() && "Invalid function return type");
  Function::arg_iterator AI = F->arg_begin();
  const llvm::PointerType *FArgType = dyn_cast<PointerType>(AI->getType());
  assert(FArgType && "Invalid sret parameter type");
  const llvm::StructType *STy =
    dyn_cast<StructType>(FArgType->getElementType());
  assert(STy && "Invalid sret parameter element type");

  // Check if it is ok to perform this promotion.
  if (isSafeToUpdateAllCallers(F) == false) {
    DEBUG(dbgs() << "SretPromotion: Not all callers can be updated\n");
    ++NumRejectedSRETUses;
    return 0;
  }

  DEBUG(dbgs() << "SretPromotion: sret argument will be promoted\n");
  ++NumSRET;
  // [1] Replace use of sret parameter
  AllocaInst *TheAlloca = new AllocaInst(STy, NULL, "mrv",
                                         F->getEntryBlock().begin());
  Value *NFirstArg = F->arg_begin();
  NFirstArg->replaceAllUsesWith(TheAlloca);

  // [2] Find and replace ret instructions
  for (Function::iterator FI = F->begin(), FE = F->end();  FI != FE; ++FI)
    for(BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; ) {
      Instruction *I = BI;
      ++BI;
      if (isa<ReturnInst>(I)) {
        Value *NV = new LoadInst(TheAlloca, "mrv.ld", I);
        ReturnInst *NR = ReturnInst::Create(F->getContext(), NV, I);
        I->replaceAllUsesWith(NR);
        I->eraseFromParent();
      }
    }

  // [3] Create the new function body and insert it into the module.
  Function *NF = cloneFunctionBody(F, STy);

  // [4] Update all call sites to use new function
  CallGraphNode *NF_CFN = updateCallSites(F, NF);

  CallGraph &CG = getAnalysis<CallGraph>();
  NF_CFN->stealCalledFunctionsFrom(CG[F]);

  delete CG.removeFunctionFromModule(F);
  return NF_CFN;
}

// Check if it is ok to perform this promotion.
bool SRETPromotion::isSafeToUpdateAllCallers(Function *F) {

  if (F->use_empty())
    // No users. OK to modify signature.
    return true;

  for (Value::use_iterator FnUseI = F->use_begin(), FnUseE = F->use_end();
       FnUseI != FnUseE; ++FnUseI) {
    // The function is passed in as an argument to (possibly) another function,
    // we can't change it!
    CallSite CS = CallSite::get(*FnUseI);
    Instruction *Call = CS.getInstruction();
    // The function is used by something else than a call or invoke instruction,
    // we can't change it!
    if (!Call || !CS.isCallee(FnUseI))
      return false;
    CallSite::arg_iterator AI = CS.arg_begin();
    Value *FirstArg = *AI;

    if (!isa<AllocaInst>(FirstArg))
      return false;

    // Check FirstArg's users.
    for (Value::use_iterator ArgI = FirstArg->use_begin(),
           ArgE = FirstArg->use_end(); ArgI != ArgE; ++ArgI) {
      User *U = *ArgI;
      // If FirstArg user is a CallInst that does not correspond to current
      // call site then this function F is not suitable for sret promotion.
      if (CallInst *CI = dyn_cast<CallInst>(U)) {
        if (CI != Call)
          return false;
      }
      // If FirstArg user is a GEP whose all users are not LoadInst then
      // this function F is not suitable for sret promotion.
      else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(U)) {
        // TODO : Use dom info and insert PHINodes to collect get results
        // from multiple call sites for this GEP.
        if (GEP->getParent() != Call->getParent())
          return false;
        for (Value::use_iterator GEPI = GEP->use_begin(), GEPE = GEP->use_end();
             GEPI != GEPE; ++GEPI)
          if (!isa<LoadInst>(GEPI))
            return false;
      }
      // Any other FirstArg users make this function unsuitable for sret
      // promotion.
      else
        return false;
    }
  }

  return true;
}

/// cloneFunctionBody - Create a new function based on F and
/// insert it into module. Remove first argument. Use STy as
/// the return type for new function.
Function *SRETPromotion::cloneFunctionBody(Function *F,
                                           const StructType *STy) {

  const FunctionType *FTy = F->getFunctionType();
  std::vector<const Type*> Params;

  // Attributes - Keep track of the parameter attributes for the arguments.
  SmallVector<AttributeWithIndex, 8> AttributesVec;
  const AttrListPtr &PAL = F->getAttributes();

  // Add any return attributes.
  if (Attributes attrs = PAL.getRetAttributes())
    AttributesVec.push_back(AttributeWithIndex::get(0, attrs));

  // Skip first argument.
  Function::arg_iterator I = F->arg_begin(), E = F->arg_end();
  ++I;
  // 0th parameter attribute is reserved for return type.
  // 1th parameter attribute is for first 1st sret argument.
  unsigned ParamIndex = 2;
  while (I != E) {
    Params.push_back(I->getType());
    if (Attributes Attrs = PAL.getParamAttributes(ParamIndex))
      AttributesVec.push_back(AttributeWithIndex::get(ParamIndex - 1, Attrs));
    ++I;
    ++ParamIndex;
  }

  // Add any fn attributes.
  if (Attributes attrs = PAL.getFnAttributes())
    AttributesVec.push_back(AttributeWithIndex::get(~0, attrs));

  FunctionType *NFTy = FunctionType::get(STy, Params, FTy->isVarArg());
  Function *NF = Function::Create(NFTy, F->getLinkage());
  NF->takeName(F);
  NF->copyAttributesFrom(F);
  NF->setAttributes(AttrListPtr::get(AttributesVec.begin(), AttributesVec.end()));
  F->getParent()->getFunctionList().insert(F, NF);
  NF->getBasicBlockList().splice(NF->begin(), F->getBasicBlockList());

  // Replace arguments
  I = F->arg_begin();
  E = F->arg_end();
  Function::arg_iterator NI = NF->arg_begin();
  ++I;
  while (I != E) {
    I->replaceAllUsesWith(NI);
    NI->takeName(I);
    ++I;
    ++NI;
  }

  return NF;
}

/// updateCallSites - Update all sites that call F to use NF.
CallGraphNode *SRETPromotion::updateCallSites(Function *F, Function *NF) {
  CallGraph &CG = getAnalysis<CallGraph>();
  SmallVector<Value*, 16> Args;

  // Attributes - Keep track of the parameter attributes for the arguments.
  SmallVector<AttributeWithIndex, 8> ArgAttrsVec;

  // Get a new callgraph node for NF.
  CallGraphNode *NF_CGN = CG.getOrInsertFunction(NF);

  while (!F->use_empty()) {
    CallSite CS = CallSite::get(*F->use_begin());
    Instruction *Call = CS.getInstruction();

    const AttrListPtr &PAL = F->getAttributes();
    // Add any return attributes.
    if (Attributes attrs = PAL.getRetAttributes())
      ArgAttrsVec.push_back(AttributeWithIndex::get(0, attrs));

    // Copy arguments, however skip first one.
    CallSite::arg_iterator AI = CS.arg_begin(), AE = CS.arg_end();
    Value *FirstCArg = *AI;
    ++AI;
    // 0th parameter attribute is reserved for return type.
    // 1th parameter attribute is for first 1st sret argument.
    unsigned ParamIndex = 2;
    while (AI != AE) {
      Args.push_back(*AI);
      if (Attributes Attrs = PAL.getParamAttributes(ParamIndex))
        ArgAttrsVec.push_back(AttributeWithIndex::get(ParamIndex - 1, Attrs));
      ++ParamIndex;
      ++AI;
    }

    // Add any function attributes.
    if (Attributes attrs = PAL.getFnAttributes())
      ArgAttrsVec.push_back(AttributeWithIndex::get(~0, attrs));

    AttrListPtr NewPAL = AttrListPtr::get(ArgAttrsVec.begin(), ArgAttrsVec.end());

    // Build new call instruction.
    Instruction *New;
    if (InvokeInst *II = dyn_cast<InvokeInst>(Call)) {
      New = InvokeInst::Create(NF, II->getNormalDest(), II->getUnwindDest(),
                               Args.begin(), Args.end(), "", Call);
      cast<InvokeInst>(New)->setCallingConv(CS.getCallingConv());
      cast<InvokeInst>(New)->setAttributes(NewPAL);
    } else {
      New = CallInst::Create(NF, Args.begin(), Args.end(), "", Call);
      cast<CallInst>(New)->setCallingConv(CS.getCallingConv());
      cast<CallInst>(New)->setAttributes(NewPAL);
      if (cast<CallInst>(Call)->isTailCall())
        cast<CallInst>(New)->setTailCall();
    }
    Args.clear();
    ArgAttrsVec.clear();
    New->takeName(Call);

    // Update the callgraph to know that the callsite has been transformed.
    CallGraphNode *CalleeNode = CG[Call->getParent()->getParent()];
    CalleeNode->removeCallEdgeFor(Call);
    CalleeNode->addCalledFunction(New, NF_CGN);

    // Update all users of sret parameter to extract value using extractvalue.
    for (Value::use_iterator UI = FirstCArg->use_begin(),
           UE = FirstCArg->use_end(); UI != UE; ) {
      User *U2 = *UI++;
      CallInst *C2 = dyn_cast<CallInst>(U2);
      if (C2 && (C2 == Call))
        continue;

      GetElementPtrInst *UGEP = cast<GetElementPtrInst>(U2);
      ConstantInt *Idx = cast<ConstantInt>(UGEP->getOperand(2));
      Value *GR = ExtractValueInst::Create(New, Idx->getZExtValue(),
                                           "evi", UGEP);
      while(!UGEP->use_empty()) {
        // isSafeToUpdateAllCallers has checked that all GEP uses are
        // LoadInsts
        LoadInst *L = cast<LoadInst>(*UGEP->use_begin());
        L->replaceAllUsesWith(GR);
        L->eraseFromParent();
      }
      UGEP->eraseFromParent();
      continue;
    }
    Call->eraseFromParent();
  }

  return NF_CGN;
}

/// nestedStructType - Return true if STy includes any
/// other aggregate types
bool SRETPromotion::nestedStructType(const StructType *STy) {
  unsigned Num = STy->getNumElements();
  for (unsigned i = 0; i < Num; i++) {
    const Type *Ty = STy->getElementType(i);
    if (!Ty->isSingleValueType() && !Ty->isVoidTy())
      return true;
  }
  return false;
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <util/system.h>

#include <string>
#include <utility>
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(getarg_tests, BasicTestingSetup)

static void ResetArgs(const std::string& strArg)
{
    std::vector<std::string> vecArg;
    if (strArg.size())
      boost::split(vecArg, strArg, IsSpace, boost::token_compress_on);

    // Insert dummy executable name:
    vecArg.insert(vecArg.begin(), "testbitcoin");

    // Convert to char*:
    std::vector<const char*> vecChar;
    for (const std::string& s : vecArg)
        vecChar.push_back(s.c_str());

    std::string error;
    BOOST_CHECK(gArgs.ParseParameters(vecChar.size(), vecChar.data(), error));
}

static void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
    gArgs.ClearArgs();
    for (const auto& arg : args) {
        gArgs.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
    }
}

BOOST_AUTO_TEST_CASE(boolarg)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    SetupArgs({foo});
    ResetArgs("-foo");
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));

    BOOST_CHECK(!gArgs.GetBoolArg("-fo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-fo", true));

    BOOST_CHECK(!gArgs.GetBoolArg("-fooo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-fooo", true));

    ResetArgs("-foo=0");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-foo=1");
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));

    // New 0.6 feature: auto-map -nosomething to !-something:
    ResetArgs("-nofoo");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-nofoo=1");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-foo -nofoo");  // -nofoo should win
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-foo=1 -nofoo=1");  // -nofoo should win
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-foo=0 -nofoo=0");  // -nofoo=0 should win
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));

    // New 0.6 feature: treat -- same as -:
    ResetArgs("--foo=1");
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));

    ResetArgs("--nofoo=1");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

}

BOOST_AUTO_TEST_CASE(stringarg)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
    SetupArgs({foo, bar});
    ResetArgs("");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");

    ResetArgs("-foo -bar");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "");

    ResetArgs("-foo=");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "");

    ResetArgs("-foo=11");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "11");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "11");

    ResetArgs("-foo=eleven");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "eleven");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");

}

BOOST_AUTO_TEST_CASE(intarg)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
    SetupArgs({foo, bar});
    ResetArgs("");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11);
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0);

    ResetArgs("-foo -bar");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 0);
    BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0);

    ResetArgs("-foo=11 -bar=12");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 11);
    BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 12);

    ResetArgs("-foo=NaN -bar=NotANumber");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 1), 0);
    BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0);
}

BOOST_AUTO_TEST_CASE(doubledash)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
    SetupArgs({foo, bar});
    ResetArgs("--foo");
    BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true);

    ResetArgs("--foo=verbose --bar=1");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "verbose");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 0), 1);
}

BOOST_AUTO_TEST_CASE(boolargno)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
    SetupArgs({foo, bar});
    ResetArgs("-nofoo");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));

    ResetArgs("-nofoo=1");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));

    ResetArgs("-nofoo=0");
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));

    ResetArgs("-foo --nofoo"); // --nofoo should win
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));

    ResetArgs("-nofoo -foo"); // foo always wins:
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
}

BOOST_AUTO_TEST_CASE(logargs)
{
    const auto okaylog_bool = std::make_pair("-okaylog-bool", ArgsManager::ALLOW_BOOL);
    const auto okaylog_negbool = std::make_pair("-okaylog-negbool", ArgsManager::ALLOW_BOOL);
    const auto okaylog = std::make_pair("-okaylog", ArgsManager::ALLOW_ANY);
    const auto dontlog = std::make_pair("-dontlog", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE);
    SetupArgs({okaylog_bool, okaylog_negbool, okaylog, dontlog});
    ResetArgs("-okaylog-bool -nookaylog-negbool -okaylog=public -dontlog=private");

    // Everything logged to debug.log will also append to str
    std::string str;
    auto print_connection = LogInstance().PushBackCallback(
        [&str](const std::string& s) {
            str += s;
        });

    // Log the arguments
    gArgs.LogArgs();

    LogInstance().DeleteCallback(print_connection);
    // Check that what should appear does, and what shouldn't doesn't.
    BOOST_CHECK(str.find("Command-line arg: okaylog-bool=\"\"") != std::string::npos);
    BOOST_CHECK(str.find("Command-line arg: okaylog-negbool=false") != std::string::npos);
    BOOST_CHECK(str.find("Command-line arg: okaylog=\"public\"") != std::string::npos);
    BOOST_CHECK(str.find("dontlog=****") != std::string::npos);
    BOOST_CHECK(str.find("private") == std::string::npos);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Create a local class inherited from BasicTestingSetup with a localized args manager and put it into the getarg_tests namespace<commit_after>// Copyright (c) 2012-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <util/system.h>

#include <string>
#include <utility>
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>

namespace getarg_tests{
    class LocalTestingSetup : BasicTestingSetup {
        protected:
        void SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args);
        void ResetArgs(const std::string& strArg);
        ArgsManager m_args;
    };
}

BOOST_FIXTURE_TEST_SUITE(getarg_tests, LocalTestingSetup)

void LocalTestingSetup :: ResetArgs(const std::string& strArg)
{
    std::vector<std::string> vecArg;
    if (strArg.size())
      boost::split(vecArg, strArg, IsSpace, boost::token_compress_on);

    // Insert dummy executable name:
    vecArg.insert(vecArg.begin(), "testbitcoin");

    // Convert to char*:
    std::vector<const char*> vecChar;
    for (const std::string& s : vecArg)
        vecChar.push_back(s.c_str());

    std::string error;
    BOOST_CHECK(gArgs.ParseParameters(vecChar.size(), vecChar.data(), error));
}

void LocalTestingSetup :: SetupArgs(const std::vector<std::pair<std::string, unsigned int>>& args)
{
    gArgs.ClearArgs();
    for (const auto& arg : args) {
        gArgs.AddArg(arg.first, "", arg.second, OptionsCategory::OPTIONS);
    }
}

BOOST_AUTO_TEST_CASE(boolarg)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    SetupArgs({foo});
    ResetArgs("-foo");
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));

    BOOST_CHECK(!gArgs.GetBoolArg("-fo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-fo", true));

    BOOST_CHECK(!gArgs.GetBoolArg("-fooo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-fooo", true));

    ResetArgs("-foo=0");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-foo=1");
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));

    // New 0.6 feature: auto-map -nosomething to !-something:
    ResetArgs("-nofoo");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-nofoo=1");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-foo -nofoo");  // -nofoo should win
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-foo=1 -nofoo=1");  // -nofoo should win
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

    ResetArgs("-foo=0 -nofoo=0");  // -nofoo=0 should win
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));

    // New 0.6 feature: treat -- same as -:
    ResetArgs("--foo=1");
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));

    ResetArgs("--nofoo=1");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));

}

BOOST_AUTO_TEST_CASE(stringarg)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
    SetupArgs({foo, bar});
    ResetArgs("");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");

    ResetArgs("-foo -bar");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "");

    ResetArgs("-foo=");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "");

    ResetArgs("-foo=11");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "11");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "11");

    ResetArgs("-foo=eleven");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "eleven");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven");

}

BOOST_AUTO_TEST_CASE(intarg)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
    SetupArgs({foo, bar});
    ResetArgs("");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11);
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0);

    ResetArgs("-foo -bar");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 0);
    BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0);

    ResetArgs("-foo=11 -bar=12");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 11);
    BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 12);

    ResetArgs("-foo=NaN -bar=NotANumber");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 1), 0);
    BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 11), 0);
}

BOOST_AUTO_TEST_CASE(doubledash)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
    SetupArgs({foo, bar});
    ResetArgs("--foo");
    BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true);

    ResetArgs("--foo=verbose --bar=1");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), "verbose");
    BOOST_CHECK_EQUAL(gArgs.GetArg("-bar", 0), 1);
}

BOOST_AUTO_TEST_CASE(boolargno)
{
    const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY);
    const auto bar = std::make_pair("-bar", ArgsManager::ALLOW_ANY);
    SetupArgs({foo, bar});
    ResetArgs("-nofoo");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));

    ResetArgs("-nofoo=1");
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));

    ResetArgs("-nofoo=0");
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));

    ResetArgs("-foo --nofoo"); // --nofoo should win
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(!gArgs.GetBoolArg("-foo", false));

    ResetArgs("-nofoo -foo"); // foo always wins:
    BOOST_CHECK(gArgs.GetBoolArg("-foo", true));
    BOOST_CHECK(gArgs.GetBoolArg("-foo", false));
}

BOOST_AUTO_TEST_CASE(logargs)
{
    const auto okaylog_bool = std::make_pair("-okaylog-bool", ArgsManager::ALLOW_BOOL);
    const auto okaylog_negbool = std::make_pair("-okaylog-negbool", ArgsManager::ALLOW_BOOL);
    const auto okaylog = std::make_pair("-okaylog", ArgsManager::ALLOW_ANY);
    const auto dontlog = std::make_pair("-dontlog", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE);
    SetupArgs({okaylog_bool, okaylog_negbool, okaylog, dontlog});
    ResetArgs("-okaylog-bool -nookaylog-negbool -okaylog=public -dontlog=private");

    // Everything logged to debug.log will also append to str
    std::string str;
    auto print_connection = LogInstance().PushBackCallback(
        [&str](const std::string& s) {
            str += s;
        });

    // Log the arguments
    gArgs.LogArgs();

    LogInstance().DeleteCallback(print_connection);
    // Check that what should appear does, and what shouldn't doesn't.
    BOOST_CHECK(str.find("Command-line arg: okaylog-bool=\"\"") != std::string::npos);
    BOOST_CHECK(str.find("Command-line arg: okaylog-negbool=false") != std::string::npos);
    BOOST_CHECK(str.find("Command-line arg: okaylog=\"public\"") != std::string::npos);
    BOOST_CHECK(str.find("dontlog=****") != std::string::npos);
    BOOST_CHECK(str.find("private") == std::string::npos);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>//===- ChangeAllocations.cpp - Modify %malloc & %free calls -----------------=//
//
// This file defines two passes that convert malloc and free instructions to
// calls to and from %malloc & %free function calls.  The LowerAllocations
// transformation is a target dependant tranformation because it depends on the
// size of data types and alignment constraints.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/ChangeAllocations.h"
#include "llvm/Target/TargetData.h"
#include "llvm/DerivedTypes.h"
#include "llvm/iMemory.h"
#include "llvm/iOther.h"
#include "llvm/SymbolTable.h"
#include "llvm/ConstantVals.h"
#include "TransformInternals.h"
using std::vector;

// doInitialization - For the lower allocations pass, this ensures that a
// module contains a declaration for a malloc and a free function.
//
// This function is always successful.
//
bool LowerAllocations::doInitialization(Module *M) {
  bool Changed = false;
  const MethodType *MallocType =
    MethodType::get(PointerType::get(Type::SByteTy),
                    vector<const Type*>(1, Type::UIntTy), false);

  SymbolTable *SymTab = M->getSymbolTableSure();

  // Check for a definition of malloc
  if (Value *V = SymTab->lookup(PointerType::get(MallocType), "malloc")) {
    MallocMeth = cast<Method>(V);      // Yup, got it
  } else {                             // Nope, add one
    M->getMethodList().push_back(MallocMeth = new Method(MallocType, false,
                                                         "malloc"));
    Changed = true;
  }

  const MethodType *FreeType =
    MethodType::get(Type::VoidTy,
                    vector<const Type*>(1, PointerType::get(Type::SByteTy)),
		    false);

  // Check for a definition of free
  if (Value *V = SymTab->lookup(PointerType::get(FreeType), "free")) {
    FreeMeth = cast<Method>(V);      // Yup, got it
  } else {                             // Nope, add one
    M->getMethodList().push_back(FreeMeth = new Method(FreeType, false,"free"));
    Changed = true;
  }

  return Changed;
}

// runOnBasicBlock - This method does the actual work of converting
// instructions over, assuming that the pass has already been initialized.
//
bool LowerAllocations::runOnBasicBlock(BasicBlock *BB) {
  bool Changed = false;
  assert(MallocMeth && FreeMeth && BB && "Pass not initialized!");

  // Loop over all of the instructions, looking for malloc or free instructions
  for (unsigned i = 0; i < BB->size(); ++i) {
    BasicBlock::InstListType &BBIL = BB->getInstList();
    if (MallocInst *MI = dyn_cast<MallocInst>(*(BBIL.begin()+i))) {
      BBIL.remove(BBIL.begin()+i);   // remove the malloc instr...

      const Type *AllocTy =cast<PointerType>(MI->getType())->getElementType();

      // Get the number of bytes to be allocated for one element of the
      // requested type...
      unsigned Size = DataLayout.getTypeSize(AllocTy);

      // malloc(type) becomes sbyte *malloc(constint)
      Value *MallocArg = ConstantUInt::get(Type::UIntTy, Size);
      if (MI->getNumOperands() && Size == 1) {
        MallocArg = MI->getOperand(0);         // Operand * 1 = Operand
      } else if (MI->getNumOperands()) {
        // Multiply it by the array size if neccesary...
        MallocArg = BinaryOperator::create(Instruction::Mul,MI->getOperand(0),
                                           MallocArg);
        BBIL.insert(BBIL.begin()+i++, cast<Instruction>(MallocArg));
      }

      // Create the call to Malloc...
      CallInst *MCall = new CallInst(MallocMeth,
                                     vector<Value*>(1, MallocArg));
      BBIL.insert(BBIL.begin()+i, MCall);

      // Create a cast instruction to convert to the right type...
      CastInst *MCast = new CastInst(MCall, MI->getType());
      BBIL.insert(BBIL.begin()+i+1, MCast);

      // Replace all uses of the old malloc inst with the cast inst
      MI->replaceAllUsesWith(MCast);
      delete MI;                          // Delete the malloc inst
      Changed = true;
    } else if (FreeInst *FI = dyn_cast<FreeInst>(*(BBIL.begin()+i))) {
      BBIL.remove(BB->getInstList().begin()+i);

      // Cast the argument to free into a ubyte*...
      CastInst *MCast = new CastInst(FI->getOperand(0),
                                     PointerType::get(Type::UByteTy));
      BBIL.insert(BBIL.begin()+i, MCast);

      // Insert a call to the free function...
      CallInst *FCall = new CallInst(FreeMeth,
                                     vector<Value*>(1, MCast));
      BBIL.insert(BBIL.begin()+i+1, FCall);

      // Delete the old free instruction
      delete FI;
      Changed = true;
    }
  }

  return Changed;
}

bool RaiseAllocations::doInitialization(Module *M) {
  SymbolTable *ST = M->getSymbolTable();
  if (!ST) return false;

  // If the module has a symbol table, they might be referring to the malloc
  // and free functions.  If this is the case, grab the method pointers that
  // the module is using.
  //
  // Lookup %malloc and %free in the symbol table, for later use.  If they
  // don't exist, or are not external, we do not worry about converting calls
  // to that function into the appropriate instruction.
  //
  const PointerType *MallocType =   // Get the type for malloc
    PointerType::get(MethodType::get(PointerType::get(Type::SByteTy),
                                  vector<const Type*>(1, Type::UIntTy), false));
  MallocMeth = cast_or_null<Method>(ST->lookup(MallocType, "malloc"));
  if (MallocMeth && !MallocMeth->isExternal())
    MallocMeth = 0;  // Don't mess with locally defined versions of the fn

  const PointerType *FreeType =     // Get the type for free
    PointerType::get(MethodType::get(Type::VoidTy,
            vector<const Type*>(1, PointerType::get(Type::SByteTy)), false));
  FreeMeth = cast_or_null<Method>(ST->lookup(FreeType, "free"));
  if (FreeMeth && !FreeMeth->isExternal())
    FreeMeth = 0;  // Don't mess with locally defined versions of the fn

  return false;
}

// doOneCleanupPass - Do one pass over the input method, fixing stuff up.
//
bool RaiseAllocations::runOnBasicBlock(BasicBlock *BB) {
  bool Changed = false;
  BasicBlock::InstListType &BIL = BB->getInstList();

  for (BasicBlock::iterator BI = BB->begin(); BI != BB->end();) {
    Instruction *I = *BI;

    if (CallInst *CI = dyn_cast<CallInst>(I)) {
      if (CI->getCalledValue() == MallocMeth) {      // Replace call to malloc?
        const Type *PtrSByte = PointerType::get(Type::SByteTy);
        MallocInst *MallocI = new MallocInst(PtrSByte, CI->getOperand(1),
                                             CI->getName());
        CI->setName("");
        BI = BIL.insert(BI, MallocI)+1;
        ReplaceInstWithInst(BIL, BI, new CastInst(MallocI, PtrSByte));
        Changed = true;
        continue;  // Skip the ++BI
      } else if (CI->getCalledValue() == FreeMeth) { // Replace call to free?
        ReplaceInstWithInst(BIL, BI, new FreeInst(CI->getOperand(1)));
        Changed = true;
        continue;  // Skip the ++BI
      }
    }

    ++BI;
  }

  return Changed;
}
<commit_msg>Don't insert a useless cast<commit_after>//===- ChangeAllocations.cpp - Modify %malloc & %free calls -----------------=//
//
// This file defines two passes that convert malloc and free instructions to
// calls to and from %malloc & %free function calls.  The LowerAllocations
// transformation is a target dependant tranformation because it depends on the
// size of data types and alignment constraints.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/ChangeAllocations.h"
#include "llvm/Target/TargetData.h"
#include "llvm/DerivedTypes.h"
#include "llvm/iMemory.h"
#include "llvm/iOther.h"
#include "llvm/SymbolTable.h"
#include "llvm/ConstantVals.h"
#include "TransformInternals.h"
using std::vector;

// doInitialization - For the lower allocations pass, this ensures that a
// module contains a declaration for a malloc and a free function.
//
// This function is always successful.
//
bool LowerAllocations::doInitialization(Module *M) {
  bool Changed = false;
  const MethodType *MallocType =
    MethodType::get(PointerType::get(Type::SByteTy),
                    vector<const Type*>(1, Type::UIntTy), false);

  SymbolTable *SymTab = M->getSymbolTableSure();

  // Check for a definition of malloc
  if (Value *V = SymTab->lookup(PointerType::get(MallocType), "malloc")) {
    MallocMeth = cast<Method>(V);      // Yup, got it
  } else {                             // Nope, add one
    M->getMethodList().push_back(MallocMeth = new Method(MallocType, false,
                                                         "malloc"));
    Changed = true;
  }

  const MethodType *FreeType =
    MethodType::get(Type::VoidTy,
                    vector<const Type*>(1, PointerType::get(Type::SByteTy)),
		    false);

  // Check for a definition of free
  if (Value *V = SymTab->lookup(PointerType::get(FreeType), "free")) {
    FreeMeth = cast<Method>(V);      // Yup, got it
  } else {                             // Nope, add one
    M->getMethodList().push_back(FreeMeth = new Method(FreeType, false,"free"));
    Changed = true;
  }

  return Changed;
}

// runOnBasicBlock - This method does the actual work of converting
// instructions over, assuming that the pass has already been initialized.
//
bool LowerAllocations::runOnBasicBlock(BasicBlock *BB) {
  bool Changed = false;
  assert(MallocMeth && FreeMeth && BB && "Pass not initialized!");

  // Loop over all of the instructions, looking for malloc or free instructions
  for (unsigned i = 0; i < BB->size(); ++i) {
    BasicBlock::InstListType &BBIL = BB->getInstList();
    if (MallocInst *MI = dyn_cast<MallocInst>(*(BBIL.begin()+i))) {
      BBIL.remove(BBIL.begin()+i);   // remove the malloc instr...

      const Type *AllocTy =cast<PointerType>(MI->getType())->getElementType();

      // Get the number of bytes to be allocated for one element of the
      // requested type...
      unsigned Size = DataLayout.getTypeSize(AllocTy);

      // malloc(type) becomes sbyte *malloc(constint)
      Value *MallocArg = ConstantUInt::get(Type::UIntTy, Size);
      if (MI->getNumOperands() && Size == 1) {
        MallocArg = MI->getOperand(0);         // Operand * 1 = Operand
      } else if (MI->getNumOperands()) {
        // Multiply it by the array size if neccesary...
        MallocArg = BinaryOperator::create(Instruction::Mul,MI->getOperand(0),
                                           MallocArg);
        BBIL.insert(BBIL.begin()+i++, cast<Instruction>(MallocArg));
      }

      // Create the call to Malloc...
      CallInst *MCall = new CallInst(MallocMeth,
                                     vector<Value*>(1, MallocArg));
      BBIL.insert(BBIL.begin()+i, MCall);

      // Create a cast instruction to convert to the right type...
      CastInst *MCast = new CastInst(MCall, MI->getType());
      BBIL.insert(BBIL.begin()+i+1, MCast);

      // Replace all uses of the old malloc inst with the cast inst
      MI->replaceAllUsesWith(MCast);
      delete MI;                          // Delete the malloc inst
      Changed = true;
    } else if (FreeInst *FI = dyn_cast<FreeInst>(*(BBIL.begin()+i))) {
      BBIL.remove(BB->getInstList().begin()+i);

      // Cast the argument to free into a ubyte*...
      CastInst *MCast = new CastInst(FI->getOperand(0),
                                     PointerType::get(Type::UByteTy));
      BBIL.insert(BBIL.begin()+i, MCast);

      // Insert a call to the free function...
      CallInst *FCall = new CallInst(FreeMeth,
                                     vector<Value*>(1, MCast));
      BBIL.insert(BBIL.begin()+i+1, FCall);

      // Delete the old free instruction
      delete FI;
      Changed = true;
    }
  }

  return Changed;
}

bool RaiseAllocations::doInitialization(Module *M) {
  SymbolTable *ST = M->getSymbolTable();
  if (!ST) return false;

  // If the module has a symbol table, they might be referring to the malloc
  // and free functions.  If this is the case, grab the method pointers that
  // the module is using.
  //
  // Lookup %malloc and %free in the symbol table, for later use.  If they
  // don't exist, or are not external, we do not worry about converting calls
  // to that function into the appropriate instruction.
  //
  const PointerType *MallocType =   // Get the type for malloc
    PointerType::get(MethodType::get(PointerType::get(Type::SByteTy),
                                  vector<const Type*>(1, Type::UIntTy), false));
  MallocMeth = cast_or_null<Method>(ST->lookup(MallocType, "malloc"));
  if (MallocMeth && !MallocMeth->isExternal())
    MallocMeth = 0;  // Don't mess with locally defined versions of the fn

  const PointerType *FreeType =     // Get the type for free
    PointerType::get(MethodType::get(Type::VoidTy,
            vector<const Type*>(1, PointerType::get(Type::SByteTy)), false));
  FreeMeth = cast_or_null<Method>(ST->lookup(FreeType, "free"));
  if (FreeMeth && !FreeMeth->isExternal())
    FreeMeth = 0;  // Don't mess with locally defined versions of the fn

  return false;
}

// doOneCleanupPass - Do one pass over the input method, fixing stuff up.
//
bool RaiseAllocations::runOnBasicBlock(BasicBlock *BB) {
  bool Changed = false;
  BasicBlock::InstListType &BIL = BB->getInstList();

  for (BasicBlock::iterator BI = BB->begin(); BI != BB->end();) {
    Instruction *I = *BI;

    if (CallInst *CI = dyn_cast<CallInst>(I)) {
      if (CI->getCalledValue() == MallocMeth) {      // Replace call to malloc?
        const Type *PtrSByte = PointerType::get(Type::SByteTy);
        MallocInst *MallocI = new MallocInst(PtrSByte, CI->getOperand(1),
                                             CI->getName());
        CI->setName("");
        ReplaceInstWithInst(BIL, BI, MallocI);
        Changed = true;
        continue;  // Skip the ++BI
      } else if (CI->getCalledValue() == FreeMeth) { // Replace call to free?
        ReplaceInstWithInst(BIL, BI, new FreeInst(CI->getOperand(1)));
        Changed = true;
        continue;  // Skip the ++BI
      }
    }

    ++BI;
  }

  return Changed;
}
<|endoftext|>


<commit_before>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2011 Sergey Lisitsyn
 * Copyright (C) 2011 Berlin Institute of Technology and Max-Planck-Society
 */

#include "preprocessor/LocallyLinearEmbedding.h"
#ifdef HAVE_LAPACK
#include "lib/lapack.h"
#include "lib/common.h"
#include "lib/Mathematics.h"
#include "lib/io.h"
#include "distance/EuclidianDistance.h"
#include "lib/Signal.h"

using namespace shogun;

CLocallyLinearEmbedding::CLocallyLinearEmbedding() : CSimplePreprocessor<float64_t>(), m_target_dim(1), m_k(1)
{
}

CLocallyLinearEmbedding::~CLocallyLinearEmbedding()
{
}

bool CLocallyLinearEmbedding::init(CFeatures* data)
{
	return true;
}

void CLocallyLinearEmbedding::cleanup()
{
}

float64_t* CLocallyLinearEmbedding::apply_to_feature_matrix(CFeatures* data)
{
	// shorthand for simplefeatures data
	CSimpleFeatures<float64_t>* pdata = (CSimpleFeatures<float64_t>*) data;
	ASSERT(pdata);

	// get dimensionality and number of vectors of data
	int32_t dim = pdata->get_num_features();
	ASSERT(m_target_dim<=dim);
	int32_t N = pdata->get_num_vectors();
	ASSERT(m_k<N);

	// loop variables
	int32_t i,j,k;

	// compute distance matrix
	CDistance* distance = new CEuclidianDistance(pdata,pdata);
	float64_t* distance_matrix;
	distance->get_distance_matrix(&distance_matrix,&N,&N);
	delete distance;

	// init matrices to be used
	int32_t* neighborhood_matrix = new int32_t[N*m_k];
	int32_t* local_neighbors_idxs = new int32_t[N];

	// construct neighborhood matrix (contains idxs of neighbors for
	// i-th object in i-th column)
	for (i=0; i<N; i++)
	{
		for (j=0; j<N; j++)
		{
			local_neighbors_idxs[j] = j;
		}

		CMath::qsort_index(distance_matrix+(i*N),local_neighbors_idxs,N);

		for (j=0; j<m_k; j++)
			neighborhood_matrix[j*N+i] = local_neighbors_idxs[j+1];
	}

	delete[] distance_matrix;
	delete[] local_neighbors_idxs;

	// init W (weight) matrix
	float64_t* W_matrix = new float64_t[N*N];
	for (i=0; i<N; i++)
		for (j=0; j<N; j++)
			W_matrix[i*N+j]=0.0;

	// init matrices and norm factor to be used
	float64_t* z_matrix = new float64_t[N*dim];
	float64_t* covariance_matrix = new float64_t[m_k*m_k];
	float64_t* id_vector = new float64_t[m_k];
	int32_t* ipiv = new int32_t[m_k];
	float64_t norming = 0.0;

	for (i=0; i<N; i++)
	{
		// get feature matrix
		SGMatrix<float64_t> feature_matrix = pdata->get_feature_matrix();

		// compute local feature matrix containing neighbors of i-th vector
		for (j=0; j<m_k; j++)
		{
			for (k=0; k<dim; k++)
				z_matrix[j*dim+k] = feature_matrix.matrix[neighborhood_matrix[j*N+i]*dim+k];
		}

		// center features by subtracting i-th feature column
		for (j=0; j<m_k; j++)
		{
			for (k=0; k<dim; k++)
				z_matrix[j*dim+k] -= feature_matrix.matrix[i*dim+k];
		}

		// compute local covariance matrix
		cblas_dgemm(CblasColMajor,CblasTrans,CblasNoTrans,
		            m_k,m_k,dim,
		            1.0,
		            z_matrix,dim,
		            z_matrix,dim,
		            0.0,
		            covariance_matrix,m_k);

		for (j=0; j<m_k; j++)
			id_vector[j] = 1.0;

		// regularize in case of ill-posed system
		if (m_k>dim)
		{
			// compute tr(C)
			float64_t trace = 0.0;
			for (j=0; j<m_k; j++)
				trace += covariance_matrix[j*m_k+j];

			for (j=0; j<m_k; j++)
				covariance_matrix[j*m_k+j] += 1e-3*trace;
		}

		// solve system of linear equations: covariance_matrix * X = 1
		clapack_dgesv(CblasColMajor,
		              m_k,1,
		              covariance_matrix,m_k,
		              ipiv,
		              id_vector,m_k);

		// normalize weights
		norming=0.0;
		for (j=0; j<m_k; j++)
			norming += CMath::abs(id_vector[j]);

		for (j=0; j<m_k; j++)
			id_vector[j]/=norming;

		// put weights into W matrix
		for (j=0; j<m_k; j++)
			W_matrix[N*neighborhood_matrix[j*N+i]+i]=id_vector[j];

	}

	// clean
	delete[] ipiv;
	delete[] id_vector;
	delete[] neighborhood_matrix;
	delete[] z_matrix;
	delete[] covariance_matrix;

	// W=I-W
	for (i=0; i<N; i++)
	{
		for (j=0; j<N; j++)
			W_matrix[j*N+i] = (i==j) ? 1.0-W_matrix[j*N+i] : -W_matrix[j*N+i];
	}

	// compute M=(W-I)'*(W-I)
	float64_t* M_matrix = new float64_t[N*N];
	cblas_dgemm(CblasColMajor,CblasTrans, CblasNoTrans,
	            N,N,N,1.0,
	            W_matrix,N,
	            W_matrix,N,
	            0.0,
	            M_matrix,N);

	delete[] W_matrix;

	// compute eigenvectors
	float64_t* eigenvalues_vector = new float64_t[N];
	int32_t eigenproblem_status = 0;
	wrap_dsyev('V','U',
				N,M_matrix,
				N,eigenvalues_vector,
				&eigenproblem_status);
	ASSERT(eigenproblem_status==0);

	// replace features with bottom eigenvectos
	float64_t* new_feature_matrix = new float64_t[N*m_target_dim];

	for (i=0; i<m_target_dim; i++)
	{
		for (j=0; j<N; j++)
			new_feature_matrix[j*m_target_dim+i] = M_matrix[(i+1)*(N)+j];
	}

	// clean
	delete[] eigenvalues_vector;
	delete[] M_matrix;

	SGMatrix<float64_t> features(new_feature_matrix,m_target_dim,N);
	pdata->set_feature_matrix(features);

	return features.matrix;
}

float64_t* CLocallyLinearEmbedding::apply_to_feature_vector(float64_t* f, int32_t &len)
{
	SG_NOTIMPLEMENTED;
}

#endif /* HAVE_LAPACK */
<commit_msg>Fixes for LLE<commit_after>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2011 Sergey Lisitsyn
 * Copyright (C) 2011 Berlin Institute of Technology and Max-Planck-Society
 */

#include "preprocessor/LocallyLinearEmbedding.h"
#ifdef HAVE_LAPACK
#include "lib/lapack.h"
#include "lib/common.h"
#include "lib/Mathematics.h"
#include "lib/io.h"
#include "distance/EuclidianDistance.h"
#include "lib/Signal.h"

using namespace shogun;

CLocallyLinearEmbedding::CLocallyLinearEmbedding() : CSimplePreprocessor<float64_t>(), m_target_dim(1), m_k(1)
{
}

CLocallyLinearEmbedding::~CLocallyLinearEmbedding()
{
}

bool CLocallyLinearEmbedding::init(CFeatures* data)
{
	return true;
}

void CLocallyLinearEmbedding::cleanup()
{
}

float64_t* CLocallyLinearEmbedding::apply_to_feature_matrix(CFeatures* data)
{
	// shorthand for simplefeatures data
	CSimpleFeatures<float64_t>* pdata = (CSimpleFeatures<float64_t>*) data;
	ASSERT(pdata);

	// get dimensionality and number of vectors of data
	int32_t dim = pdata->get_num_features();
	ASSERT(m_target_dim<=dim);
	int32_t N = pdata->get_num_vectors();
	ASSERT(m_k<N);

	// loop variables
	int32_t i,j,k;

	// compute distance matrix
	CDistance* distance = new CEuclidianDistance(pdata,pdata);
	float64_t* distance_matrix;
	distance->get_distance_matrix(&distance_matrix,&N,&N);
	delete distance;

	// init matrices to be used
	int32_t* neighborhood_matrix = new int32_t[N*m_k];
	int32_t* local_neighbors_idxs = new int32_t[N];

	// construct neighborhood matrix (contains idxs of neighbors for
	// i-th object in i-th column)
	for (i=0; i<N; i++)
	{
		for (j=0; j<N; j++)
		{
			local_neighbors_idxs[j] = j;
		}

		CMath::qsort_index(distance_matrix+(i*N),local_neighbors_idxs,N);

		for (j=0; j<m_k; j++)
			neighborhood_matrix[j*N+i] = local_neighbors_idxs[j+1];
	}

	delete[] distance_matrix;
	delete[] local_neighbors_idxs;

	// init W (weight) matrix
	float64_t* W_matrix = new float64_t[N*N];
	for (i=0; i<N; i++)
		for (j=0; j<N; j++)
			W_matrix[i*N+j]=0.0;

	// init matrices and norm factor to be used
	float64_t* z_matrix = new float64_t[N*dim];
	float64_t* covariance_matrix = new float64_t[m_k*m_k];
	float64_t* id_vector = new float64_t[m_k];
	int32_t* ipiv = new int32_t[m_k];
	float64_t norming = 0.0;

	// get feature matrix
	SGMatrix<float64_t> feature_matrix = pdata->get_feature_matrix();

	for (i=0; i<N; i++)
	{
		// compute local feature matrix containing neighbors of i-th vector
		for (j=0; j<m_k; j++)
		{
			for (k=0; k<dim; k++)
				z_matrix[j*dim+k] = feature_matrix.matrix[neighborhood_matrix[j*N+i]*dim+k];
		}

		// center features by subtracting i-th feature column
		for (j=0; j<m_k; j++)
		{
			for (k=0; k<dim; k++)
				z_matrix[j*dim+k] -= feature_matrix.matrix[i*dim+k];
		}

		// compute local covariance matrix
		cblas_dgemm(CblasColMajor,CblasTrans,CblasNoTrans,
		            m_k,m_k,dim,
		            1.0,
		            z_matrix,dim,
		            z_matrix,dim,
		            0.0,
		            covariance_matrix,m_k);

		for (j=0; j<m_k; j++)
			id_vector[j] = 1.0;

		// regularize in case of ill-posed system
		if (m_k>dim)
		{
			// compute tr(C)
			float64_t trace = 0.0;
			for (j=0; j<m_k; j++)
				trace += covariance_matrix[j*m_k+j];

			for (j=0; j<m_k; j++)
				covariance_matrix[j*m_k+j] += 1e-3*trace;
		}

		// solve system of linear equations: covariance_matrix * X = 1
		clapack_dgesv(CblasColMajor,
		              m_k,1,
		              covariance_matrix,m_k,
		              ipiv,
		              id_vector,m_k);

		// normalize weights
		norming=0.0;
		for (j=0; j<m_k; j++)
			norming += id_vector[j];

		for (j=0; j<m_k; j++)
			id_vector[j]/=norming;

		// put weights into W matrix
		for (j=0; j<m_k; j++)
			W_matrix[N*neighborhood_matrix[j*N+i]+i]=id_vector[j];

	}

	// clean
	delete[] ipiv;
	delete[] id_vector;
	delete[] neighborhood_matrix;
	delete[] z_matrix;
	delete[] covariance_matrix;

	// W=I-W
	for (i=0; i<N; i++)
	{
		for (j=0; j<N; j++)
			W_matrix[j*N+i] = (i==j) ? 1.0-W_matrix[j*N+i] : -W_matrix[j*N+i];
	}

	// compute M=(W-I)'*(W-I)
	float64_t* M_matrix = new float64_t[N*N];
	cblas_dgemm(CblasColMajor,CblasTrans, CblasNoTrans,
	            N,N,N,1.0,
	            W_matrix,N,
	            W_matrix,N,
	            0.0,
	            M_matrix,N);

	delete[] W_matrix;

	// TODO add some more efficient method usage if supported by some library
	// compute eigenvectors
	float64_t* eigenvalues_vector = new float64_t[N];
	int32_t eigenproblem_status = 0;
	wrap_dsyev('V','U',
				N,M_matrix,
				N,eigenvalues_vector,
				&eigenproblem_status);
	ASSERT(eigenproblem_status==0);

	// replace features with bottom eigenvectos
	float64_t* new_feature_matrix = new float64_t[N*m_target_dim];

	for (i=0; i<m_target_dim; i++)
	{
		for (j=0; j<N; j++)
			new_feature_matrix[j*m_target_dim+i] = M_matrix[(i+1)*(N)+j];
	}

	// clean
	delete[] eigenvalues_vector;
	delete[] M_matrix;

	SGMatrix<float64_t> features(new_feature_matrix,m_target_dim,N);
	pdata->set_feature_matrix(features);

	return features.matrix;
}

float64_t* CLocallyLinearEmbedding::apply_to_feature_vector(float64_t* f, int32_t &len)
{
	SG_NOTIMPLEMENTED;
}

#endif /* HAVE_LAPACK */
<|endoftext|>


<commit_before>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2015 ETH Zurich
 ** All rights reserved.
 **
 ** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 ** following conditions are met:
 **
 **    * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 **      following disclaimer.
 **    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 **      following disclaimer in the documentation and/or other materials provided with the distribution.
 **    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
 **      derived from this software without specific prior written permission.
 **
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **
 **********************************************************************************************************************/

#include "AllMetaDefinitions.h"

#include "MacroDefinitions.h"
#include "MacroExpansion.h"
#include "MacroExpansions.h"

#include "NodeHelpers.h"
#include "../ClangHelpers.h"

#include "OOModel/src/allOOModelNodes.h"

namespace CppImport {

AllMetaDefinitions::AllMetaDefinitions(OOModel::Project* root, ClangHelpers& clangHelper,
									  const MacroDefinitions& macroDefinitions, MacroExpansions& macroExpansions)
	: root_{root}, clang_{clangHelper}, macroDefinitions_{macroDefinitions}, macroExpansions_{macroExpansions},
	  standardMetaDefinitions_{clangHelper, macroDefinitions, macroExpansions} {}

void AllMetaDefinitions::createMetaDef(QList<Model::Node*> nodes, MacroExpansion* expansion, NodeToCloneMap& mapping,
											 QList<MacroArgumentInfo>& arguments)
{
	if (auto metaDef = standardMetaDefinitions_.createMetaDef(expansion->definition()))
	{
		auto metaDefParent = metaDefinitionParent(expansion->definition());

		// check whether this expansion is not a potential partial begin macro specialization
		if (auto beginChild = partialBeginChild(expansion))
			handlePartialBeginSpecialization(metaDefParent, metaDef, expansion, beginChild);
		else
			standardMetaDefinitions_.createMetaDefinitionBody(metaDef, nodes, expansion, mapping, arguments);

		clang_.insertDeclarationInFolder(metaDef, expansion->definition()->getMacroInfo()->getDefinitionLoc(),
													metaDefParent);
	}
}

OOModel::Declaration* AllMetaDefinitions::metaDefinitionParent(const clang::MacroDirective* md)
{
	OOModel::Declaration* result = clang_.projectForLocation(md->getLocation());
	if (result == clang_.rootProject())
	{
		result = NodeHelpers::findDeclaration(root_->modules(), "ExternalMacro");

		if (!result)
		{
			result = new OOModel::Module{"ExternalMacro", OOModel::Module::ModuleKind::Folder};
			root_->modules()->append(result);
		}
	}

	Q_ASSERT(result);
	return result;
}

void AllMetaDefinitions::handlePartialBeginSpecialization(OOModel::Declaration* metaDefParent,
																	  OOModel::MetaDefinition* metaDef,
																	  MacroExpansion* expansion,
																	  MacroExpansion* beginChild)
{
	QList<Model::Node*> statements = macroExpansions_.topLevelNodes(expansion, MacroExpansions::NodeOriginType::Direct);

	if (!statements.empty())
	{
		// create a new list containing all the additional statements defined in expansion (the specialization)
		auto list = new Model::List{};
		for (auto stmt : statements) list->append(stmt->clone());

		auto childDef = standardMetaDefinitions_.metaDefinition(beginChild->definition());
		Q_ASSERT(childDef);

		if (metaDefParent->name().endsWith("_CPP"))
		{
			QString cppSpecializationSpliceName = "cppSpecSplice";

			if (!NodeHelpers::findDeclaration(childDef->arguments(), cppSpecializationSpliceName))
			{
				childDef->arguments()->append(new OOModel::FormalMetaArgument{cppSpecializationSpliceName});

				auto classContext = DCast<OOModel::Class>(childDef->context());
				Q_ASSERT(classContext);

				if (classContext->methods()->size() > 0)
					classContext->methods()->last()->items()->append(
								new OOModel::ExpressionStatement{new OOModel::ReferenceExpression{cppSpecializationSpliceName}});
			}
		}

		beginChild->metaCall()->arguments()->append(list);
		specializations_.insert(macroDefinitions_.signature(expansion->definition()), list);
	}

	metaDef->context()->metaCalls()->append(beginChild->metaCall());
}

void AllMetaDefinitions::applyPartialBeginSpecializationTransformation(MacroExpansion* hExpansion,
																						MacroExpansion* cppExpansion)
{
	auto hMetaDefinition = standardMetaDefinitions_.metaDefinition(hExpansion->definition());

	// if the metaBindingInput was not yet added to this header xMacro begin meta definition then add it
	if (!NodeHelpers::findDeclaration(hMetaDefinition->arguments(), "metaBindingInput"))
		hMetaDefinition->arguments()->append(new OOModel::FormalMetaArgument{"metaBindingInput"});

	/*
	 * try to find a child meta call of this meta definition and modify it according to the specialization.
	 * we have to do it over the tree because the inner meta call might originate from an expansion of a
	 * translation unit prior to this one.
	 */
	auto specHash = macroDefinitions_.signature(cppExpansion->definition());

	auto it = specializations_.find(specHash);
	if (it != specializations_.end())
		for (auto expr : *hMetaDefinition->context()->metaCalls())
			if (auto metaCall = DCast<OOModel::MetaCallExpression>(expr))
				if (auto callee = DCast<OOModel::ReferenceExpression>(metaCall->callee()))
					if (callee->name().startsWith("BEGIN_"))
						if (!specialized_.contains(metaCall))
						{
							// avoid multiple specialization transformations
							specialized_.insert(metaCall);

							// apply specialization transformations
							metaCall->arguments()->append((*it)->clone());
							metaCall->arguments()->append(new OOModel::ReferenceExpression{"metaBindingInput"});

							return;
						}
}

void AllMetaDefinitions::handleXMacros()
{
	for (auto expansion : macroExpansions_.expansions())
		if (!expansion->xMacroChildren().empty())
		{
			// case: expansion is a xMacro parent

			// find the .cpp counterpart
			for (auto node : macroExpansions_.topLevelNodes(expansion, MacroExpansions::NodeOriginType::Transitive))
				if (auto other = matchingXMacroExpansion(node))
				{
					// case: expansion is a .h xMacro parent and other is a .cpp xMacro parent

					applyPartialBeginSpecializationTransformation(expansion, other);

					// remove the .cpp xMacro meta call from the original tree
					NodeHelpers::removeNodeFromParent(other->metaCall(), true);

					// create the meta call that also contains unbound xMacro children
					auto merged = new OOModel::MetaCallExpression{};
					merged->setCallee(new OOModel::ReferenceExpression{
												macroDefinitions_.definitionName(expansion->definition())});

					for (auto arg : *expansion->metaCall()->arguments())
						merged->arguments()->append(arg->clone());

					// create the unbound xMacro children list
					auto list = new Model::List{};
					for (auto xMacroChild : expansion->xMacroChildren())
					{
						auto unbound = new OOModel::MetaCallExpression{
									macroDefinitions_.definitionName(xMacroChild->definition())};
						for (auto arg : *xMacroChild->metaCall()->arguments())
							unbound->arguments()->append(arg->clone());

						list->append(unbound);
					}
					merged->arguments()->append(list);

					// replace the .h xMacro meta call with the newly created merged meta call
					expansion->metaCall()->parent()->replaceChild(expansion->metaCall(), merged);

					auto metaDef = createXMacroMetaDef(expansion, other);

					// check whether to insert meta bindings for all xMacro children
					for (auto i = 0; i < expansion->xMacroChildren().size(); i++)
					{
						auto xMacroChildH = expansion->xMacroChildren().at(i);
						auto xMacroChildCpp = other->xMacroChildren().at(i);

						auto unboundName = macroDefinitions_.definitionName(xMacroChildH->definition());

						auto binding1 = metaDef->metaBindings()->at(0);
						auto binding2 = metaDef->metaBindings()->at(1);

						// if the binding for this xMacro child already exists do nothing
						if (NodeHelpers::findDeclaration(binding1->mappings(), unboundName)) continue;

						// insert meta bindings for this xMacro child
						auto mapping1 = new OOModel::MetaCallMapping{unboundName};
						mapping1->setValue(new OOModel::ReferenceExpression{
													 macroDefinitions_.definitionName(xMacroChildH->definition())});
						binding1->mappings()->append(mapping1);

						auto mapping2 = new OOModel::MetaCallMapping{unboundName};
						mapping2->setValue(new OOModel::ReferenceExpression{
													 macroDefinitions_.definitionName(xMacroChildCpp->definition())});
						binding2->mappings()->append(mapping2);
					}

					break;
				}
		}
}

OOModel::MetaDefinition* AllMetaDefinitions::createXMacroMetaDef(MacroExpansion* hExpansion,
																					  MacroExpansion* cppExpansion)
{
	auto hBaseExpansion = basePartialBegin(hExpansion);
	auto cppBaseExpansion = basePartialBegin(cppExpansion);

	auto mergedMetaDef = xMacroMetaDefinition(hBaseExpansion->definition());
	if (!mergedMetaDef)
	{
		auto hBaseMetaDef = standardMetaDefinitions_.metaDefinition(hBaseExpansion->definition());
		auto cppBaseMetaDef = standardMetaDefinitions_.metaDefinition(cppBaseExpansion->definition());

		mergedMetaDef = hBaseMetaDef->clone();
		mergedMetaDef->setName(macroDefinitions_.definitionName(hBaseExpansion->definition()));
		xMacrometaDefinitions_.insert(macroDefinitions_.definitionName(hBaseExpansion->definition()), mergedMetaDef);

		for (auto cppArg : *cppBaseMetaDef->arguments())
			if (!NodeHelpers::findDeclaration(mergedMetaDef->arguments(), cppArg->name()))
				mergedMetaDef->arguments()->append(cppArg->clone());

		/* assumptions:
		 * - the context of hBaseMetaDef is a Module
		 * - the context module of hBaseMetaDef contains exactly one class
		 * - the context of cppBaseMetaDef is a Class
		 * - the merged MetaDefinition is correct if we merge those 2 classes
		 */
		auto mergedClass = DCast<OOModel::Class>(DCast<OOModel::Module>(mergedMetaDef->context())->classes()->first());
		auto cppBaseClass = DCast<OOModel::Class>(cppBaseMetaDef->context());

		mergeClasses(mergedClass, cppBaseClass);

		QString metaBindingInputName = "metaBindingInput";
		QString declarationSpliceName = "list1";
		QString statementSpliceName = "list2";

		// add an argument for the input to the MetaBindings
		mergedMetaDef->arguments()->append(new OOModel::FormalMetaArgument{metaBindingInputName});

		// add splices for the MetaBinding results
		mergedClass->metaCalls()->append(new OOModel::ReferenceExpression{declarationSpliceName});
		mergedClass->methods()->last()->items()->append(new OOModel::ExpressionStatement{
																			new OOModel::ReferenceExpression{statementSpliceName}});

		// MetaBinding for declarations splice
		auto declarationsSpliceMetaBinding = new OOModel::MetaBinding{declarationSpliceName};
		declarationsSpliceMetaBinding->setInput(new OOModel::ReferenceExpression{metaBindingInputName});
		mergedMetaDef->metaBindings()->append(declarationsSpliceMetaBinding);

		// MetaBinding for statements splice
		auto statementsSpliceMetaBinding = new OOModel::MetaBinding{statementSpliceName};
		statementsSpliceMetaBinding->setInput(new OOModel::ReferenceExpression{metaBindingInputName});
		mergedMetaDef->metaBindings()->append(statementsSpliceMetaBinding);

		// add the merged MetaDefinition to the tree
		hBaseMetaDef->parent()->replaceChild(hBaseMetaDef, mergedMetaDef);
	}

	NodeHelpers::removeNodeFromParent(standardMetaDefinitions_.metaDefinition(cppExpansion->definition()), true);
	NodeHelpers::removeNodeFromParent(standardMetaDefinitions_.metaDefinition(cppBaseExpansion->definition()), true);

	return mergedMetaDef;
}

MacroExpansion* AllMetaDefinitions::basePartialBegin(MacroExpansion* partialBeginExpansion)
{
	Q_ASSERT(macroDefinitions_.isPartialBegin(partialBeginExpansion->definition()));

	for (auto child : partialBeginExpansion->children())
		if (macroDefinitions_.isPartialBegin(child->definition()))
		 return basePartialBegin(child);

	return partialBeginExpansion;
}

void AllMetaDefinitions::mergeClasses(OOModel::Class* merged, OOModel::Class* mergee)
{
	for (auto metaCall : *mergee->metaCalls())
		merged->metaCalls()->append(metaCall->clone());

	for (auto mergedMethod : *merged->methods())
		for (auto mergeeMethod : *mergee->methods())
		{
			if (mergedMethod->name() == mergeeMethod->name())
			{
				for (auto k = 0; k < mergeeMethod->items()->size(); k++)
					mergedMethod->items()->append(mergeeMethod->items()->at(k)->clone());

				mergedMethod->memberInitializers()->clear();
				for (auto memberInitializer : *mergeeMethod->memberInitializers())
					mergedMethod->memberInitializers()->append(memberInitializer->clone());
			}
		}

}

MacroExpansion* AllMetaDefinitions::partialBeginChild(MacroExpansion* expansion)
{
	for (auto child : expansion->children())
		if (macroDefinitions_.isPartialBegin(child->definition()))
			return child;

	return nullptr;
}

OOModel::MetaDefinition* AllMetaDefinitions::xMacroMetaDefinition(const clang::MacroDirective* md)
{
	QString h = macroDefinitions_.definitionName(md);

	auto it = xMacrometaDefinitions_.find(h);

	return it != xMacrometaDefinitions_.end() ? *it : nullptr;
}

MacroExpansion* AllMetaDefinitions::matchingXMacroExpansion(Model::Node* node)
{
	if (auto metaCall = DCast<OOModel::MetaCallExpression>(node))
		for (auto expansion : macroExpansions_.expansions())
			if (!expansion->xMacroChildren().empty())
				if (expansion->metaCall() == metaCall)
					return expansion;

	for (auto child : node->children())
		if (auto expansion = matchingXMacroExpansion(child))
			return expansion;

	return nullptr;
}

}
<commit_msg>remove condition targetung old model structure<commit_after>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2015 ETH Zurich
 ** All rights reserved.
 **
 ** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 ** following conditions are met:
 **
 **    * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 **      following disclaimer.
 **    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 **      following disclaimer in the documentation and/or other materials provided with the distribution.
 **    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
 **      derived from this software without specific prior written permission.
 **
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **
 **********************************************************************************************************************/

#include "AllMetaDefinitions.h"

#include "MacroDefinitions.h"
#include "MacroExpansion.h"
#include "MacroExpansions.h"

#include "NodeHelpers.h"
#include "../ClangHelpers.h"

#include "OOModel/src/allOOModelNodes.h"

namespace CppImport {

AllMetaDefinitions::AllMetaDefinitions(OOModel::Project* root, ClangHelpers& clangHelper,
									  const MacroDefinitions& macroDefinitions, MacroExpansions& macroExpansions)
	: root_{root}, clang_{clangHelper}, macroDefinitions_{macroDefinitions}, macroExpansions_{macroExpansions},
	  standardMetaDefinitions_{clangHelper, macroDefinitions, macroExpansions} {}

void AllMetaDefinitions::createMetaDef(QList<Model::Node*> nodes, MacroExpansion* expansion, NodeToCloneMap& mapping,
											 QList<MacroArgumentInfo>& arguments)
{
	if (auto metaDef = standardMetaDefinitions_.createMetaDef(expansion->definition()))
	{
		auto metaDefParent = metaDefinitionParent(expansion->definition());

		// check whether this expansion is not a potential partial begin macro specialization
		if (auto beginChild = partialBeginChild(expansion))
			handlePartialBeginSpecialization(metaDefParent, metaDef, expansion, beginChild);
		else
			standardMetaDefinitions_.createMetaDefinitionBody(metaDef, nodes, expansion, mapping, arguments);

		clang_.insertDeclarationInFolder(metaDef, expansion->definition()->getMacroInfo()->getDefinitionLoc(),
													metaDefParent);
	}
}

OOModel::Declaration* AllMetaDefinitions::metaDefinitionParent(const clang::MacroDirective* md)
{
	OOModel::Declaration* result = clang_.projectForLocation(md->getLocation());
	if (result == clang_.rootProject())
	{
		result = NodeHelpers::findDeclaration(root_->modules(), "ExternalMacro");

		if (!result)
		{
			result = new OOModel::Module{"ExternalMacro", OOModel::Module::ModuleKind::Folder};
			root_->modules()->append(result);
		}
	}

	Q_ASSERT(result);
	return result;
}

void AllMetaDefinitions::handlePartialBeginSpecialization(OOModel::Declaration* metaDefParent,
																	  OOModel::MetaDefinition* metaDef,
																	  MacroExpansion* expansion,
																	  MacroExpansion* beginChild)
{
	QList<Model::Node*> statements = macroExpansions_.topLevelNodes(expansion, MacroExpansions::NodeOriginType::Direct);

	if (!statements.empty())
	{
		// create a new list containing all the additional statements defined in expansion (the specialization)
		auto list = new Model::List{};
		for (auto stmt : statements) list->append(stmt->clone());

		auto childDef = standardMetaDefinitions_.metaDefinition(beginChild->definition());
		Q_ASSERT(childDef);

		QString cppSpecializationSpliceName = "cppSpecSplice";

		if (!NodeHelpers::findDeclaration(childDef->arguments(), cppSpecializationSpliceName))
		{
			childDef->arguments()->append(new OOModel::FormalMetaArgument{cppSpecializationSpliceName});

			auto classContext = DCast<OOModel::Class>(childDef->context());
			Q_ASSERT(classContext);

			if (classContext->methods()->size() > 0)
				classContext->methods()->last()->items()->append(
							new OOModel::ExpressionStatement{new OOModel::ReferenceExpression{cppSpecializationSpliceName}});
		}

		beginChild->metaCall()->arguments()->append(list);
		specializations_.insert(macroDefinitions_.signature(expansion->definition()), list);
	}

	metaDef->context()->metaCalls()->append(beginChild->metaCall());
}

void AllMetaDefinitions::applyPartialBeginSpecializationTransformation(MacroExpansion* hExpansion,
																						MacroExpansion* cppExpansion)
{
	auto hMetaDefinition = standardMetaDefinitions_.metaDefinition(hExpansion->definition());

	// if the metaBindingInput was not yet added to this header xMacro begin meta definition then add it
	if (!NodeHelpers::findDeclaration(hMetaDefinition->arguments(), "metaBindingInput"))
		hMetaDefinition->arguments()->append(new OOModel::FormalMetaArgument{"metaBindingInput"});

	/*
	 * try to find a child meta call of this meta definition and modify it according to the specialization.
	 * we have to do it over the tree because the inner meta call might originate from an expansion of a
	 * translation unit prior to this one.
	 */
	auto specHash = macroDefinitions_.signature(cppExpansion->definition());

	auto it = specializations_.find(specHash);
	if (it != specializations_.end())
		for (auto expr : *hMetaDefinition->context()->metaCalls())
			if (auto metaCall = DCast<OOModel::MetaCallExpression>(expr))
				if (auto callee = DCast<OOModel::ReferenceExpression>(metaCall->callee()))
					if (callee->name().startsWith("BEGIN_"))
						if (!specialized_.contains(metaCall))
						{
							// avoid multiple specialization transformations
							specialized_.insert(metaCall);

							// apply specialization transformations
							metaCall->arguments()->append((*it)->clone());
							metaCall->arguments()->append(new OOModel::ReferenceExpression{"metaBindingInput"});

							return;
						}
}

void AllMetaDefinitions::handleXMacros()
{
	for (auto expansion : macroExpansions_.expansions())
		if (!expansion->xMacroChildren().empty())
		{
			// case: expansion is a xMacro parent

			// find the .cpp counterpart
			for (auto node : macroExpansions_.topLevelNodes(expansion, MacroExpansions::NodeOriginType::Transitive))
				if (auto other = matchingXMacroExpansion(node))
				{
					// case: expansion is a .h xMacro parent and other is a .cpp xMacro parent

					applyPartialBeginSpecializationTransformation(expansion, other);

					// remove the .cpp xMacro meta call from the original tree
					NodeHelpers::removeNodeFromParent(other->metaCall(), true);

					// create the meta call that also contains unbound xMacro children
					auto merged = new OOModel::MetaCallExpression{};
					merged->setCallee(new OOModel::ReferenceExpression{
												macroDefinitions_.definitionName(expansion->definition())});

					for (auto arg : *expansion->metaCall()->arguments())
						merged->arguments()->append(arg->clone());

					// create the unbound xMacro children list
					auto list = new Model::List{};
					for (auto xMacroChild : expansion->xMacroChildren())
					{
						auto unbound = new OOModel::MetaCallExpression{
									macroDefinitions_.definitionName(xMacroChild->definition())};
						for (auto arg : *xMacroChild->metaCall()->arguments())
							unbound->arguments()->append(arg->clone());

						list->append(unbound);
					}
					merged->arguments()->append(list);

					// replace the .h xMacro meta call with the newly created merged meta call
					expansion->metaCall()->parent()->replaceChild(expansion->metaCall(), merged);

					auto metaDef = createXMacroMetaDef(expansion, other);

					// check whether to insert meta bindings for all xMacro children
					for (auto i = 0; i < expansion->xMacroChildren().size(); i++)
					{
						auto xMacroChildH = expansion->xMacroChildren().at(i);
						auto xMacroChildCpp = other->xMacroChildren().at(i);

						auto unboundName = macroDefinitions_.definitionName(xMacroChildH->definition());

						auto binding1 = metaDef->metaBindings()->at(0);
						auto binding2 = metaDef->metaBindings()->at(1);

						// if the binding for this xMacro child already exists do nothing
						if (NodeHelpers::findDeclaration(binding1->mappings(), unboundName)) continue;

						// insert meta bindings for this xMacro child
						auto mapping1 = new OOModel::MetaCallMapping{unboundName};
						mapping1->setValue(new OOModel::ReferenceExpression{
													 macroDefinitions_.definitionName(xMacroChildH->definition())});
						binding1->mappings()->append(mapping1);

						auto mapping2 = new OOModel::MetaCallMapping{unboundName};
						mapping2->setValue(new OOModel::ReferenceExpression{
													 macroDefinitions_.definitionName(xMacroChildCpp->definition())});
						binding2->mappings()->append(mapping2);
					}

					break;
				}
		}
}

OOModel::MetaDefinition* AllMetaDefinitions::createXMacroMetaDef(MacroExpansion* hExpansion,
																					  MacroExpansion* cppExpansion)
{
	auto hBaseExpansion = basePartialBegin(hExpansion);
	auto cppBaseExpansion = basePartialBegin(cppExpansion);

	auto mergedMetaDef = xMacroMetaDefinition(hBaseExpansion->definition());
	if (!mergedMetaDef)
	{
		auto hBaseMetaDef = standardMetaDefinitions_.metaDefinition(hBaseExpansion->definition());
		auto cppBaseMetaDef = standardMetaDefinitions_.metaDefinition(cppBaseExpansion->definition());

		mergedMetaDef = hBaseMetaDef->clone();
		mergedMetaDef->setName(macroDefinitions_.definitionName(hBaseExpansion->definition()));
		xMacrometaDefinitions_.insert(macroDefinitions_.definitionName(hBaseExpansion->definition()), mergedMetaDef);

		for (auto cppArg : *cppBaseMetaDef->arguments())
			if (!NodeHelpers::findDeclaration(mergedMetaDef->arguments(), cppArg->name()))
				mergedMetaDef->arguments()->append(cppArg->clone());

		/* assumptions:
		 * - the context of hBaseMetaDef is a Module
		 * - the context module of hBaseMetaDef contains exactly one class
		 * - the context of cppBaseMetaDef is a Class
		 * - the merged MetaDefinition is correct if we merge those 2 classes
		 */
		auto mergedClass = DCast<OOModel::Class>(DCast<OOModel::Module>(mergedMetaDef->context())->classes()->first());
		auto cppBaseClass = DCast<OOModel::Class>(cppBaseMetaDef->context());

		mergeClasses(mergedClass, cppBaseClass);

		QString metaBindingInputName = "metaBindingInput";
		QString declarationSpliceName = "list1";
		QString statementSpliceName = "list2";

		// add an argument for the input to the MetaBindings
		mergedMetaDef->arguments()->append(new OOModel::FormalMetaArgument{metaBindingInputName});

		// add splices for the MetaBinding results
		mergedClass->metaCalls()->append(new OOModel::ReferenceExpression{declarationSpliceName});
		mergedClass->methods()->last()->items()->append(new OOModel::ExpressionStatement{
																			new OOModel::ReferenceExpression{statementSpliceName}});

		// MetaBinding for declarations splice
		auto declarationsSpliceMetaBinding = new OOModel::MetaBinding{declarationSpliceName};
		declarationsSpliceMetaBinding->setInput(new OOModel::ReferenceExpression{metaBindingInputName});
		mergedMetaDef->metaBindings()->append(declarationsSpliceMetaBinding);

		// MetaBinding for statements splice
		auto statementsSpliceMetaBinding = new OOModel::MetaBinding{statementSpliceName};
		statementsSpliceMetaBinding->setInput(new OOModel::ReferenceExpression{metaBindingInputName});
		mergedMetaDef->metaBindings()->append(statementsSpliceMetaBinding);

		// add the merged MetaDefinition to the tree
		hBaseMetaDef->parent()->replaceChild(hBaseMetaDef, mergedMetaDef);
	}

	NodeHelpers::removeNodeFromParent(standardMetaDefinitions_.metaDefinition(cppExpansion->definition()), true);
	NodeHelpers::removeNodeFromParent(standardMetaDefinitions_.metaDefinition(cppBaseExpansion->definition()), true);

	return mergedMetaDef;
}

MacroExpansion* AllMetaDefinitions::basePartialBegin(MacroExpansion* partialBeginExpansion)
{
	Q_ASSERT(macroDefinitions_.isPartialBegin(partialBeginExpansion->definition()));

	for (auto child : partialBeginExpansion->children())
		if (macroDefinitions_.isPartialBegin(child->definition()))
		 return basePartialBegin(child);

	return partialBeginExpansion;
}

void AllMetaDefinitions::mergeClasses(OOModel::Class* merged, OOModel::Class* mergee)
{
	for (auto metaCall : *mergee->metaCalls())
		merged->metaCalls()->append(metaCall->clone());

	for (auto mergedMethod : *merged->methods())
		for (auto mergeeMethod : *mergee->methods())
		{
			if (mergedMethod->name() == mergeeMethod->name())
			{
				for (auto k = 0; k < mergeeMethod->items()->size(); k++)
					mergedMethod->items()->append(mergeeMethod->items()->at(k)->clone());

				mergedMethod->memberInitializers()->clear();
				for (auto memberInitializer : *mergeeMethod->memberInitializers())
					mergedMethod->memberInitializers()->append(memberInitializer->clone());
			}
		}

}

MacroExpansion* AllMetaDefinitions::partialBeginChild(MacroExpansion* expansion)
{
	for (auto child : expansion->children())
		if (macroDefinitions_.isPartialBegin(child->definition()))
			return child;

	return nullptr;
}

OOModel::MetaDefinition* AllMetaDefinitions::xMacroMetaDefinition(const clang::MacroDirective* md)
{
	QString h = macroDefinitions_.definitionName(md);

	auto it = xMacrometaDefinitions_.find(h);

	return it != xMacrometaDefinitions_.end() ? *it : nullptr;
}

MacroExpansion* AllMetaDefinitions::matchingXMacroExpansion(Model::Node* node)
{
	if (auto metaCall = DCast<OOModel::MetaCallExpression>(node))
		for (auto expansion : macroExpansions_.expansions())
			if (!expansion->xMacroChildren().empty())
				if (expansion->metaCall() == metaCall)
					return expansion;

	for (auto child : node->children())
		if (auto expansion = matchingXMacroExpansion(child))
			return expansion;

	return nullptr;
}

}
<|endoftext|>