| //===-- AutoUpgrade.cpp - Implement auto-upgrade helper functions ---------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file was developed by Reid Spencer and is distributed under the |
| // University of Illinois Open Source License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements the auto-upgrade helper functions |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Assembly/AutoUpgrade.h" |
| #include "llvm/Constants.h" |
| #include "llvm/DerivedTypes.h" |
| #include "llvm/Function.h" |
| #include "llvm/Module.h" |
| #include "llvm/Instructions.h" |
| #include "llvm/Intrinsics.h" |
| #include "llvm/SymbolTable.h" |
| #include <iostream> |
| using namespace llvm; |
| |
| static Function *getUpgradedUnaryFn(Function *F) { |
| const std::string &Name = F->getName(); |
| Module *M = F->getParent(); |
| switch (F->getReturnType()->getTypeID()) { |
| default: return 0; |
| case Type::UByteTyID: |
| case Type::SByteTyID: |
| return M->getOrInsertFunction(Name+".i8", |
| Type::UByteTy, Type::UByteTy, NULL); |
| case Type::UShortTyID: |
| case Type::ShortTyID: |
| return M->getOrInsertFunction(Name+".i16", |
| Type::UShortTy, Type::UShortTy, NULL); |
| case Type::UIntTyID: |
| case Type::IntTyID: |
| return M->getOrInsertFunction(Name+".i32", |
| Type::UIntTy, Type::UIntTy, NULL); |
| case Type::ULongTyID: |
| case Type::LongTyID: |
| return M->getOrInsertFunction(Name+".i64", |
| Type::ULongTy, Type::ULongTy, NULL); |
| case Type::FloatTyID: |
| return M->getOrInsertFunction(Name+".f32", |
| Type::FloatTy, Type::FloatTy, NULL); |
| case Type::DoubleTyID: |
| return M->getOrInsertFunction(Name+".f64", |
| Type::DoubleTy, Type::DoubleTy, NULL); |
| } |
| } |
| |
| static Function *getUpgradedIntrinsic(Function *F) { |
| // If there's no function, we can't get the argument type. |
| if (!F) return 0; |
| |
| // Get the Function's name. |
| const std::string& Name = F->getName(); |
| |
| // Quickly eliminate it, if it's not a candidate. |
| if (Name.length() <= 8 || Name[0] != 'l' || Name[1] != 'l' || |
| Name[2] != 'v' || Name[3] != 'm' || Name[4] != '.') |
| return 0; |
| |
| Module *M = F->getParent(); |
| switch (Name[5]) { |
| default: break; |
| case 'b': |
| if (Name == "llvm.bswap") return getUpgradedUnaryFn(F); |
| break; |
| case 'c': |
| if (Name == "llvm.ctpop" || Name == "llvm.ctlz" || Name == "llvm.cttz") |
| return getUpgradedUnaryFn(F); |
| break; |
| case 'd': |
| if (Name == "llvm.dbg.stoppoint") { |
| PointerType *ESP = |
| PointerType::get(StructType::get(std::vector<const Type*>())); |
| if (F->getReturnType() != Type::VoidTy || |
| F->getFunctionType()->getParamType(2) != ESP) { |
| return M->getOrInsertFunction(Name, Type::VoidTy, |
| Type::UIntTy, Type::UIntTy, ESP, NULL); |
| } |
| } else if (Name == "llvm.dbg.func.start") { |
| PointerType *ESP = |
| PointerType::get(StructType::get(std::vector<const Type*>())); |
| if (F->getReturnType() != Type::VoidTy || |
| F->getFunctionType()->getParamType(0) != ESP) { |
| return M->getOrInsertFunction(Name, Type::VoidTy, ESP, NULL); |
| } |
| } else if (Name == "llvm.dbg.region.start") { |
| PointerType *ESP = |
| PointerType::get(StructType::get(std::vector<const Type*>())); |
| if (F->getReturnType() != Type::VoidTy || |
| F->getFunctionType()->getParamType(0) != ESP) { |
| return M->getOrInsertFunction(Name, Type::VoidTy, ESP, NULL); |
| } |
| } else if (Name == "llvm.dbg.region.end") { |
| PointerType *ESP = |
| PointerType::get(StructType::get(std::vector<const Type*>())); |
| if (F->getReturnType() != Type::VoidTy || |
| F->getFunctionType()->getParamType(0) != ESP) { |
| return M->getOrInsertFunction(Name, Type::VoidTy, ESP, NULL); |
| } |
| } else if (Name == "llvm.dbg.declare") { |
| PointerType *ESP = |
| PointerType::get(StructType::get(std::vector<const Type*>())); |
| if (F->getReturnType() != Type::VoidTy || |
| F->getFunctionType()->getParamType(0) != ESP || |
| F->getFunctionType()->getParamType(1) != ESP) { |
| return M->getOrInsertFunction(Name, Type::VoidTy, ESP, ESP, NULL); |
| } |
| } |
| break; |
| case 'i': |
| if (Name == "llvm.isunordered" && F->arg_begin() != F->arg_end()) { |
| if (F->arg_begin()->getType() == Type::FloatTy) |
| return M->getOrInsertFunction(Name+".f32", F->getFunctionType()); |
| if (F->arg_begin()->getType() == Type::DoubleTy) |
| return M->getOrInsertFunction(Name+".f64", F->getFunctionType()); |
| } |
| break; |
| case 'm': |
| if (Name == "llvm.memcpy" || Name == "llvm.memset" || |
| Name == "llvm.memmove") { |
| if (F->getFunctionType()->getParamType(2) == Type::UIntTy || |
| F->getFunctionType()->getParamType(2) == Type::IntTy) |
| return M->getOrInsertFunction(Name+".i32", Type::VoidTy, |
| PointerType::get(Type::SByteTy), |
| F->getFunctionType()->getParamType(1), |
| Type::UIntTy, Type::UIntTy, NULL); |
| if (F->getFunctionType()->getParamType(2) == Type::ULongTy || |
| F->getFunctionType()->getParamType(2) == Type::LongTy) |
| return M->getOrInsertFunction(Name+".i64", Type::VoidTy, |
| PointerType::get(Type::SByteTy), |
| F->getFunctionType()->getParamType(1), |
| Type::ULongTy, Type::UIntTy, NULL); |
| } |
| break; |
| case 's': |
| if (Name == "llvm.sqrt") |
| return getUpgradedUnaryFn(F); |
| break; |
| } |
| return 0; |
| } |
| |
| // Occasionally upgraded function call site arguments need to be permutated to |
| // some new order. The result of getArgumentPermutation is an array of size |
| // F->getFunctionType()getNumParams() indicating the new operand order. A value |
| // of zero in the array indicates replacing with UndefValue for the arg type. |
| // NULL is returned if there is no permutation. It's assumed that the function |
| // name is in the form "llvm.?????" |
| static unsigned *getArgumentPermutation(Function* Fn, Function* NewFn) { |
| const std::string& Name = Fn->getName(); |
| unsigned N = Fn->getFunctionType()->getNumParams(); |
| unsigned M = NewFn->getFunctionType()->getNumParams(); |
| |
| switch (Name[5]) { |
| case 'd': |
| if (Name == "llvm.dbg.stoppoint") { |
| static unsigned Permutation[] = { 2, 3, 4 }; |
| assert(M == (sizeof(Permutation) / sizeof(unsigned)) && |
| "Permutation is wrong length"); |
| if (N == 4) return Permutation; |
| } else if (Name == "llvm.dbg.region.start") { |
| static unsigned Permutation[] = { 0 }; |
| assert(M == (sizeof(Permutation) / sizeof(unsigned)) && |
| "Permutation is wrong length"); |
| if (N == 0) return Permutation; |
| } else if (Name == "llvm.dbg.region.end") { |
| static unsigned Permutation[] = { 0 }; |
| assert(M == (sizeof(Permutation) / sizeof(unsigned)) && |
| "Permutation is wrong length"); |
| if (N == 0) return Permutation; |
| } else if (Name == "llvm.dbg.declare") { |
| static unsigned Permutation[] = { 0, 0 }; |
| assert(M == (sizeof(Permutation) / sizeof(unsigned)) && |
| "Permutation is wrong length"); |
| if (N == 0) return Permutation; |
| } |
| break; |
| } |
| return NULL; |
| } |
| |
| // UpgradeIntrinsicFunction - Convert overloaded intrinsic function names to |
| // their non-overloaded variants by appending the appropriate suffix based on |
| // the argument types. |
| Function *llvm::UpgradeIntrinsicFunction(Function* F) { |
| // See if its one of the name's we're interested in. |
| if (Function *R = getUpgradedIntrinsic(F)) { |
| if (R->getName() != F->getName()) |
| std::cerr << "WARNING: change " << F->getName() << " to " |
| << R->getName() << "\n"; |
| return R; |
| } |
| return 0; |
| } |
| |
| // CastArg - Perform the appropriate cast of an upgraded argument. |
| // |
| static Value *CastArg(Value *Arg, const Type *Ty, Instruction *InsertBefore) { |
| if (Constant *C = dyn_cast<Constant>(Arg)) { |
| return ConstantExpr::getCast(C, Ty); |
| } else { |
| return CastInst::createInferredCast(Arg, Ty, "autoupgrade_cast", |
| InsertBefore); |
| } |
| } |
| |
| // UpgradeIntrinsicCall - In the BC reader, change a call to an intrinsic to be |
| // a call to an upgraded intrinsic. We may have to permute the order or promote |
| // some arguments with a cast. |
| void llvm::UpgradeIntrinsicCall(CallInst *CI, Function *NewFn) { |
| Function *F = CI->getCalledFunction(); |
| |
| const FunctionType *NewFnTy = NewFn->getFunctionType(); |
| std::vector<Value*> Oprnds; |
| |
| unsigned *Permutation = getArgumentPermutation(F, NewFn); |
| unsigned N = NewFnTy->getNumParams(); |
| |
| if (Permutation) { |
| for (unsigned i = 0; i != N; ++i) { |
| unsigned p = Permutation[i]; |
| |
| if (p) { |
| Value *V = CI->getOperand(p); |
| if (V->getType() != NewFnTy->getParamType(i)) |
| V = CastArg(V, NewFnTy->getParamType(i), CI); |
| Oprnds.push_back(V); |
| } else |
| Oprnds.push_back(UndefValue::get(NewFnTy->getParamType(i))); |
| } |
| } else if (N) { |
| assert(N == (CI->getNumOperands() - 1) && |
| "Upgraded function needs permutation"); |
| for (unsigned i = 0; i != N; ++i) { |
| Value *V = CI->getOperand(i + 1); |
| if (V->getType() != NewFnTy->getParamType(i)) |
| V = CastArg(V, NewFnTy->getParamType(i), CI); |
| Oprnds.push_back(V); |
| } |
| } |
| |
| bool NewIsVoid = NewFn->getReturnType() == Type::VoidTy; |
| |
| CallInst *NewCI = new CallInst(NewFn, Oprnds, |
| NewIsVoid ? "" : CI->getName(), |
| CI); |
| NewCI->setTailCall(CI->isTailCall()); |
| NewCI->setCallingConv(CI->getCallingConv()); |
| |
| if (!CI->use_empty()) { |
| if (NewIsVoid) { |
| CI->replaceAllUsesWith(UndefValue::get(CI->getType())); |
| } else { |
| Instruction *RetVal = NewCI; |
| |
| if (F->getReturnType() != NewFn->getReturnType()) { |
| RetVal = |
| new BitCastInst(NewCI, F->getReturnType(), NewCI->getName(), CI); |
| NewCI->moveBefore(RetVal); |
| } |
| |
| CI->replaceAllUsesWith(RetVal); |
| } |
| } |
| CI->eraseFromParent(); |
| } |
| |
| bool llvm::UpgradeCallsToIntrinsic(Function* F) { |
| if (Function* NewFn = UpgradeIntrinsicFunction(F)) { |
| for (Value::use_iterator UI = F->use_begin(), UE = F->use_end(); |
| UI != UE; ) { |
| if (CallInst* CI = dyn_cast<CallInst>(*UI++)) |
| UpgradeIntrinsicCall(CI, NewFn); |
| } |
| if (NewFn != F) |
| F->eraseFromParent(); |
| return true; |
| } |
| return false; |
| } |