| @@ -0,0 +1,206 @@ | |||
| //===- ExpandByVal.cpp - Expand out use of "byval" and "sret" attributes---===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This pass expands out by-value passing of structs as arguments and | |||
| // return values. In LLVM IR terms, it expands out the "byval" and | |||
| // "sret" function argument attributes. | |||
| // | |||
| // The semantics of the "byval" attribute are that the callee function | |||
| // gets a private copy of the pointed-to argument that it is allowed | |||
| // to modify. In implementing this, we have a choice between making | |||
| // the caller responsible for making the copy or making the callee | |||
| // responsible for making the copy. We choose the former, because | |||
| // this matches how the normal native calling conventions work, and | |||
| // because it often allows the caller to write struct contents | |||
| // directly into the stack slot that it passes the callee, without an | |||
| // additional copy. | |||
| // | |||
| // Note that this pass does not attempt to modify functions that pass | |||
| // structs by value without using "byval" or "sret", such as: | |||
| // | |||
| // define %struct.X @func() ; struct return | |||
| // define void @func(%struct.X %arg) ; struct arg | |||
| // | |||
| // The pass only handles functions such as: | |||
| // | |||
| // define void @func(%struct.X* sret %result_buffer) ; struct return | |||
| // define void @func(%struct.X* byval %ptr_to_arg) ; struct arg | |||
| // | |||
| // This is because PNaCl Clang generates the latter and not the former. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/IR/Attributes.h" | |||
| #include "llvm/IR/DataLayout.h" | |||
| #include "llvm/IR/Function.h" | |||
| #include "llvm/IR/IRBuilder.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| // This is a ModulePass so that it can strip attributes from | |||
| // declared functions as well as defined functions. | |||
| class ExpandByVal : public ModulePass { | |||
| public: | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandByVal() : ModulePass(ID) { | |||
| initializeExpandByValPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| virtual bool runOnModule(Module &M); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandByVal::ID = 0; | |||
| INITIALIZE_PASS(ExpandByVal, "expand-byval", | |||
| "Expand out by-value passing of structs", | |||
| false, false) | |||
|
|
|||
| // removeAttribute() currently does not work on Attribute::Alignment | |||
| // (it fails with an assertion error), so we have to take a more | |||
| // convoluted route to removing this attribute by recreating the | |||
| // AttributeSet. | |||
| AttributeSet RemoveAttrs(LLVMContext &Context, AttributeSet Attrs) { | |||
| SmallVector<AttributeSet, 8> AttrList; | |||
| for (unsigned Slot = 0; Slot < Attrs.getNumSlots(); ++Slot) { | |||
| unsigned Index = Attrs.getSlotIndex(Slot); | |||
| AttrBuilder AB; | |||
| for (AttributeSet::iterator Attr = Attrs.begin(Slot), E = Attrs.end(Slot); | |||
| Attr != E; ++Attr) { | |||
| if (Attr->isEnumAttribute() && | |||
| Attr->getKindAsEnum() != Attribute::ByVal && | |||
| Attr->getKindAsEnum() != Attribute::StructRet) { | |||
| AB.addAttribute(*Attr); | |||
| } | |||
| // IR semantics require that ByVal implies NoAlias. However, IR | |||
| // semantics do not require StructRet to imply NoAlias. For | |||
| // example, a global variable address can be passed as a | |||
| // StructRet argument, although Clang does not do so and Clang | |||
| // explicitly adds NoAlias to StructRet arguments. | |||
| if (Attr->isEnumAttribute() && | |||
| Attr->getKindAsEnum() == Attribute::ByVal) { | |||
| AB.addAttribute(Attribute::get(Context, Attribute::NoAlias)); | |||
| } | |||
| } | |||
| AttrList.push_back(AttributeSet::get(Context, Index, AB)); | |||
| } | |||
| return AttributeSet::get(Context, AttrList); | |||
| } | |||
|
|
|||
| // ExpandCall() can take a CallInst or an InvokeInst. It returns | |||
| // whether the instruction was modified. | |||
| template <class InstType> | |||
| static bool ExpandCall(DataLayout *DL, InstType *Call) { | |||
| bool Modify = false; | |||
| AttributeSet Attrs = Call->getAttributes(); | |||
| for (unsigned ArgIdx = 0; ArgIdx < Call->getNumArgOperands(); ++ArgIdx) { | |||
| unsigned AttrIdx = ArgIdx + 1; | |||
|
|
|||
| if (Attrs.hasAttribute(AttrIdx, Attribute::StructRet)) | |||
| Modify = true; | |||
|
|
|||
| if (Attrs.hasAttribute(AttrIdx, Attribute::ByVal)) { | |||
| Modify = true; | |||
|
|
|||
| Value *ArgPtr = Call->getArgOperand(ArgIdx); | |||
| Type *ArgType = ArgPtr->getType()->getPointerElementType(); | |||
| ConstantInt *ArgSize = ConstantInt::get( | |||
| Call->getContext(), APInt(64, DL->getTypeStoreSize(ArgType))); | |||
| // In principle, using the alignment from the argument attribute | |||
| // should be enough. However, Clang is not emitting this | |||
| // attribute for PNaCl. LLVM alloca instructions do not use the | |||
| // ABI alignment of the type, so this must be specified | |||
| // explicitly. | |||
| // See https://code.google.com/p/nativeclient/issues/detail?id=3403 | |||
| // | |||
| // Note that the parameter may have no alignment, but we have | |||
| // more useful information from the type which we can use here | |||
| // -- 0 in the parameter means no alignment is specified there, | |||
| // so it has default alignment, but in memcpy 0 means | |||
| // pessimistic alignment, the same as 1. | |||
| unsigned Alignment = | |||
| std::max(Attrs.getParamAlignment(AttrIdx), | |||
| DL->getABITypeAlignment(ArgType)); | |||
|
|
|||
| // Make a copy of the byval argument. | |||
| Instruction *CopyBuf = new AllocaInst(ArgType, 0, Alignment, | |||
| ArgPtr->getName() + ".byval_copy"); | |||
| Function *Func = Call->getParent()->getParent(); | |||
| Func->getEntryBlock().getInstList().push_front(CopyBuf); | |||
| IRBuilder<> Builder(Call); | |||
| Builder.CreateLifetimeStart(CopyBuf, ArgSize); | |||
| // Using the argument's alignment attribute for the memcpy | |||
| // should be OK because the LLVM Language Reference says that | |||
| // the alignment attribute specifies "the alignment of the stack | |||
| // slot to form and the known alignment of the pointer specified | |||
| // to the call site". | |||
| Instruction *MemCpy = Builder.CreateMemCpy(CopyBuf, ArgPtr, ArgSize, | |||
| Alignment); | |||
| MemCpy->setDebugLoc(Call->getDebugLoc()); | |||
|
|
|||
| Call->setArgOperand(ArgIdx, CopyBuf); | |||
|
|
|||
| // Mark the argument copy as unused using llvm.lifetime.end. | |||
| if (isa<CallInst>(Call)) { | |||
| BasicBlock::iterator It = BasicBlock::iterator(Call); | |||
| Builder.SetInsertPoint(&*(++It)); | |||
| Builder.CreateLifetimeEnd(CopyBuf, ArgSize); | |||
| } else if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Call)) { | |||
| Builder.SetInsertPoint(&*Invoke->getNormalDest()->getFirstInsertionPt()); | |||
| Builder.CreateLifetimeEnd(CopyBuf, ArgSize); | |||
| Builder.SetInsertPoint(&*Invoke->getUnwindDest()->getFirstInsertionPt()); | |||
| Builder.CreateLifetimeEnd(CopyBuf, ArgSize); | |||
| } | |||
| } | |||
| } | |||
| if (Modify) { | |||
| Call->setAttributes(RemoveAttrs(Call->getContext(), Attrs)); | |||
|
|
|||
| if (CallInst *CI = dyn_cast<CallInst>(Call)) { | |||
| // This is no longer a tail call because the callee references | |||
| // memory alloca'd by the caller. | |||
| CI->setTailCall(false); | |||
| } | |||
| } | |||
| return Modify; | |||
| } | |||
|
|
|||
| bool ExpandByVal::runOnModule(Module &M) { | |||
| bool Modified = false; | |||
| DataLayout DL(&M); | |||
|
|
|||
| for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) { | |||
| AttributeSet NewAttrs = RemoveAttrs(Func->getContext(), | |||
| Func->getAttributes()); | |||
| Modified |= (NewAttrs != Func->getAttributes()); | |||
| Func->setAttributes(NewAttrs); | |||
|
|
|||
| for (Function::iterator BB = Func->begin(), E = Func->end(); | |||
| BB != E; ++BB) { | |||
| for (BasicBlock::iterator Inst = BB->begin(), E = BB->end(); | |||
| Inst != E; ++Inst) { | |||
| if (CallInst *Call = dyn_cast<CallInst>(Inst)) { | |||
| Modified |= ExpandCall(&DL, Call); | |||
| } else if (InvokeInst *Call = dyn_cast<InvokeInst>(Inst)) { | |||
| Modified |= ExpandCall(&DL, Call); | |||
| } | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| return Modified; | |||
| } | |||
|
|
|||
| ModulePass *llvm::createExpandByValPass() { | |||
| return new ExpandByVal(); | |||
| } | |||
| @@ -0,0 +1,121 @@ | |||
| //===- ExpandConstantExpr.cpp - Convert ConstantExprs to Instructions------===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This pass expands out ConstantExprs into Instructions. | |||
| // | |||
| // Note that this only converts ConstantExprs that are referenced by | |||
| // Instructions. It does not convert ConstantExprs that are used as | |||
| // initializers for global variables. | |||
| // | |||
| // This simplifies the language so that the PNaCl translator does not | |||
| // need to handle ConstantExprs as part of a stable wire format for | |||
| // PNaCl. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include <map> | |||
|
|
|||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/Function.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| static bool expandInstruction(Instruction *Inst); | |||
|
|
|||
| namespace { | |||
| // This is a FunctionPass because our handling of PHI nodes means | |||
| // that our modifications may cross BasicBlocks. | |||
| struct ExpandConstantExpr : public FunctionPass { | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandConstantExpr() : FunctionPass(ID) { | |||
| initializeExpandConstantExprPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| virtual bool runOnFunction(Function &Func); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandConstantExpr::ID = 0; | |||
| INITIALIZE_PASS(ExpandConstantExpr, "expand-constant-expr", | |||
| "Expand out ConstantExprs into Instructions", | |||
| false, false) | |||
|
|
|||
| static Value *expandConstantExpr(Instruction *InsertPt, ConstantExpr *Expr) { | |||
| Instruction *NewInst = Expr->getAsInstruction(); | |||
| NewInst->insertBefore(InsertPt); | |||
| NewInst->setName("expanded"); | |||
| expandInstruction(NewInst); | |||
| return NewInst; | |||
| } | |||
|
|
|||
| // XXX Emscripten: Utilities for illegal expressions. | |||
| static bool isIllegal(Type *T) { | |||
| if (!T->isIntegerTy()) return false; | |||
| unsigned Bits = T->getIntegerBitWidth(); | |||
| // we need to expand out not just 64-bit and larger values, but also i24s, so PromoteIntegers can process them | |||
| return Bits != 1 && Bits != 8 && Bits != 16 && Bits != 32; | |||
| } | |||
| static bool ContainsIllegalTypes(const Value *Expr) { | |||
| if (isIllegal(Expr->getType())) | |||
| return true; | |||
| if (const User *U = dyn_cast<User>(Expr)) { | |||
| for (User::const_op_iterator I = U->op_begin(), E = U->op_end(); I != E; ++I) { | |||
| if (Constant *C = dyn_cast<Constant>(*I)) { | |||
| if (!isa<GlobalValue>(C) && ContainsIllegalTypes(C)) { | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
|
|
|||
| static bool expandInstruction(Instruction *Inst) { | |||
| // A landingpad can only accept ConstantExprs, so it should remain | |||
| // unmodified. | |||
| if (isa<LandingPadInst>(Inst)) | |||
| return false; | |||
|
|
|||
| bool Modified = false; | |||
| for (unsigned OpNum = 0; OpNum < Inst->getNumOperands(); OpNum++) { | |||
| if (ConstantExpr *Expr = | |||
| dyn_cast<ConstantExpr>(Inst->getOperand(OpNum))) { | |||
| // XXX Emscripten: Only do the expansion of the expression contains | |||
| // illegal types, for now, since we can handle legal ConstantExprs | |||
| // in the backend directly. | |||
| if (ContainsIllegalTypes(Expr)) { | |||
| Modified = true; | |||
| Use *U = &Inst->getOperandUse(OpNum); | |||
| PhiSafeReplaceUses(U, expandConstantExpr(PhiSafeInsertPt(U), Expr)); | |||
| } | |||
| } | |||
| } | |||
| return Modified; | |||
| } | |||
|
|
|||
| bool ExpandConstantExpr::runOnFunction(Function &Func) { | |||
| bool Modified = false; | |||
| for (llvm::Function::iterator BB = Func.begin(), E = Func.end(); | |||
| BB != E; | |||
| ++BB) { | |||
| for (BasicBlock::InstListType::iterator Inst = BB->begin(), E = BB->end(); | |||
| Inst != E; | |||
| ++Inst) { | |||
| Modified |= expandInstruction(&*Inst); | |||
| } | |||
| } | |||
| return Modified; | |||
| } | |||
|
|
|||
| FunctionPass *llvm::createExpandConstantExprPass() { | |||
| return new ExpandConstantExpr(); | |||
| } | |||
| @@ -0,0 +1,154 @@ | |||
| //===- ExpandCtors.cpp - Convert ctors/dtors to concrete arrays -----------===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This pass converts LLVM's special symbols llvm.global_ctors and | |||
| // llvm.global_dtors to concrete arrays, __init_array_start/end and | |||
| // __fini_array_start/end, that are usable by a C library. | |||
| // | |||
| // This pass sorts the contents of global_ctors/dtors according to the | |||
| // priority values they contain and removes the priority values. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include <vector> | |||
|
|
|||
| #include "llvm/Pass.h" | |||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/DerivedTypes.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/IR/TypeBuilder.h" | |||
| #include "llvm/Support/raw_ostream.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| struct ExpandCtors : public ModulePass { | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandCtors() : ModulePass(ID) { | |||
| initializeExpandCtorsPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| virtual bool runOnModule(Module &M); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandCtors::ID = 0; | |||
| INITIALIZE_PASS(ExpandCtors, "nacl-expand-ctors", | |||
| "Hook up constructor and destructor arrays to libc", | |||
| false, false) | |||
|
|
|||
| static void setGlobalVariableValue(Module &M, const char *Name, | |||
| Constant *Value) { | |||
| if (GlobalVariable *Var = M.getNamedGlobal(Name)) { | |||
| if (Var->hasInitializer()) { | |||
| report_fatal_error(std::string("Variable ") + Name + | |||
| " already has an initializer"); | |||
| } | |||
| Var->replaceAllUsesWith(ConstantExpr::getBitCast(Value, Var->getType())); | |||
| Var->eraseFromParent(); | |||
| } | |||
| } | |||
|
|
|||
| struct FuncArrayEntry { | |||
| uint64_t priority; | |||
| Constant *func; | |||
| }; | |||
|
|
|||
| static bool compareEntries(FuncArrayEntry Entry1, FuncArrayEntry Entry2) { | |||
| return Entry1.priority < Entry2.priority; | |||
| } | |||
|
|
|||
| static void readFuncList(GlobalVariable *Array, std::vector<Constant*> *Funcs) { | |||
| if (!Array->hasInitializer()) | |||
| return; | |||
| Constant *Init = Array->getInitializer(); | |||
| ArrayType *Ty = dyn_cast<ArrayType>(Init->getType()); | |||
| if (!Ty) { | |||
| errs() << "Initializer: " << *Array->getInitializer() << "\n"; | |||
| report_fatal_error("ExpandCtors: Initializer is not of array type"); | |||
| } | |||
| if (Ty->getNumElements() == 0) | |||
| return; | |||
| ConstantArray *InitList = dyn_cast<ConstantArray>(Init); | |||
| if (!InitList) { | |||
| errs() << "Initializer: " << *Array->getInitializer() << "\n"; | |||
| report_fatal_error("ExpandCtors: Unexpected initializer ConstantExpr"); | |||
| } | |||
| std::vector<FuncArrayEntry> FuncsToSort; | |||
| for (unsigned Index = 0; Index < InitList->getNumOperands(); ++Index) { | |||
| ConstantStruct *CS = cast<ConstantStruct>(InitList->getOperand(Index)); | |||
| FuncArrayEntry Entry; | |||
| Entry.priority = cast<ConstantInt>(CS->getOperand(0))->getZExtValue(); | |||
| Entry.func = CS->getOperand(1); | |||
| FuncsToSort.push_back(Entry); | |||
| } | |||
|
|
|||
| std::sort(FuncsToSort.begin(), FuncsToSort.end(), compareEntries); | |||
| for (std::vector<FuncArrayEntry>::iterator Iter = FuncsToSort.begin(); | |||
| Iter != FuncsToSort.end(); | |||
| ++Iter) { | |||
| Funcs->push_back(Iter->func); | |||
| } | |||
| } | |||
|
|
|||
| static void defineFuncArray(Module &M, const char *LlvmArrayName, | |||
| const char *StartSymbol, | |||
| const char *EndSymbol) { | |||
| std::vector<Constant*> Funcs; | |||
|
|
|||
| GlobalVariable *Array = M.getNamedGlobal(LlvmArrayName); | |||
| if (Array) { | |||
| readFuncList(Array, &Funcs); | |||
| // No code should be referencing global_ctors/global_dtors, | |||
| // because this symbol is internal to LLVM. | |||
| Array->eraseFromParent(); | |||
| } | |||
|
|
|||
| Type *FuncTy = FunctionType::get(Type::getVoidTy(M.getContext()), false); | |||
| Type *FuncPtrTy = FuncTy->getPointerTo(); | |||
| ArrayType *ArrayTy = ArrayType::get(FuncPtrTy, Funcs.size()); | |||
| GlobalVariable *NewArray = | |||
| new GlobalVariable(M, ArrayTy, /* isConstant= */ true, | |||
| GlobalValue::InternalLinkage, | |||
| ConstantArray::get(ArrayTy, Funcs)); | |||
| setGlobalVariableValue(M, StartSymbol, NewArray); | |||
| // We do this last so that LLVM gives NewArray the name | |||
| // "__{init,fini}_array_start" without adding any suffixes to | |||
| // disambiguate from the original GlobalVariable's name. This is | |||
| // not essential -- it just makes the output easier to understand | |||
| // when looking at symbols for debugging. | |||
| NewArray->setName(StartSymbol); | |||
|
|
|||
| // We replace "__{init,fini}_array_end" with the address of the end | |||
| // of NewArray. This removes the name "__{init,fini}_array_end" | |||
| // from the output, which is not ideal for debugging. Ideally we | |||
| // would convert "__{init,fini}_array_end" to being a GlobalAlias | |||
| // that points to the end of the array. However, unfortunately LLVM | |||
| // does not generate correct code when a GlobalAlias contains a | |||
| // GetElementPtr ConstantExpr. | |||
| Constant *NewArrayEnd = | |||
| ConstantExpr::getGetElementPtr(ArrayTy, NewArray, | |||
| ConstantInt::get(M.getContext(), | |||
| APInt(32, 1))); | |||
| setGlobalVariableValue(M, EndSymbol, NewArrayEnd); | |||
| } | |||
|
|
|||
| bool ExpandCtors::runOnModule(Module &M) { | |||
| defineFuncArray(M, "llvm.global_ctors", | |||
| "__init_array_start", "__init_array_end"); | |||
| defineFuncArray(M, "llvm.global_dtors", | |||
| "__fini_array_start", "__fini_array_end"); | |||
| return true; | |||
| } | |||
|
|
|||
| ModulePass *llvm::createExpandCtorsPass() { | |||
| return new ExpandCtors(); | |||
| } | |||
| @@ -0,0 +1,151 @@ | |||
| //===- ExpandGetElementPtr.cpp - Expand GetElementPtr into arithmetic------===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This pass expands out GetElementPtr instructions into ptrtoint, | |||
| // inttoptr and arithmetic instructions. | |||
| // | |||
| // This simplifies the language so that the PNaCl translator does not | |||
| // need to handle GetElementPtr and struct types as part of a stable | |||
| // wire format for PNaCl. | |||
| // | |||
| // Note that we drop the "inbounds" attribute of GetElementPtr. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/IR/BasicBlock.h" | |||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/DataLayout.h" | |||
| #include "llvm/IR/Function.h" | |||
| #include "llvm/IR/InstrTypes.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/IR/Type.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| class ExpandGetElementPtr : public BasicBlockPass { | |||
| public: | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandGetElementPtr() : BasicBlockPass(ID) { | |||
| initializeExpandGetElementPtrPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| virtual bool runOnBasicBlock(BasicBlock &BB); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandGetElementPtr::ID = 0; | |||
| INITIALIZE_PASS(ExpandGetElementPtr, "expand-getelementptr", | |||
| "Expand out GetElementPtr instructions into arithmetic", | |||
| false, false) | |||
|
|
|||
| static Value *CastToPtrSize(Value *Val, Instruction *InsertPt, | |||
| const DebugLoc &Debug, Type *PtrType) { | |||
| unsigned ValSize = Val->getType()->getIntegerBitWidth(); | |||
| unsigned PtrSize = PtrType->getIntegerBitWidth(); | |||
| if (ValSize == PtrSize) | |||
| return Val; | |||
| Instruction *Inst; | |||
| if (ValSize > PtrSize) { | |||
| Inst = new TruncInst(Val, PtrType, "gep_trunc", InsertPt); | |||
| } else { | |||
| // GEP indexes must be sign-extended. | |||
| Inst = new SExtInst(Val, PtrType, "gep_sext", InsertPt); | |||
| } | |||
| Inst->setDebugLoc(Debug); | |||
| return Inst; | |||
| } | |||
|
|
|||
| static void FlushOffset(Instruction **Ptr, uint64_t *CurrentOffset, | |||
| Instruction *InsertPt, const DebugLoc &Debug, | |||
| Type *PtrType) { | |||
| if (*CurrentOffset) { | |||
| *Ptr = BinaryOperator::Create(Instruction::Add, *Ptr, | |||
| ConstantInt::get(PtrType, *CurrentOffset), | |||
| "gep", InsertPt); | |||
| (*Ptr)->setDebugLoc(Debug); | |||
| *CurrentOffset = 0; | |||
| } | |||
| } | |||
|
|
|||
| static void ExpandGEP(GetElementPtrInst *GEP, DataLayout *DL, Type *PtrType) { | |||
| const DebugLoc &Debug = GEP->getDebugLoc(); | |||
| Instruction *Ptr = new PtrToIntInst(GEP->getPointerOperand(), PtrType, | |||
| "gep_int", GEP); | |||
| Ptr->setDebugLoc(Debug); | |||
|
|
|||
| Type *CurrentTy = GEP->getPointerOperand()->getType(); | |||
| // We do some limited constant folding ourselves. An alternative | |||
| // would be to generate verbose, unfolded output (e.g. multiple | |||
| // adds; adds of zero constants) and use a later pass such as | |||
| // "-instcombine" to clean that up. However, "-instcombine" can | |||
| // reintroduce GetElementPtr instructions. | |||
| uint64_t CurrentOffset = 0; | |||
|
|
|||
| for (GetElementPtrInst::op_iterator Op = GEP->op_begin() + 1; | |||
| Op != GEP->op_end(); | |||
| ++Op) { | |||
| Value *Index = *Op; | |||
| if (StructType *StTy = dyn_cast<StructType>(CurrentTy)) { | |||
| uint64_t Field = cast<ConstantInt>(Op)->getZExtValue(); | |||
| CurrentTy = StTy->getElementType(Field); | |||
| CurrentOffset += DL->getStructLayout(StTy)->getElementOffset(Field); | |||
| } else { | |||
| CurrentTy = cast<SequentialType>(CurrentTy)->getElementType(); | |||
| uint64_t ElementSize = DL->getTypeAllocSize(CurrentTy); | |||
| if (ConstantInt *C = dyn_cast<ConstantInt>(Index)) { | |||
| CurrentOffset += C->getSExtValue() * ElementSize; | |||
| } else { | |||
| FlushOffset(&Ptr, &CurrentOffset, GEP, Debug, PtrType); | |||
| Index = CastToPtrSize(Index, GEP, Debug, PtrType); | |||
| if (ElementSize != 1) { | |||
| Index = CopyDebug( | |||
| BinaryOperator::Create(Instruction::Mul, Index, | |||
| ConstantInt::get(PtrType, ElementSize), | |||
| "gep_array", GEP), | |||
| GEP); | |||
| } | |||
| Ptr = BinaryOperator::Create(Instruction::Add, Ptr, | |||
| Index, "gep", GEP); | |||
| Ptr->setDebugLoc(Debug); | |||
| } | |||
| } | |||
| } | |||
| FlushOffset(&Ptr, &CurrentOffset, GEP, Debug, PtrType); | |||
|
|
|||
| assert(CurrentTy == GEP->getResultElementType()); | |||
| Instruction *Result = new IntToPtrInst(Ptr, GEP->getType(), "", GEP); | |||
| Result->setDebugLoc(Debug); | |||
| Result->takeName(GEP); | |||
| GEP->replaceAllUsesWith(Result); | |||
| GEP->eraseFromParent(); | |||
| } | |||
|
|
|||
| bool ExpandGetElementPtr::runOnBasicBlock(BasicBlock &BB) { | |||
| bool Modified = false; | |||
| DataLayout DL(BB.getParent()->getParent()); | |||
| Type *PtrType = DL.getIntPtrType(BB.getContext()); | |||
|
|
|||
| for (BasicBlock::InstListType::iterator Iter = BB.begin(); | |||
| Iter != BB.end(); ) { | |||
| Instruction *Inst = &*Iter++; | |||
| if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Inst)) { | |||
| Modified = true; | |||
| ExpandGEP(GEP, &DL, PtrType); | |||
| } | |||
| } | |||
| return Modified; | |||
| } | |||
|
|
|||
| BasicBlockPass *llvm::createExpandGetElementPtrPass() { | |||
| return new ExpandGetElementPtr(); | |||
| } | |||
| @@ -0,0 +1,152 @@ | |||
| //===- ExpandIndirectBr.cpp - Expand out indirectbr and blockaddress-------===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This pass expands out indirectbr instructions and blockaddress | |||
| // ConstantExprs, which are not currently supported in PNaCl's stable | |||
| // ABI. indirectbr is used to implement computed gotos (a GNU | |||
| // extension to C). This pass replaces indirectbr instructions with | |||
| // switch instructions. | |||
| // | |||
| // The resulting use of switches might not be as fast as the original | |||
| // indirectbrs. If you are compiling a program that has a | |||
| // compile-time option for using computed gotos, it's possible that | |||
| // the program will run faster with the option turned off than with | |||
| // using computed gotos + ExpandIndirectBr (for example, if the | |||
| // program does extra work to take advantage of computed gotos). | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/ADT/DenseMap.h" | |||
| #include "llvm/ADT/DenseSet.h" | |||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| // This is a ModulePass so that it can expand out blockaddress | |||
| // ConstantExprs inside global variable initializers. | |||
| class ExpandIndirectBr : public ModulePass { | |||
| public: | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandIndirectBr() : ModulePass(ID) { | |||
| initializeExpandIndirectBrPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| virtual bool runOnModule(Module &M); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandIndirectBr::ID = 0; | |||
| INITIALIZE_PASS(ExpandIndirectBr, "expand-indirectbr", | |||
| "Expand out indirectbr and blockaddress (computed gotos)", | |||
| false, false) | |||
|
|
|||
| static bool convertFunction(Function *Func) { | |||
| bool Changed = false; | |||
| IntegerType *I32 = Type::getInt32Ty(Func->getContext()); | |||
|
|
|||
| // Skip zero in case programs treat a null pointer as special. | |||
| uint32_t NextNum = 1; | |||
| DenseMap<BasicBlock *, ConstantInt *> LabelNums; | |||
| BasicBlock *DefaultBB = NULL; | |||
|
|
|||
| // Replace each indirectbr with a switch. | |||
| // | |||
| // If there are multiple indirectbr instructions in the function, | |||
| // this could be expensive. While an indirectbr is usually | |||
| // converted to O(1) machine instructions, the switch we generate | |||
| // here will be O(n) in the number of target labels. | |||
| // | |||
| // However, Clang usually generates just a single indirectbr per | |||
| // function anyway when compiling C computed gotos. | |||
| // | |||
| // We could try to generate one switch to handle all the indirectbr | |||
| // instructions in the function, but that would be complicated to | |||
| // implement given that variables that are live at one indirectbr | |||
| // might not be live at others. | |||
| for (llvm::Function::iterator BB = Func->begin(), E = Func->end(); | |||
| BB != E; ++BB) { | |||
| if (IndirectBrInst *Br = dyn_cast<IndirectBrInst>(BB->getTerminator())) { | |||
| Changed = true; | |||
|
|
|||
| if (!DefaultBB) { | |||
| DefaultBB = BasicBlock::Create(Func->getContext(), | |||
| "indirectbr_default", Func); | |||
| new UnreachableInst(Func->getContext(), DefaultBB); | |||
| } | |||
|
|
|||
| // An indirectbr can list the same target block multiple times. | |||
| // Keep track of the basic blocks we've handled to avoid adding | |||
| // the same case multiple times. | |||
| DenseSet<BasicBlock *> BlocksSeen; | |||
|
|
|||
| Value *Cast = new PtrToIntInst(Br->getAddress(), I32, | |||
| "indirectbr_cast", Br); | |||
| unsigned Count = Br->getNumSuccessors(); | |||
| SwitchInst *Switch = SwitchInst::Create(Cast, DefaultBB, Count, Br); | |||
| for (unsigned I = 0; I < Count; ++I) { | |||
| BasicBlock *Dest = Br->getSuccessor(I); | |||
| if (!BlocksSeen.insert(Dest).second) { | |||
| // Remove duplicated entries from phi nodes. | |||
| for (BasicBlock::iterator Inst = Dest->begin(); ; ++Inst) { | |||
| PHINode *Phi = dyn_cast<PHINode>(Inst); | |||
| if (!Phi) | |||
| break; | |||
| Phi->removeIncomingValue(Br->getParent()); | |||
| } | |||
| continue; | |||
| } | |||
| ConstantInt *Val; | |||
| if (LabelNums.count(Dest) == 0) { | |||
| Val = ConstantInt::get(I32, NextNum++); | |||
| LabelNums[Dest] = Val; | |||
|
|
|||
| BlockAddress *BA = BlockAddress::get(Func, Dest); | |||
| Value *ValAsPtr = ConstantExpr::getIntToPtr(Val, BA->getType()); | |||
| BA->replaceAllUsesWith(ValAsPtr); | |||
| BA->destroyConstant(); | |||
| } else { | |||
| Val = LabelNums[Dest]; | |||
| } | |||
| Switch->addCase(Val, Br->getSuccessor(I)); | |||
| } | |||
| Br->eraseFromParent(); | |||
| } | |||
| } | |||
|
|
|||
| // If there are any blockaddresses that are never used by an | |||
| // indirectbr, replace them with dummy values. | |||
| SmallVector<Value *, 20> Users(Func->user_begin(), Func->user_end()); | |||
| for (auto U : Users) { | |||
| if (BlockAddress *BA = dyn_cast<BlockAddress>(U)) { | |||
| Changed = true; | |||
| Value *DummyVal = ConstantExpr::getIntToPtr(ConstantInt::get(I32, ~0L), | |||
| BA->getType()); | |||
| BA->replaceAllUsesWith(DummyVal); | |||
| BA->destroyConstant(); | |||
| } | |||
| } | |||
| return Changed; | |||
| } | |||
|
|
|||
| bool ExpandIndirectBr::runOnModule(Module &M) { | |||
| bool Changed = false; | |||
| for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) { | |||
| Changed |= convertFunction(&*Func); | |||
| } | |||
| return Changed; | |||
| } | |||
|
|
|||
| ModulePass *llvm::createExpandIndirectBrPass() { | |||
| return new ExpandIndirectBr(); | |||
| } | |||
| @@ -0,0 +1,100 @@ | |||
| //==- ExpandInsertExtractElement.cpp - Expand vector insert and extract -=// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===------------------------------------------------------------------===// | |||
| // | |||
| // This pass expands insertelement and extractelement instructions with | |||
| // variable indices, which SIMD.js doesn't natively support yet. | |||
| // | |||
| //===------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/ADT/SmallVector.h" | |||
| #include "llvm/IR/Function.h" | |||
| #include "llvm/IR/IRBuilder.h" | |||
| #include "llvm/IR/InstIterator.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
| #include "llvm/Transforms/Utils/Local.h" | |||
| #include <map> | |||
| #include <vector> | |||
|
|
|||
| #include "llvm/Support/raw_ostream.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
|
|
|||
| class ExpandInsertExtractElement : public FunctionPass { | |||
| bool Changed; | |||
|
|
|||
| public: | |||
| static char ID; | |||
| ExpandInsertExtractElement() : FunctionPass(ID) { | |||
| initializeExpandInsertExtractElementPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| bool runOnFunction(Function &F) override; | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandInsertExtractElement::ID = 0; | |||
| INITIALIZE_PASS(ExpandInsertExtractElement, "expand-insert-extract-elements", | |||
| "Expand and lower insert and extract element operations", | |||
| false, false) | |||
|
|
|||
| // Utilities | |||
|
|
|||
| bool ExpandInsertExtractElement::runOnFunction(Function &F) { | |||
| Changed = false; | |||
|
|
|||
| Instruction *Entry = &*F.getEntryBlock().begin(); | |||
| Type *Int32 = Type::getInt32Ty(F.getContext()); | |||
| Constant *Zero = ConstantInt::get(Int32, 0); | |||
| for (inst_iterator I = inst_begin(&F), E = inst_end(&F); I != E; ) { | |||
| Instruction *Inst = &*I++; | |||
|
|
|||
| if (InsertElementInst *III = dyn_cast<InsertElementInst>(Inst)) { | |||
| if (isa<ConstantInt>(III->getOperand(2))) | |||
| continue; | |||
|
|
|||
| Type *AllocaTy = III->getType(); | |||
| Instruction *A = new AllocaInst(AllocaTy, 0, "", Entry); | |||
| CopyDebug(new StoreInst(III->getOperand(0), A, III), III); | |||
|
|
|||
| Value *Idxs[] = { Zero, III->getOperand(2) }; | |||
| Instruction *B = CopyDebug( | |||
| GetElementPtrInst::Create(AllocaTy, A, Idxs, "", III), III); | |||
| CopyDebug(new StoreInst(III->getOperand(1), B, III), III); | |||
|
|
|||
| Instruction *L = CopyDebug(new LoadInst(A, "", III), III); | |||
| III->replaceAllUsesWith(L); | |||
| III->eraseFromParent(); | |||
| } else if (ExtractElementInst *EII = dyn_cast<ExtractElementInst>(Inst)) { | |||
| if (isa<ConstantInt>(EII->getOperand(1))) | |||
| continue; | |||
|
|
|||
| Type *AllocaTy = EII->getOperand(0)->getType(); | |||
| Instruction *A = new AllocaInst(AllocaTy, 0, "", Entry); | |||
| CopyDebug(new StoreInst(EII->getOperand(0), A, EII), EII); | |||
|
|
|||
| Value *Idxs[] = { Zero, EII->getOperand(1) }; | |||
| Instruction *B = CopyDebug( | |||
| GetElementPtrInst::Create(AllocaTy, A, Idxs, "", EII), EII); | |||
| Instruction *L = CopyDebug(new LoadInst(B, "", EII), EII); | |||
| EII->replaceAllUsesWith(L); | |||
| EII->eraseFromParent(); | |||
| } | |||
| } | |||
|
|
|||
| return Changed; | |||
| } | |||
|
|
|||
| FunctionPass *llvm::createExpandInsertExtractElementPass() { | |||
| return new ExpandInsertExtractElement(); | |||
| } | |||
| @@ -0,0 +1,110 @@ | |||
| //===- ExpandShuffleVector.cpp - shufflevector to {insert/extract}element -===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // Replace all shufflevector instructions by insertelement / extractelement. | |||
| // BackendCanonicalize is able to reconstruct the shufflevector. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/IR/BasicBlock.h" | |||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/Instruction.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/IR/Type.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| class ExpandShuffleVector : public BasicBlockPass { | |||
| public: | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandShuffleVector() : BasicBlockPass(ID), M(0) { | |||
| initializeExpandShuffleVectorPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
| using BasicBlockPass::doInitialization; | |||
| bool doInitialization(Module &Mod) override { | |||
| M = &Mod; | |||
| return false; // Unchanged. | |||
| } | |||
| bool runOnBasicBlock(BasicBlock &BB) override; | |||
|
|
|||
| private: | |||
| const Module *M; | |||
| void Expand(ShuffleVectorInst *Shuf, Type *Int32); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandShuffleVector::ID = 0; | |||
| INITIALIZE_PASS( | |||
| ExpandShuffleVector, "expand-shufflevector", | |||
| "Expand shufflevector instructions into insertelement and extractelement", | |||
| false, false) | |||
|
|
|||
| void ExpandShuffleVector::Expand(ShuffleVectorInst *Shuf, Type *Int32) { | |||
| Value *L = Shuf->getOperand(0); | |||
| Value *R = Shuf->getOperand(1); | |||
| assert(L->getType() == R->getType()); | |||
| VectorType *SrcVecTy = cast<VectorType>(L->getType()); | |||
| VectorType *DstVecTy = Shuf->getType(); | |||
| Type *ElemTy = DstVecTy->getElementType(); | |||
| SmallVector<int, 16> Mask = Shuf->getShuffleMask(); | |||
| unsigned NumSrcElems = SrcVecTy->getNumElements(); | |||
| unsigned NumDstElems = Mask.size(); | |||
|
|
|||
| // Start with an undefined vector, extract each element from either L | |||
| // or R according to the Mask, and insert it into contiguous element | |||
| // locations in the result vector. | |||
| // | |||
| // The sources for shufflevector must have the same type but the | |||
| // destination could be a narrower or wider vector with the same | |||
| // element type. | |||
| Instruction *ExtractLoc = Shuf; | |||
| Value *Res = UndefValue::get(DstVecTy); | |||
| for (unsigned Elem = 0; Elem != NumDstElems; ++Elem) { | |||
| bool IsUndef = | |||
| 0 > Mask[Elem] || static_cast<unsigned>(Mask[Elem]) >= NumSrcElems * 2; | |||
| bool IsL = static_cast<unsigned>(Mask[Elem]) < NumSrcElems; | |||
| Value *From = IsL ? L : R; | |||
| int Adjustment = IsL ? 0 : NumSrcElems; | |||
| Constant *ExtractIdx = ConstantInt::get(Int32, Mask[Elem] - Adjustment); | |||
| Constant *InsertIdx = ConstantInt::get(Int32, Elem); | |||
| Value *ElemToInsert = IsUndef ? UndefValue::get(ElemTy) | |||
| : (Value *)ExtractElementInst::Create( | |||
| From, ExtractIdx, "", ExtractLoc); | |||
| Res = InsertElementInst::Create(Res, ElemToInsert, InsertIdx, "", Shuf); | |||
| if (ExtractLoc == Shuf) | |||
| // All the extracts should be added just before the first insert we added. | |||
| ExtractLoc = cast<Instruction>(Res); | |||
| } | |||
|
|
|||
| Shuf->replaceAllUsesWith(Res); | |||
| Shuf->eraseFromParent(); | |||
| } | |||
|
|
|||
| bool ExpandShuffleVector::runOnBasicBlock(BasicBlock &BB) { | |||
| Type *Int32 = Type::getInt32Ty(M->getContext()); | |||
| typedef SmallVector<ShuffleVectorInst *, 8> Instructions; | |||
| Instructions Shufs; | |||
|
|
|||
| for (BasicBlock::iterator BBI = BB.begin(); BBI != BB.end(); ++BBI) | |||
| if (ShuffleVectorInst *S = dyn_cast<ShuffleVectorInst>(&*BBI)) | |||
| Shufs.push_back(S); | |||
|
|
|||
| for (Instructions::iterator S = Shufs.begin(), E = Shufs.end(); S != E; ++S) | |||
| Expand(*S, Int32); | |||
|
|
|||
| return !Shufs.empty(); | |||
| } | |||
|
|
|||
| BasicBlockPass *llvm::createExpandShuffleVectorPass() { | |||
| return new ExpandShuffleVector(); | |||
| } | |||
| @@ -0,0 +1,250 @@ | |||
| //===- ExpandSmallArguments.cpp - Expand out arguments smaller than i32----===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // LLVM IR allows function return types and argument types such as | |||
| // "zeroext i8" and "signext i8". The Language Reference says that | |||
| // zeroext "indicates to the code generator that the parameter or | |||
| // return value should be zero-extended to the extent required by the | |||
| // target's ABI (which is usually 32-bits, but is 8-bits for a i1 on | |||
| // x86-64) by the caller (for a parameter) or the callee (for a return | |||
| // value)". | |||
| // | |||
| // This can lead to non-portable behaviour when calling functions | |||
| // without C prototypes or with wrong C prototypes. | |||
| // | |||
| // In order to remove this non-portability from PNaCl, and to simplify | |||
| // the language that the PNaCl translator accepts, the | |||
| // ExpandSmallArguments pass widens integer arguments and return types | |||
| // to be at least 32 bits. The pass inserts explicit cast | |||
| // instructions (ZExtInst/SExtInst/TruncInst) as needed. | |||
| // | |||
| // The pass chooses between ZExtInst and SExtInst widening based on | |||
| // whether a "signext" attribute is present. However, in principle | |||
| // the pass could always use zero-extension, because the extent to | |||
| // which either zero-extension or sign-extension is done is up to the | |||
| // target ABI, which is up to PNaCl to specify. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/IR/Function.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/IntrinsicInst.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
| #include "llvm/Transforms/Utils/BasicBlockUtils.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| // This is a ModulePass because the pass recreates functions in | |||
| // order to change their arguments' types. | |||
| class ExpandSmallArguments : public ModulePass { | |||
| public: | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandSmallArguments() : ModulePass(ID) { | |||
| initializeExpandSmallArgumentsPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| virtual bool runOnModule(Module &M); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandSmallArguments::ID = 0; | |||
| INITIALIZE_PASS(ExpandSmallArguments, "expand-small-arguments", | |||
| "Expand function arguments to be at least 32 bits in size", | |||
| false, false) | |||
|
|
|||
| // Returns the normalized version of the given argument/return type. | |||
| static Type *NormalizeType(Type *Ty) { | |||
| if (IntegerType *IntTy = dyn_cast<IntegerType>(Ty)) { | |||
| if (IntTy->getBitWidth() < 32) { | |||
| return IntegerType::get(Ty->getContext(), 32); | |||
| } | |||
| } | |||
| return Ty; | |||
| } | |||
|
|
|||
| // Returns the normalized version of the given function type. | |||
| static FunctionType *NormalizeFunctionType(FunctionType *FTy) { | |||
| if (FTy->isVarArg()) { | |||
| report_fatal_error( | |||
| "ExpandSmallArguments does not handle varargs functions"); | |||
| } | |||
| SmallVector<Type *, 8> ArgTypes; | |||
| for (unsigned I = 0; I < FTy->getNumParams(); ++I) { | |||
| ArgTypes.push_back(NormalizeType(FTy->getParamType(I))); | |||
| } | |||
| return FunctionType::get(NormalizeType(FTy->getReturnType()), | |||
| ArgTypes, false); | |||
| } | |||
|
|
|||
| // Convert the given function to use normalized argument/return types. | |||
| static bool ConvertFunction(Function *Func) { | |||
| FunctionType *FTy = Func->getFunctionType(); | |||
| FunctionType *NFTy = NormalizeFunctionType(FTy); | |||
| if (NFTy == FTy) | |||
| return false; // No change needed. | |||
| Function *NewFunc = RecreateFunction(Func, NFTy); | |||
|
|
|||
| // Move the arguments across to the new function. | |||
| for (Function::arg_iterator I = Func->arg_begin(), E = Func->arg_end(), | |||
| NewI = NewFunc->arg_begin(); | |||
| I != E; ++I, ++NewI) { | |||
| auto Arg = &*I; | |||
| auto NewArg = &*NewI; | |||
| NewArg->takeName(Arg); | |||
| if (Arg->getType() == NewArg->getType()) { | |||
| Arg->replaceAllUsesWith(NewArg); | |||
| } else { | |||
| Instruction *Trunc = new TruncInst( | |||
| NewArg, Arg->getType(), NewArg->getName() + ".arg_trunc", | |||
| &*NewFunc->getEntryBlock().getFirstInsertionPt()); | |||
| Arg->replaceAllUsesWith(Trunc); | |||
| } | |||
| } | |||
|
|
|||
| if (FTy->getReturnType() != NFTy->getReturnType()) { | |||
| // Fix up return instructions. | |||
| Instruction::CastOps CastType = | |||
| Func->getAttributes().hasAttribute(0, Attribute::SExt) ? | |||
| Instruction::SExt : Instruction::ZExt; | |||
| for (Function::iterator BB = NewFunc->begin(), E = NewFunc->end(); | |||
| BB != E; | |||
| ++BB) { | |||
| for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); | |||
| Iter != E; ) { | |||
| Instruction *Inst = &*Iter++; | |||
| if (ReturnInst *Ret = dyn_cast<ReturnInst>(Inst)) { | |||
| Value *Ext = CopyDebug( | |||
| CastInst::Create(CastType, Ret->getReturnValue(), | |||
| NFTy->getReturnType(), | |||
| Ret->getReturnValue()->getName() + ".ret_ext", | |||
| Ret), | |||
| Ret); | |||
| CopyDebug(ReturnInst::Create(Ret->getContext(), Ext, Ret), Ret); | |||
| Ret->eraseFromParent(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| Func->eraseFromParent(); | |||
| return true; | |||
| } | |||
|
|
|||
| // Convert the given call to use normalized argument/return types. | |||
| template <class T> static bool ConvertCall(T *Call, Pass *P) { | |||
| // Don't try to change calls to intrinsics. | |||
| if (isa<IntrinsicInst>(Call)) | |||
| return false; | |||
| FunctionType *FTy = cast<FunctionType>( | |||
| Call->getCalledValue()->getType()->getPointerElementType()); | |||
| FunctionType *NFTy = NormalizeFunctionType(FTy); | |||
| if (NFTy == FTy) | |||
| return false; // No change needed. | |||
|
|
|||
| // Convert arguments. | |||
| SmallVector<Value *, 8> Args; | |||
| for (unsigned I = 0; I < Call->getNumArgOperands(); ++I) { | |||
| Value *Arg = Call->getArgOperand(I); | |||
| if (NFTy->getParamType(I) != FTy->getParamType(I)) { | |||
| Instruction::CastOps CastType = | |||
| Call->getAttributes().hasAttribute(I + 1, Attribute::SExt) ? | |||
| Instruction::SExt : Instruction::ZExt; | |||
| Arg = CopyDebug(CastInst::Create(CastType, Arg, NFTy->getParamType(I), | |||
| "arg_ext", Call), Call); | |||
| } | |||
| Args.push_back(Arg); | |||
| } | |||
| Value *CastFunc = | |||
| CopyDebug(new BitCastInst(Call->getCalledValue(), NFTy->getPointerTo(), | |||
| Call->getName() + ".arg_cast", Call), Call); | |||
| Value *Result = NULL; | |||
| if (CallInst *OldCall = dyn_cast<CallInst>(Call)) { | |||
| CallInst *NewCall = CopyDebug(CallInst::Create(CastFunc, Args, "", OldCall), | |||
| OldCall); | |||
| NewCall->takeName(OldCall); | |||
| NewCall->setAttributes(OldCall->getAttributes()); | |||
| NewCall->setCallingConv(OldCall->getCallingConv()); | |||
| NewCall->setTailCall(OldCall->isTailCall()); | |||
| Result = NewCall; | |||
|
|
|||
| if (FTy->getReturnType() != NFTy->getReturnType()) { | |||
| Result = CopyDebug(new TruncInst(NewCall, FTy->getReturnType(), | |||
| NewCall->getName() + ".ret_trunc", Call), | |||
| Call); | |||
| } | |||
| } else if (InvokeInst *OldInvoke = dyn_cast<InvokeInst>(Call)) { | |||
| BasicBlock *Parent = OldInvoke->getParent(); | |||
| BasicBlock *NormalDest = OldInvoke->getNormalDest(); | |||
| BasicBlock *UnwindDest = OldInvoke->getUnwindDest(); | |||
|
|
|||
| if (FTy->getReturnType() != NFTy->getReturnType()) { | |||
| if (BasicBlock *SplitDest = SplitCriticalEdge(Parent, NormalDest)) { | |||
| NormalDest = SplitDest; | |||
| } | |||
| } | |||
|
|
|||
| InvokeInst *New = CopyDebug(InvokeInst::Create(CastFunc, NormalDest, | |||
| UnwindDest, Args, | |||
| "", OldInvoke), | |||
| OldInvoke); | |||
| New->takeName(OldInvoke); | |||
|
|
|||
| if (FTy->getReturnType() != NFTy->getReturnType()) { | |||
| Result = CopyDebug(new TruncInst(New, FTy->getReturnType(), | |||
| New->getName() + ".ret_trunc", | |||
| NormalDest->getTerminator()), | |||
| OldInvoke); | |||
| } else { | |||
| Result = New; | |||
| } | |||
|
|
|||
| New->setAttributes(OldInvoke->getAttributes()); | |||
| New->setCallingConv(OldInvoke->getCallingConv()); | |||
| } | |||
| Call->replaceAllUsesWith(Result); | |||
| Call->eraseFromParent(); | |||
| return true; | |||
| } | |||
|
|
|||
| bool ExpandSmallArguments::runOnModule(Module &M) { | |||
| bool Changed = false; | |||
| for (Module::iterator Iter = M.begin(), E = M.end(); Iter != E; ) { | |||
| Function *Func = &*Iter++; | |||
| // Don't try to change intrinsic declarations because intrinsics | |||
| // will continue to have non-normalized argument types. For | |||
| // example, memset() takes an i8 argument. It shouldn't matter | |||
| // whether we modify the types of other function declarations, but | |||
| // we don't expect to see non-intrinsic function declarations in a | |||
| // PNaCl pexe. | |||
| if (Func->empty()) | |||
| continue; | |||
|
|
|||
| for (Function::iterator BB = Func->begin(), E = Func->end(); BB != E; | |||
| ++BB) { | |||
| for (BasicBlock::iterator Iter = BB->begin(), E = BB->end(); Iter != E;) { | |||
| Instruction *Inst = &*Iter++; | |||
| if (CallInst *Call = dyn_cast<CallInst>(Inst)) { | |||
| Changed |= ConvertCall(Call, this); | |||
| } else if (InvokeInst *Invoke = dyn_cast<InvokeInst>(Inst)) { | |||
| Changed |= ConvertCall(Invoke, this); | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| Changed |= ConvertFunction(Func); | |||
| } | |||
| return Changed; | |||
| } | |||
|
|
|||
| ModulePass *llvm::createExpandSmallArgumentsPass() { | |||
| return new ExpandSmallArguments(); | |||
| } | |||
| @@ -0,0 +1,336 @@ | |||
| //===- ExpandTls.cpp - Convert TLS variables to a concrete layout----------===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This pass expands out uses of thread-local (TLS) variables into | |||
| // more primitive operations. | |||
| // | |||
| // A reference to the address of a TLS variable is expanded into code | |||
| // which gets the current thread's thread pointer using | |||
| // @llvm.nacl.read.tp() and adds a fixed offset. | |||
| // | |||
| // This pass allocates the offsets (relative to the thread pointer) | |||
| // that will be used for TLS variables. It sets up the global | |||
| // variables __tls_template_start, __tls_template_end etc. to contain | |||
| // a template for initializing TLS variables' values for each thread. | |||
| // This is a task normally performed by the linker in ELF systems. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include <vector> | |||
|
|
|||
| #include "llvm/Pass.h" | |||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/DataLayout.h" | |||
| #include "llvm/IR/DerivedTypes.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Intrinsics.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| struct VarInfo { | |||
| GlobalVariable *TlsVar; | |||
| bool IsBss; // Whether variable is in zero-intialized part of template | |||
| int TemplateIndex; | |||
| }; | |||
|
|
|||
| class PassState { | |||
| public: | |||
| PassState(Module *M): M(M), DL(M), Offset(0), Alignment(1) {} | |||
|
|
|||
| Module *M; | |||
| DataLayout DL; | |||
| uint64_t Offset; | |||
| // 'Alignment' is the maximum variable alignment seen so far, in | |||
| // bytes. After visiting all TLS variables, this is the overall | |||
| // alignment required for the TLS template. | |||
| uint32_t Alignment; | |||
| }; | |||
|
|
|||
| class ExpandTls : public ModulePass { | |||
| public: | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandTls() : ModulePass(ID) { | |||
| initializeExpandTlsPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| virtual bool runOnModule(Module &M); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandTls::ID = 0; | |||
| INITIALIZE_PASS(ExpandTls, "nacl-expand-tls", | |||
| "Expand out TLS variables and fix TLS variable layout", | |||
| false, false) | |||
|
|
|||
| static void setGlobalVariableValue(Module &M, const char *Name, | |||
| Constant *Value) { | |||
| if (GlobalVariable *Var = M.getNamedGlobal(Name)) { | |||
| if (Var->hasInitializer()) { | |||
| report_fatal_error(std::string("Variable ") + Name + | |||
| " already has an initializer"); | |||
| } | |||
| Var->replaceAllUsesWith(ConstantExpr::getBitCast(Value, Var->getType())); | |||
| Var->eraseFromParent(); | |||
| } | |||
| } | |||
|
|
|||
| // Insert alignment padding into the TLS template. | |||
| static void padToAlignment(PassState *State, | |||
| std::vector<Type*> *FieldTypes, | |||
| std::vector<Constant*> *FieldValues, | |||
| unsigned Alignment) { | |||
| if ((State->Offset & (Alignment - 1)) != 0) { | |||
| unsigned PadSize = Alignment - (State->Offset & (Alignment - 1)); | |||
| Type *i8 = Type::getInt8Ty(State->M->getContext()); | |||
| Type *PadType = ArrayType::get(i8, PadSize); | |||
| FieldTypes->push_back(PadType); | |||
| if (FieldValues) | |||
| FieldValues->push_back(Constant::getNullValue(PadType)); | |||
| State->Offset += PadSize; | |||
| } | |||
| if (State->Alignment < Alignment) { | |||
| State->Alignment = Alignment; | |||
| } | |||
| } | |||
|
|
|||
| static void addVarToTlsTemplate(PassState *State, | |||
| std::vector<Type*> *FieldTypes, | |||
| std::vector<Constant*> *FieldValues, | |||
| GlobalVariable *TlsVar) { | |||
| unsigned Alignment = State->DL.getPreferredAlignment(TlsVar); | |||
| padToAlignment(State, FieldTypes, FieldValues, Alignment); | |||
|
|
|||
| FieldTypes->push_back(TlsVar->getType()->getElementType()); | |||
| if (FieldValues) | |||
| FieldValues->push_back(TlsVar->getInitializer()); | |||
| State->Offset += | |||
| State->DL.getTypeAllocSize(TlsVar->getType()->getElementType()); | |||
| } | |||
|
|
|||
| static StructType *buildTlsTemplate(Module &M, std::vector<VarInfo> *TlsVars) { | |||
| std::vector<Type*> FieldBssTypes; | |||
| std::vector<Type*> FieldInitTypes; | |||
| std::vector<Constant*> FieldInitValues; | |||
| PassState State(&M); | |||
|
|
|||
| for (Module::global_iterator GV = M.global_begin(); | |||
| GV != M.global_end(); | |||
| ++GV) { | |||
| if (GV->isThreadLocal()) { | |||
| if (!GV->hasInitializer()) { | |||
| // Since this is a whole-program transformation, "extern" TLS | |||
| // variables are not allowed at this point. | |||
| report_fatal_error(std::string("TLS variable without an initializer: ") | |||
| + GV->getName()); | |||
| } | |||
| if (!GV->getInitializer()->isNullValue()) { | |||
| addVarToTlsTemplate(&State, &FieldInitTypes, | |||
| &FieldInitValues, &*GV); | |||
| VarInfo Info; | |||
| Info.TlsVar = &*GV; | |||
| Info.IsBss = false; | |||
| Info.TemplateIndex = FieldInitTypes.size() - 1; | |||
| TlsVars->push_back(Info); | |||
| } | |||
| } | |||
| } | |||
| // Handle zero-initialized TLS variables in a second pass, because | |||
| // these should follow non-zero-initialized TLS variables. | |||
| for (Module::global_iterator GV = M.global_begin(); | |||
| GV != M.global_end(); | |||
| ++GV) { | |||
| if (GV->isThreadLocal() && GV->getInitializer()->isNullValue()) { | |||
| addVarToTlsTemplate(&State, &FieldBssTypes, NULL, &*GV); | |||
| VarInfo Info; | |||
| Info.TlsVar = &*GV; | |||
| Info.IsBss = true; | |||
| Info.TemplateIndex = FieldBssTypes.size() - 1; | |||
| TlsVars->push_back(Info); | |||
| } | |||
| } | |||
| // Add final alignment padding so that | |||
| // (struct tls_struct *) __nacl_read_tp() - 1 | |||
| // gives the correct, aligned start of the TLS variables given the | |||
| // x86-style layout we are using. This requires some more bytes to | |||
| // be memset() to zero at runtime. This wastage doesn't seem | |||
| // important gives that we're not trying to optimize packing by | |||
| // reordering to put similarly-aligned variables together. | |||
| padToAlignment(&State, &FieldBssTypes, NULL, State.Alignment); | |||
|
|
|||
| // We create the TLS template structs as "packed" because we insert | |||
| // alignment padding ourselves, and LLVM's implicit insertion of | |||
| // padding would interfere with ours. tls_bss_template can start at | |||
| // a non-aligned address immediately following the last field in | |||
| // tls_init_template. | |||
| StructType *InitTemplateType = | |||
| StructType::create(M.getContext(), "tls_init_template"); | |||
| InitTemplateType->setBody(FieldInitTypes, /*isPacked=*/true); | |||
| StructType *BssTemplateType = | |||
| StructType::create(M.getContext(), "tls_bss_template"); | |||
| BssTemplateType->setBody(FieldBssTypes, /*isPacked=*/true); | |||
|
|
|||
| StructType *TemplateType = StructType::create(M.getContext(), "tls_struct"); | |||
| SmallVector<Type*, 2> TemplateTopFields; | |||
| TemplateTopFields.push_back(InitTemplateType); | |||
| TemplateTopFields.push_back(BssTemplateType); | |||
| TemplateType->setBody(TemplateTopFields, /*isPacked=*/true); | |||
| PointerType *TemplatePtrType = PointerType::get(TemplateType, 0); | |||
|
|
|||
| // We define the following symbols, which are the same as those | |||
| // defined by NaCl's original customized binutils linker scripts: | |||
| // __tls_template_start | |||
| // __tls_template_tdata_end | |||
| // __tls_template_end | |||
| // We also define __tls_template_alignment, which was not defined by | |||
| // the original linker scripts. | |||
|
|
|||
| const char *StartSymbol = "__tls_template_start"; | |||
| Constant *TemplateData = ConstantStruct::get(InitTemplateType, | |||
| FieldInitValues); | |||
| GlobalVariable *TemplateDataVar = | |||
| new GlobalVariable(M, InitTemplateType, /*isConstant=*/true, | |||
| GlobalValue::InternalLinkage, TemplateData); | |||
| setGlobalVariableValue(M, StartSymbol, TemplateDataVar); | |||
| TemplateDataVar->setName(StartSymbol); | |||
|
|
|||
| Constant *TdataEnd = ConstantExpr::getGetElementPtr( | |||
| InitTemplateType, | |||
| TemplateDataVar, | |||
| ConstantInt::get(M.getContext(), APInt(32, 1))); | |||
| setGlobalVariableValue(M, "__tls_template_tdata_end", TdataEnd); | |||
|
|
|||
| Constant *TotalEnd = ConstantExpr::getGetElementPtr( | |||
| TemplateType, | |||
| ConstantExpr::getBitCast(TemplateDataVar, TemplatePtrType), | |||
| ConstantInt::get(M.getContext(), APInt(32, 1))); | |||
| setGlobalVariableValue(M, "__tls_template_end", TotalEnd); | |||
|
|
|||
| const char *AlignmentSymbol = "__tls_template_alignment"; | |||
| Type *i32 = Type::getInt32Ty(M.getContext()); | |||
| GlobalVariable *AlignmentVar = new GlobalVariable( | |||
| M, i32, /*isConstant=*/true, | |||
| GlobalValue::InternalLinkage, | |||
| ConstantInt::get(M.getContext(), APInt(32, State.Alignment))); | |||
| setGlobalVariableValue(M, AlignmentSymbol, AlignmentVar); | |||
| AlignmentVar->setName(AlignmentSymbol); | |||
|
|
|||
| return TemplateType; | |||
| } | |||
|
|
|||
| static void rewriteTlsVars(Module &M, std::vector<VarInfo> *TlsVars, | |||
| StructType *TemplateType) { | |||
| // Set up the intrinsic that reads the thread pointer. | |||
| Function *ReadTpFunc = Intrinsic::getDeclaration(&M, Intrinsic::nacl_read_tp); | |||
|
|
|||
| for (std::vector<VarInfo>::iterator VarInfo = TlsVars->begin(); | |||
| VarInfo != TlsVars->end(); | |||
| ++VarInfo) { | |||
| GlobalVariable *Var = VarInfo->TlsVar; | |||
| while (Var->hasNUsesOrMore(1)) { | |||
| Use *U = &*Var->use_begin(); | |||
| Instruction *InsertPt = PhiSafeInsertPt(U); | |||
| Value *RawThreadPtr = CallInst::Create(ReadTpFunc, "tls_raw", InsertPt); | |||
| Value *TypedThreadPtr = new BitCastInst( | |||
| RawThreadPtr, TemplateType->getPointerTo(), "tls_struct", InsertPt); | |||
| SmallVector<Value*, 3> Indexes; | |||
| // We use -1 because we use the x86-style TLS layout in which | |||
| // the TLS data is stored at addresses below the thread pointer. | |||
| // This is largely because a check in nacl_irt_thread_create() | |||
| // in irt/irt_thread.c requires the thread pointer to be a | |||
| // self-pointer on x86-32. | |||
| // TODO(mseaborn): I intend to remove that check because it is | |||
| // non-portable. In the mean time, we want PNaCl pexes to work | |||
| // in older Chromium releases when translated to nexes. | |||
| Indexes.push_back(ConstantInt::get( | |||
| M.getContext(), APInt(32, -1))); | |||
| Indexes.push_back(ConstantInt::get( | |||
| M.getContext(), APInt(32, VarInfo->IsBss ? 1 : 0))); | |||
| Indexes.push_back(ConstantInt::get( | |||
| M.getContext(), APInt(32, VarInfo->TemplateIndex))); | |||
| Value *TlsField = GetElementPtrInst::Create( | |||
| TemplateType, TypedThreadPtr, Indexes, "field", InsertPt); | |||
| PhiSafeReplaceUses(U, TlsField); | |||
| } | |||
| VarInfo->TlsVar->eraseFromParent(); | |||
| } | |||
| } | |||
|
|
|||
| static void replaceFunction(Module &M, const char *Name, Value *NewFunc) { | |||
| if (Function *Func = M.getFunction(Name)) { | |||
| if (Func->hasLocalLinkage()) | |||
| return; | |||
| if (!Func->isDeclaration()) | |||
| report_fatal_error(std::string("Function already defined: ") + Name); | |||
| Func->replaceAllUsesWith(NewFunc); | |||
| Func->eraseFromParent(); | |||
| } | |||
| } | |||
|
|
|||
| // Provide fixed definitions for NaCl's TLS layout functions, | |||
| // __nacl_tp_*(). We adopt the x86-style layout: ExpandTls will | |||
| // output a program that uses the x86-style layout wherever it runs. | |||
| // | |||
| // This overrides the architecture-specific definitions of | |||
| // __nacl_tp_*() that PNaCl's native support code makes available to | |||
| // non-ABI-stable code. | |||
| static void defineTlsLayoutFunctions(Module &M) { | |||
| Type *i32 = Type::getInt32Ty(M.getContext()); | |||
| SmallVector<Type*, 1> ArgTypes; | |||
| ArgTypes.push_back(i32); | |||
| FunctionType *FuncType = FunctionType::get(i32, ArgTypes, /*isVarArg=*/false); | |||
| Function *NewFunc; | |||
| BasicBlock *BB; | |||
|
|
|||
| // Define the function as follows: | |||
| // uint32_t __nacl_tp_tdb_offset(uint32_t tdb_size) { | |||
| // return 0; | |||
| // } | |||
| // This means the thread pointer points to the TDB. | |||
| NewFunc = Function::Create(FuncType, GlobalValue::InternalLinkage, | |||
| "nacl_tp_tdb_offset", &M); | |||
| BB = BasicBlock::Create(M.getContext(), "entry", NewFunc); | |||
| ReturnInst::Create(M.getContext(), | |||
| ConstantInt::get(M.getContext(), APInt(32, 0)), BB); | |||
| replaceFunction(M, "__nacl_tp_tdb_offset", NewFunc); | |||
|
|
|||
| // Define the function as follows: | |||
| // uint32_t __nacl_tp_tls_offset(uint32_t tls_size) { | |||
| // return -tls_size; | |||
| // } | |||
| // This means the TLS variables are stored below the thread pointer. | |||
| NewFunc = Function::Create(FuncType, GlobalValue::InternalLinkage, | |||
| "nacl_tp_tls_offset", &M); | |||
| BB = BasicBlock::Create(M.getContext(), "entry", NewFunc); | |||
| Value *Arg = &*NewFunc->arg_begin(); | |||
| Arg->setName("size"); | |||
| Value *Result = BinaryOperator::CreateNeg(Arg, "result", BB); | |||
| ReturnInst::Create(M.getContext(), Result, BB); | |||
| replaceFunction(M, "__nacl_tp_tls_offset", NewFunc); | |||
| } | |||
|
|
|||
| bool ExpandTls::runOnModule(Module &M) { | |||
| ModulePass *Pass = createExpandTlsConstantExprPass(); | |||
| Pass->runOnModule(M); | |||
| delete Pass; | |||
|
|
|||
| std::vector<VarInfo> TlsVars; | |||
| StructType *TemplateType = buildTlsTemplate(M, &TlsVars); | |||
| rewriteTlsVars(M, &TlsVars, TemplateType); | |||
|
|
|||
| defineTlsLayoutFunctions(M); | |||
|
|
|||
| return true; | |||
| } | |||
|
|
|||
| ModulePass *llvm::createExpandTlsPass() { | |||
| return new ExpandTls(); | |||
| } | |||
| @@ -0,0 +1,107 @@ | |||
| //===- ExpandTlsConstantExpr.cpp - Convert ConstantExprs to Instructions---===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This pass is a helper used by the ExpandTls pass. | |||
| // | |||
| // LLVM treats the address of a TLS variable as a ConstantExpr. This | |||
| // is arguably a bug because the address of a TLS variable is *not* a | |||
| // constant: it varies between threads. | |||
| // | |||
| // See http://llvm.org/bugs/show_bug.cgi?id=14353 | |||
| // | |||
| // This is also a problem for the ExpandTls pass, which wants to use | |||
| // replaceUsesOfWith() to replace each TLS variable with an | |||
| // Instruction sequence that calls @llvm.nacl.read.tp(). This doesn't | |||
| // work if the TLS variable is used inside other ConstantExprs, | |||
| // because ConstantExprs are interned and are not associated with any | |||
| // function, whereas each Instruction must be part of a function. | |||
| // | |||
| // To fix that problem, this pass converts ConstantExprs that | |||
| // reference TLS variables into Instructions. | |||
| // | |||
| // For example, this use of a 'ptrtoint' ConstantExpr: | |||
| // | |||
| // ret i32 ptrtoint (i32* @tls_var to i32) | |||
| // | |||
| // is converted into this 'ptrtoint' Instruction: | |||
| // | |||
| // %expanded = ptrtoint i32* @tls_var to i32 | |||
| // ret i32 %expanded | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include <vector> | |||
|
|
|||
| #include "llvm/Pass.h" | |||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| class ExpandTlsConstantExpr : public ModulePass { | |||
| public: | |||
| static char ID; // Pass identification, replacement for typeid | |||
| ExpandTlsConstantExpr() : ModulePass(ID) { | |||
| initializeExpandTlsConstantExprPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
|
|
|||
| virtual bool runOnModule(Module &M); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandTlsConstantExpr::ID = 0; | |||
| INITIALIZE_PASS(ExpandTlsConstantExpr, "nacl-expand-tls-constant-expr", | |||
| "Eliminate ConstantExpr references to TLS variables", | |||
| false, false) | |||
|
|
|||
| // This removes ConstantExpr references to the given Constant. | |||
| static void expandConstExpr(Constant *Expr) { | |||
| // First, ensure that ConstantExpr references to Expr are converted | |||
| // to Instructions so that we can modify them. | |||
| for (Use &U : Expr->uses()) | |||
| if (ConstantExpr *CE = dyn_cast<ConstantExpr>(U.getUser())) | |||
| expandConstExpr(CE); | |||
| Expr->removeDeadConstantUsers(); | |||
|
|
|||
| if (ConstantExpr *CE = dyn_cast<ConstantExpr>(Expr)) { | |||
| while (Expr->hasNUsesOrMore(1)) { | |||
| Use *U = &*Expr->use_begin(); | |||
| Instruction *NewInst = CE->getAsInstruction(); | |||
| NewInst->insertBefore(PhiSafeInsertPt(U)); | |||
| NewInst->setName("expanded"); | |||
| PhiSafeReplaceUses(U, NewInst); | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| bool ExpandTlsConstantExpr::runOnModule(Module &M) { | |||
| for (Module::alias_iterator Iter = M.alias_begin(); | |||
| Iter != M.alias_end(); ) { | |||
| GlobalAlias *GA = &*Iter++; | |||
| if (GA->isThreadDependent()) { | |||
| GA->replaceAllUsesWith(GA->getAliasee()); | |||
| GA->eraseFromParent(); | |||
| } | |||
| } | |||
| for (Module::global_iterator Global = M.global_begin(); | |||
| Global != M.global_end(); | |||
| ++Global) { | |||
| if (Global->isThreadLocal()) { | |||
| expandConstExpr(&*Global); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
|
|
|||
| ModulePass *llvm::createExpandTlsConstantExprPass() { | |||
| return new ExpandTlsConstantExpr(); | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| //===-- ExpandUtils.cpp - Helper functions for expansion passes -----------===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/IR/BasicBlock.h" | |||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/Function.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Support/raw_ostream.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| Instruction *llvm::PhiSafeInsertPt(Use *U) { | |||
| Instruction *InsertPt = cast<Instruction>(U->getUser()); | |||
| if (PHINode *PN = dyn_cast<PHINode>(InsertPt)) { | |||
| // We cannot insert instructions before a PHI node, so insert | |||
| // before the incoming block's terminator. This could be | |||
| // suboptimal if the terminator is a conditional. | |||
| InsertPt = PN->getIncomingBlock(*U)->getTerminator(); | |||
| } | |||
| return InsertPt; | |||
| } | |||
|
|
|||
| void llvm::PhiSafeReplaceUses(Use *U, Value *NewVal) { | |||
| User *UR = U->getUser(); | |||
| if (PHINode *PN = dyn_cast<PHINode>(UR)) { | |||
| // A PHI node can have multiple incoming edges from the same | |||
| // block, in which case all these edges must have the same | |||
| // incoming value. | |||
| BasicBlock *BB = PN->getIncomingBlock(*U); | |||
| for (unsigned I = 0; I < PN->getNumIncomingValues(); ++I) { | |||
| if (PN->getIncomingBlock(I) == BB) | |||
| PN->setIncomingValue(I, NewVal); | |||
| } | |||
| } else { | |||
| UR->replaceUsesOfWith(U->get(), NewVal); | |||
| } | |||
| } | |||
|
|
|||
| Function *llvm::RecreateFunction(Function *Func, FunctionType *NewType) { | |||
| Function *NewFunc = Function::Create(NewType, Func->getLinkage()); | |||
| NewFunc->copyAttributesFrom(Func); | |||
| Func->getParent()->getFunctionList().insert(Func->getIterator(), NewFunc); | |||
| NewFunc->takeName(Func); | |||
| NewFunc->getBasicBlockList().splice(NewFunc->begin(), | |||
| Func->getBasicBlockList()); | |||
| Func->replaceAllUsesWith( | |||
| ConstantExpr::getBitCast(NewFunc, | |||
| Func->getFunctionType()->getPointerTo())); | |||
| return NewFunc; | |||
| } | |||
| @@ -0,0 +1,324 @@ | |||
| //===- ExpandVarArgs.cpp - Expand out variable argument function calls-----===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // This pass expands out all use of variable argument functions. | |||
| // | |||
| // This pass replaces a varargs function call with a function call in | |||
| // which a pointer to the variable arguments is passed explicitly. | |||
| // The callee explicitly allocates space for the variable arguments on | |||
| // the stack using "alloca". | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/ADT/SmallVector.h" | |||
| #include "llvm/ADT/Triple.h" | |||
| #include "llvm/IR/Constants.h" | |||
| #include "llvm/IR/DataLayout.h" | |||
| #include "llvm/IR/Function.h" | |||
| #include "llvm/IR/IRBuilder.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/IntrinsicInst.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| class ExpandVarArgs : public ModulePass { | |||
| public: | |||
| static char ID; | |||
| ExpandVarArgs() : ModulePass(ID) { | |||
| initializeExpandVarArgsPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
| virtual bool runOnModule(Module &M); | |||
| }; | |||
| } | |||
|
|
|||
| char ExpandVarArgs::ID = 0; | |||
| INITIALIZE_PASS(ExpandVarArgs, "expand-varargs", | |||
| "Expand out variable argument function definitions and calls", | |||
| false, false) | |||
|
|
|||
| static bool isEmscriptenJSArgsFunc(Module *M, StringRef Name) { | |||
| // TODO(jfb) Make these intrinsics in clang and remove the assert: these | |||
| // intrinsics should only exist for Emscripten. | |||
| bool isEmscriptenSpecial = Name.equals("emscripten_asm_const_int") || | |||
| Name.equals("emscripten_asm_const_double") || | |||
| Name.equals("emscripten_landingpad") || | |||
| Name.equals("emscripten_resume"); | |||
| assert(isEmscriptenSpecial ? Triple(M->getTargetTriple()).isOSEmscripten() | |||
| : true); | |||
| return isEmscriptenSpecial; | |||
| } | |||
|
|
|||
| static bool ExpandVarArgFunc(Module *M, Function *Func) { | |||
| if (Func->isDeclaration() && Func->use_empty()) | |||
| return false; // No point in doing any work. | |||
|
|
|||
| if (isEmscriptenJSArgsFunc(M, Func->getName())) | |||
| return false; | |||
|
|
|||
| Type *PtrType = Type::getInt8PtrTy(Func->getContext()); | |||
|
|
|||
| FunctionType *FTy = Func->getFunctionType(); | |||
| SmallVector<Type *, 8> Params(FTy->param_begin(), FTy->param_end()); | |||
| Params.push_back(PtrType); | |||
| FunctionType *NFTy = | |||
| FunctionType::get(FTy->getReturnType(), Params, /*isVarArg=*/false); | |||
| Function *NewFunc = RecreateFunction(Func, NFTy); | |||
|
|
|||
| // Declare the new argument as "noalias". | |||
| NewFunc->setAttributes(Func->getAttributes().addAttribute( | |||
| Func->getContext(), FTy->getNumParams() + 1, Attribute::NoAlias)); | |||
|
|
|||
| // Move the arguments across to the new function. | |||
| auto NewArg = NewFunc->arg_begin(); | |||
| for (Argument &Arg : Func->args()) { | |||
| Arg.replaceAllUsesWith(&*NewArg); | |||
| NewArg->takeName(&Arg); | |||
| ++NewArg; | |||
| } | |||
| // The last argument is the new `i8 * noalias %varargs`. | |||
| NewArg->setName("varargs"); | |||
|
|
|||
| Func->eraseFromParent(); | |||
|
|
|||
| // Expand out uses of llvm.va_start in this function. | |||
| for (BasicBlock &BB : *NewFunc) { | |||
| for (auto BI = BB.begin(), BE = BB.end(); BI != BE;) { | |||
| Instruction *I = &*BI++; | |||
| if (auto *VAS = dyn_cast<VAStartInst>(I)) { | |||
| IRBuilder<> IRB(VAS); | |||
| Value *Cast = IRB.CreateBitCast(VAS->getArgList(), | |||
| PtrType->getPointerTo(), "arglist"); | |||
| IRB.CreateStore(&*NewArg, Cast); | |||
| VAS->eraseFromParent(); | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| return true; | |||
| } | |||
|
|
|||
| static void ExpandVAArgInst(VAArgInst *Inst, DataLayout *DL) { | |||
| Type *IntPtrTy = DL->getIntPtrType(Inst->getContext()); | |||
| auto *One = ConstantInt::get(IntPtrTy, 1); | |||
| IRBuilder<> IRB(Inst); | |||
| auto *ArgList = IRB.CreateBitCast( | |||
| Inst->getPointerOperand(), | |||
| Inst->getType()->getPointerTo()->getPointerTo(), "arglist"); | |||
|
|
|||
| // The caller spilled all of the va_args onto the stack in an unpacked | |||
| // struct. Each va_arg load from that struct needs to realign the element to | |||
| // its target-appropriate alignment in the struct in order to jump over | |||
| // padding that may have been in-between arguments. Do this with ConstantExpr | |||
| // to ensure good code gets generated, following the same approach as | |||
| // Support/MathExtras.h:alignAddr: | |||
| // ((uintptr_t)Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1) | |||
| // This assumes the alignment of the type is a power of 2 (or 1, in which case | |||
| // no realignment occurs). | |||
| auto *Ptr = IRB.CreateLoad(ArgList, "arglist_current"); | |||
| auto *AlignOf = ConstantExpr::getIntegerCast( | |||
| ConstantExpr::getAlignOf(Inst->getType()), IntPtrTy, /*isSigned=*/false); | |||
| auto *AlignMinus1 = ConstantExpr::getNUWSub(AlignOf, One); | |||
| auto *NotAlignMinus1 = IRB.CreateNot(AlignMinus1); | |||
| auto *CurrentPtr = IRB.CreateIntToPtr( | |||
| IRB.CreateAnd( | |||
| IRB.CreateNUWAdd(IRB.CreatePtrToInt(Ptr, IntPtrTy), AlignMinus1), | |||
| NotAlignMinus1), | |||
| Ptr->getType()); | |||
|
|
|||
| auto *Result = IRB.CreateLoad(CurrentPtr, "va_arg"); | |||
| Result->takeName(Inst); | |||
|
|
|||
| // Update the va_list to point to the next argument. | |||
| Value *Indexes[] = {One}; | |||
| auto *Next = IRB.CreateInBoundsGEP(CurrentPtr, Indexes, "arglist_next"); | |||
| IRB.CreateStore(Next, ArgList); | |||
|
|
|||
| Inst->replaceAllUsesWith(Result); | |||
| Inst->eraseFromParent(); | |||
| } | |||
|
|
|||
| static void ExpandVAEnd(VAEndInst *VAE) { | |||
| // va_end() is a no-op in this implementation. | |||
| VAE->eraseFromParent(); | |||
| } | |||
|
|
|||
| static void ExpandVACopyInst(VACopyInst *Inst) { | |||
| // va_list may have more space reserved, but we only need to | |||
| // copy a single pointer. | |||
| Type *PtrTy = Type::getInt8PtrTy(Inst->getContext())->getPointerTo(); | |||
| IRBuilder<> IRB(Inst); | |||
| auto *Src = IRB.CreateBitCast(Inst->getSrc(), PtrTy, "vacopy_src"); | |||
| auto *Dest = IRB.CreateBitCast(Inst->getDest(), PtrTy, "vacopy_dest"); | |||
| auto *CurrentPtr = IRB.CreateLoad(Src, "vacopy_currentptr"); | |||
| IRB.CreateStore(CurrentPtr, Dest); | |||
| Inst->eraseFromParent(); | |||
| } | |||
|
|
|||
| // ExpandVarArgCall() converts a CallInst or InvokeInst to expand out | |||
| // of varargs. It returns whether the module was modified. | |||
| template <class InstType> | |||
| static bool ExpandVarArgCall(Module *M, InstType *Call, DataLayout *DL) { | |||
| FunctionType *FuncType = cast<FunctionType>( | |||
| Call->getCalledValue()->getType()->getPointerElementType()); | |||
| if (!FuncType->isFunctionVarArg()) | |||
| return false; | |||
| if (auto *F = dyn_cast<Function>(Call->getCalledValue())) | |||
| if (isEmscriptenJSArgsFunc(M, F->getName())) | |||
| return false; | |||
|
|
|||
| Function *F = Call->getParent()->getParent(); | |||
| LLVMContext &Ctx = M->getContext(); | |||
|
|
|||
| SmallVector<AttributeSet, 8> Attrs; | |||
| Attrs.push_back(Call->getAttributes().getFnAttributes()); | |||
| Attrs.push_back(Call->getAttributes().getRetAttributes()); | |||
|
|
|||
| // Split argument list into fixed and variable arguments. | |||
| SmallVector<Value *, 8> FixedArgs; | |||
| SmallVector<Value *, 8> VarArgs; | |||
| SmallVector<Type *, 8> VarArgsTypes; | |||
| for (unsigned I = 0, E = FuncType->getNumParams(); I < E; ++I) { | |||
| FixedArgs.push_back(Call->getArgOperand(I)); | |||
| // AttributeSets use 1-based indexing. | |||
| Attrs.push_back(Call->getAttributes().getParamAttributes(I + 1)); | |||
| } | |||
| for (unsigned I = FuncType->getNumParams(), E = Call->getNumArgOperands(); | |||
| I < E; ++I) { | |||
| Value *ArgVal = Call->getArgOperand(I); | |||
| VarArgs.push_back(ArgVal); | |||
| bool isByVal = Call->getAttributes().hasAttribute(I + 1, Attribute::ByVal); | |||
| // For "byval" arguments we must dereference the pointer. | |||
| VarArgsTypes.push_back(isByVal ? ArgVal->getType()->getPointerElementType() | |||
| : ArgVal->getType()); | |||
| } | |||
| if (VarArgsTypes.size() == 0) { | |||
| // Some buggy code (e.g. 176.gcc in Spec2k) uses va_arg on an | |||
| // empty argument list, which gives undefined behaviour in C. To | |||
| // work around such programs, we create a dummy varargs buffer on | |||
| // the stack even though there are no arguments to put in it. | |||
| // This allows va_arg to read an undefined value from the stack | |||
| // rather than crashing by reading from an uninitialized pointer. | |||
| // An alternative would be to pass a null pointer to catch the | |||
| // invalid use of va_arg. | |||
| VarArgsTypes.push_back(Type::getInt32Ty(Ctx)); | |||
| } | |||
|
|
|||
| // Create struct type for packing variable arguments into. | |||
| StructType *VarArgsTy = StructType::get(Ctx, VarArgsTypes); | |||
|
|
|||
| // Allocate space for the variable argument buffer. Do this at the | |||
| // start of the function so that we don't leak space if the function | |||
| // is called in a loop. | |||
| IRBuilder<> IRB(&*F->getEntryBlock().getFirstInsertionPt()); | |||
| auto *Buf = IRB.CreateAlloca(VarArgsTy, nullptr, "vararg_buffer"); | |||
|
|
|||
| // Call llvm.lifetime.start/end intrinsics to indicate that Buf is | |||
| // only used for the duration of the function call, so that the | |||
| // stack space can be reused elsewhere. | |||
| auto LifetimeStart = Intrinsic::getDeclaration(M, Intrinsic::lifetime_start); | |||
| auto LifetimeEnd = Intrinsic::getDeclaration(M, Intrinsic::lifetime_end); | |||
| auto *I8Ptr = Type::getInt8Ty(Ctx)->getPointerTo(); | |||
| auto *BufPtr = IRB.CreateBitCast(Buf, I8Ptr, "vararg_lifetime_bitcast"); | |||
| auto *BufSize = | |||
| ConstantInt::get(Ctx, APInt(64, DL->getTypeAllocSize(VarArgsTy))); | |||
| IRB.CreateCall(LifetimeStart, {BufSize, BufPtr}); | |||
|
|
|||
| // Copy variable arguments into buffer. | |||
| int Index = 0; | |||
| IRB.SetInsertPoint(Call); | |||
| for (Value *Arg : VarArgs) { | |||
| Value *Indexes[] = {ConstantInt::get(Ctx, APInt(32, 0)), | |||
| ConstantInt::get(Ctx, APInt(32, Index))}; | |||
| Value *Ptr = IRB.CreateInBoundsGEP(Buf, Indexes, "vararg_ptr"); | |||
| bool isByVal = Call->getAttributes().hasAttribute( | |||
| FuncType->getNumParams() + Index + 1, Attribute::ByVal); | |||
| if (isByVal) | |||
| IRB.CreateMemCpy(Ptr, Arg, DL->getTypeAllocSize( | |||
| Arg->getType()->getPointerElementType()), | |||
| /*Align=*/1); | |||
| else | |||
| IRB.CreateStore(Arg, Ptr); | |||
| ++Index; | |||
| } | |||
|
|
|||
| // Cast function to new type to add our extra pointer argument. | |||
| SmallVector<Type *, 8> ArgTypes(FuncType->param_begin(), | |||
| FuncType->param_end()); | |||
| ArgTypes.push_back(VarArgsTy->getPointerTo()); | |||
| FunctionType *NFTy = FunctionType::get(FuncType->getReturnType(), ArgTypes, | |||
| /*isVarArg=*/false); | |||
| Value *CastFunc = IRB.CreateBitCast(Call->getCalledValue(), | |||
| NFTy->getPointerTo(), "vararg_func"); | |||
|
|
|||
| // Create the converted function call. | |||
| FixedArgs.push_back(Buf); | |||
| Instruction *NewCall; | |||
| if (auto *C = dyn_cast<CallInst>(Call)) { | |||
| auto *N = IRB.CreateCall(CastFunc, FixedArgs); | |||
| N->setAttributes(AttributeSet::get(Ctx, Attrs)); | |||
| NewCall = N; | |||
| IRB.CreateCall(LifetimeEnd, {BufSize, BufPtr}); | |||
| } else if (auto *C = dyn_cast<InvokeInst>(Call)) { | |||
| auto *N = IRB.CreateInvoke(CastFunc, C->getNormalDest(), C->getUnwindDest(), | |||
| FixedArgs, C->getName()); | |||
| N->setAttributes(AttributeSet::get(Ctx, Attrs)); | |||
| (IRBuilder<>(&*C->getNormalDest()->getFirstInsertionPt())) | |||
| .CreateCall(LifetimeEnd, {BufSize, BufPtr}); | |||
| (IRBuilder<>(&*C->getUnwindDest()->getFirstInsertionPt())) | |||
| .CreateCall(LifetimeEnd, {BufSize, BufPtr}); | |||
| NewCall = N; | |||
| } else { | |||
| llvm_unreachable("not a call/invoke"); | |||
| } | |||
|
|
|||
| NewCall->takeName(Call); | |||
| Call->replaceAllUsesWith(NewCall); | |||
| Call->eraseFromParent(); | |||
|
|
|||
| return true; | |||
| } | |||
|
|
|||
| bool ExpandVarArgs::runOnModule(Module &M) { | |||
| bool Changed = false; | |||
| DataLayout DL(&M); | |||
|
|
|||
| for (auto MI = M.begin(), ME = M.end(); MI != ME;) { | |||
| Function *F = &*MI++; | |||
| for (BasicBlock &BB : *F) { | |||
| for (auto BI = BB.begin(), BE = BB.end(); BI != BE;) { | |||
| Instruction *I = &*BI++; | |||
| if (auto *VI = dyn_cast<VAArgInst>(I)) { | |||
| Changed = true; | |||
| ExpandVAArgInst(VI, &DL); | |||
| } else if (auto *VAE = dyn_cast<VAEndInst>(I)) { | |||
| Changed = true; | |||
| ExpandVAEnd(VAE); | |||
| } else if (auto *VAC = dyn_cast<VACopyInst>(I)) { | |||
| Changed = true; | |||
| ExpandVACopyInst(VAC); | |||
| } else if (auto *Call = dyn_cast<CallInst>(I)) { | |||
| Changed |= ExpandVarArgCall(&M, Call, &DL); | |||
| } else if (auto *Call = dyn_cast<InvokeInst>(I)) { | |||
| Changed |= ExpandVarArgCall(&M, Call, &DL); | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| if (F->isVarArg()) | |||
| Changed |= ExpandVarArgFunc(&M, F); | |||
| } | |||
|
|
|||
| return Changed; | |||
| } | |||
|
|
|||
| ModulePass *llvm::createExpandVarArgsPass() { return new ExpandVarArgs(); } | |||
| @@ -0,0 +1,264 @@ | |||
| //===- FixVectorLoadStoreAlignment.cpp - Vector load/store alignment ------===// | |||
| // | |||
| // The LLVM Compiler Infrastructure | |||
| // | |||
| // This file is distributed under the University of Illinois Open Source | |||
| // License. See LICENSE.TXT for details. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
| // | |||
| // Fix vector load/store alignment by: | |||
| // - Leaving as-is if the alignment is equal to the vector's element width. | |||
| // - Reducing the alignment to vector's element width if it's greater and the | |||
| // current alignment is a factor of the element alignment. | |||
| // - Scalarizing if the alignment is smaller than the element-wise alignment. | |||
| // | |||
| // Volatile vector load/store are handled the same, and can therefore be broken | |||
| // up as allowed by C/C++. | |||
| // | |||
| // TODO(jfb) Atomic accesses cause errors at compile-time. This could be | |||
| // implemented as a call to the C++ runtime, since 128-bit atomics | |||
| // aren't usually lock-free. | |||
| // | |||
| //===----------------------------------------------------------------------===// | |||
|
|
|||
| #include "llvm/IR/DataLayout.h" | |||
| #include "llvm/IR/IRBuilder.h" | |||
| #include "llvm/IR/Instruction.h" | |||
| #include "llvm/IR/Instructions.h" | |||
| #include "llvm/IR/Module.h" | |||
| #include "llvm/Pass.h" | |||
| #include "llvm/Support/Debug.h" | |||
| #include "llvm/Support/MathExtras.h" | |||
| #include "llvm/Support/raw_ostream.h" | |||
| #include "llvm/Transforms/NaCl.h" | |||
|
|
|||
| using namespace llvm; | |||
|
|
|||
| namespace { | |||
| class FixVectorLoadStoreAlignment : public BasicBlockPass { | |||
| public: | |||
| static char ID; // Pass identification, replacement for typeid | |||
| FixVectorLoadStoreAlignment() : BasicBlockPass(ID), M(0), DL(0) { | |||
| initializeFixVectorLoadStoreAlignmentPass(*PassRegistry::getPassRegistry()); | |||
| } | |||
| using BasicBlockPass::doInitialization; | |||
| bool doInitialization(Module &Mod) override { | |||
| M = &Mod; | |||
| return false; // Unchanged. | |||
| } | |||
| bool runOnBasicBlock(BasicBlock &BB) override; | |||
|
|
|||
| private: | |||
| typedef SmallVector<Instruction *, 8> Instructions; | |||
| const Module *M; | |||
| const DataLayout *DL; | |||
|
|
|||
| /// Some sub-classes of Instruction have a non-virtual function | |||
| /// indicating which operand is the pointer operand. This template | |||
| /// function returns the pointer operand's type, and requires that | |||
| /// InstTy have a getPointerOperand function. | |||
| template <typename InstTy> | |||
| static PointerType *pointerOperandType(const InstTy *I) { | |||
| return cast<PointerType>(I->getPointerOperand()->getType()); | |||
| } | |||
|
|
|||
| /// Similar to pointerOperandType, this template function checks | |||
| /// whether the pointer operand is a pointer to a vector type. | |||
| template <typename InstTy> | |||
| static bool pointerOperandIsVectorPointer(const Instruction *I) { | |||
| return pointerOperandType(cast<InstTy>(I))->getElementType()->isVectorTy(); | |||
| } | |||
|
|
|||
| /// Returns true if one of the Instruction's operands is a pointer to | |||
| /// a vector type. This is more general than the above and assumes we | |||
| /// don't know which Instruction type is provided. | |||
| static bool hasVectorPointerOperand(const Instruction *I) { | |||
| for (User::const_op_iterator IB = I->op_begin(), IE = I->op_end(); IB != IE; | |||
| ++IB) | |||
| if (PointerType *PtrTy = dyn_cast<PointerType>((*IB)->getType())) | |||
| if (isa<VectorType>(PtrTy->getElementType())) | |||
| return true; | |||
| return false; | |||
| } | |||
|
|
|||
| /// Vectors are expected to be element-aligned. If they are, leave as-is; if | |||
| /// the alignment is too much then narrow the alignment (when possible); | |||
| /// otherwise return false. | |||
| template <typename InstTy> | |||
| static bool tryFixVectorAlignment(const DataLayout *DL, Instruction *I) { | |||
| InstTy *LoadStore = cast<InstTy>(I); | |||
| VectorType *VecTy = | |||
| cast<VectorType>(pointerOperandType(LoadStore)->getElementType()); | |||
| Type *ElemTy = VecTy->getElementType(); | |||
| uint64_t ElemBitSize = DL->getTypeSizeInBits(ElemTy); | |||
| uint64_t ElemByteSize = ElemBitSize / CHAR_BIT; | |||
| uint64_t CurrentByteAlign = LoadStore->getAlignment(); | |||
| bool isABIAligned = CurrentByteAlign == 0; | |||
| uint64_t VecABIByteAlign = DL->getABITypeAlignment(VecTy); | |||
| CurrentByteAlign = isABIAligned ? VecABIByteAlign : CurrentByteAlign; | |||
|
|
|||
| if (CHAR_BIT * ElemByteSize != ElemBitSize) | |||
| return false; // Minimum byte-size elements. | |||
| if (MinAlign(ElemByteSize, CurrentByteAlign) == ElemByteSize) { | |||
| // Element-aligned, or compatible over-aligned. Keep element-aligned. | |||
| LoadStore->setAlignment(ElemByteSize); | |||
| return true; | |||
| } | |||
| return false; // Under-aligned. | |||
| } | |||
|
|
|||
| void visitVectorLoadStore(BasicBlock &BB, Instructions &Loads, | |||
| Instructions &Stores) const; | |||
| void scalarizeVectorLoadStore(BasicBlock &BB, const Instructions &Loads, | |||
| const Instructions &Stores) const; | |||
| }; | |||
| } // anonymous namespace | |||
|
|
|||
| char FixVectorLoadStoreAlignment::ID = 0; | |||
| INITIALIZE_PASS(FixVectorLoadStoreAlignment, "fix-vector-load-store-alignment", | |||
| "Ensure vector load/store have element-size alignment", | |||
| false, false) | |||
|
|
|||
| void FixVectorLoadStoreAlignment::visitVectorLoadStore( | |||
| BasicBlock &BB, Instructions &Loads, Instructions &Stores) const { | |||
| for (BasicBlock::iterator BBI = BB.begin(), BBE = BB.end(); BBI != BBE; | |||
| ++BBI) { | |||
| Instruction *I = &*BBI; | |||
| // The following list of instructions is based on mayReadOrWriteMemory. | |||
| switch (I->getOpcode()) { | |||
| case Instruction::Load: | |||
| if (pointerOperandIsVectorPointer<LoadInst>(I)) { | |||
| if (cast<LoadInst>(I)->isAtomic()) | |||
| report_fatal_error("unhandled: atomic vector store"); | |||
| if (!tryFixVectorAlignment<LoadInst>(DL, I)) | |||
| Loads.push_back(I); | |||
| } | |||
| break; | |||
| case Instruction::Store: | |||
| if (pointerOperandIsVectorPointer<StoreInst>(I)) { | |||
| if (cast<StoreInst>(I)->isAtomic()) | |||
| report_fatal_error("unhandled: atomic vector store"); | |||
| if (!tryFixVectorAlignment<StoreInst>(DL, I)) | |||
| Stores.push_back(I); | |||
| } | |||
| break; | |||
| case Instruction::Alloca: | |||
| case Instruction::Fence: | |||
| case Instruction::VAArg: | |||
| // Leave these memory operations as-is, even when they deal with | |||
| // vectors. | |||
| break; | |||
| case Instruction::Call: | |||
| case Instruction::Invoke: | |||
| // Call/invoke don't touch memory per-se, leave them as-is. | |||
| break; | |||
| case Instruction::AtomicCmpXchg: | |||
| if (pointerOperandIsVectorPointer<AtomicCmpXchgInst>(I)) | |||
| report_fatal_error( | |||
| "unhandled: atomic compare and exchange operation on vector"); | |||
| break; | |||
| case Instruction::AtomicRMW: | |||
| if (pointerOperandIsVectorPointer<AtomicRMWInst>(I)) | |||
| report_fatal_error("unhandled: atomic RMW operation on vector"); | |||
| break; | |||
| default: | |||
| if (I->mayReadOrWriteMemory() && hasVectorPointerOperand(I)) { | |||
| errs() << "Not handled: " << *I << '\n'; | |||
| report_fatal_error( | |||
| "unexpected: vector operations which may read/write memory"); | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| } | |||
|
|
|||
| void FixVectorLoadStoreAlignment::scalarizeVectorLoadStore( | |||
| BasicBlock &BB, const Instructions &Loads, | |||
| const Instructions &Stores) const { | |||
| for (Instructions::const_iterator IB = Loads.begin(), IE = Loads.end(); | |||
| IB != IE; ++IB) { | |||
| LoadInst *VecLoad = cast<LoadInst>(*IB); | |||
| VectorType *LoadedVecTy = | |||
| cast<VectorType>(pointerOperandType(VecLoad)->getElementType()); | |||
| Type *ElemTy = LoadedVecTy->getElementType(); | |||
|
|
|||
| // The base of the vector is as aligned as the vector load (where | |||
| // zero means ABI alignment for the vector), whereas subsequent | |||
| // elements are as aligned as the base+offset can be. | |||
| unsigned BaseAlign = VecLoad->getAlignment() | |||
| ? VecLoad->getAlignment() | |||
| : DL->getABITypeAlignment(LoadedVecTy); | |||
| unsigned ElemAllocSize = DL->getTypeAllocSize(ElemTy); | |||
|
|
|||
| // Fill in the vector element by element. | |||
| IRBuilder<> IRB(VecLoad); | |||
| Value *Loaded = UndefValue::get(LoadedVecTy); | |||
| Value *Base = | |||
| IRB.CreateBitCast(VecLoad->getPointerOperand(), ElemTy->getPointerTo()); | |||
|
|
|||
| for (unsigned Elem = 0, NumElems = LoadedVecTy->getNumElements(); | |||
| Elem != NumElems; ++Elem) { | |||
| unsigned Align = MinAlign(BaseAlign, ElemAllocSize * Elem); | |||
| Value *GEP = IRB.CreateConstInBoundsGEP1_32(ElemTy, Base, Elem); | |||
| LoadInst *LoadedElem = | |||
| IRB.CreateAlignedLoad(GEP, Align, VecLoad->isVolatile()); | |||
| LoadedElem->setSynchScope(VecLoad->getSynchScope()); | |||
| Loaded = IRB.CreateInsertElement( | |||
| Loaded, LoadedElem, | |||
| ConstantInt::get(Type::getInt32Ty(M->getContext()), Elem)); | |||
| } | |||
|
|
|||
| VecLoad->replaceAllUsesWith(Loaded); | |||
| VecLoad->eraseFromParent(); | |||
| } | |||
|
|
|||
| for (Instructions::const_iterator IB = Stores.begin(), IE = Stores.end(); | |||
| IB != IE; ++IB) { | |||
| StoreInst *VecStore = cast<StoreInst>(*IB); | |||
| Value *StoredVec = VecStore->getValueOperand(); | |||
| VectorType *StoredVecTy = cast<VectorType>(StoredVec->getType()); | |||
| Type *ElemTy = StoredVecTy->getElementType(); | |||
|
|
|||
| unsigned BaseAlign = VecStore->getAlignment() | |||
| ? VecStore->getAlignment() | |||
| : DL->getABITypeAlignment(StoredVecTy); | |||
| unsigned ElemAllocSize = DL->getTypeAllocSize(ElemTy); | |||
|
|
|||
| // Fill in the vector element by element. | |||
| IRBuilder<> IRB(VecStore); | |||
| Value *Base = IRB.CreateBitCast(VecStore->getPointerOperand(), | |||
| ElemTy->getPointerTo()); | |||
|
|
|||
| for (unsigned Elem = 0, NumElems = StoredVecTy->getNumElements(); | |||
| Elem != NumElems; ++Elem) { | |||
| unsigned Align = MinAlign(BaseAlign, ElemAllocSize * Elem); | |||
| Value *GEP = IRB.CreateConstInBoundsGEP1_32(ElemTy, Base, Elem); | |||
| Value *ElemToStore = IRB.CreateExtractElement( | |||
| StoredVec, ConstantInt::get(Type::getInt32Ty(M->getContext()), Elem)); | |||
| StoreInst *StoredElem = IRB.CreateAlignedStore(ElemToStore, GEP, Align, | |||
| VecStore->isVolatile()); | |||
| StoredElem->setSynchScope(VecStore->getSynchScope()); | |||
| } | |||
|
|
|||
| VecStore->eraseFromParent(); | |||
| } | |||
| } | |||
|
|
|||
| bool FixVectorLoadStoreAlignment::runOnBasicBlock(BasicBlock &BB) { | |||
| bool Changed = false; | |||
| if (!DL) | |||
| DL = &BB.getParent()->getParent()->getDataLayout(); | |||
| Instructions Loads; | |||
| Instructions Stores; | |||
| visitVectorLoadStore(BB, Loads, Stores); | |||
| if (!(Loads.empty() && Stores.empty())) { | |||
| Changed = true; | |||
| scalarizeVectorLoadStore(BB, Loads, Stores); | |||
| } | |||
| return Changed; | |||
| } | |||
|
|
|||
| BasicBlockPass *llvm::createFixVectorLoadStoreAlignmentPass() { | |||
| return new FixVectorLoadStoreAlignment(); | |||
| } | |||