llvm-project/llvm/lib/VMCore/AutoUpgrade.cpp

287 lines
10 KiB
C++

//===-- 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)) {
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 {
Value *Cast = new CastInst(Arg, Ty, "autoupgrade_cast", InsertBefore);
return Cast;
}
}
// 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 CastInst(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;
}