| Index: lib/Transforms/NaCl/ExpandByVal.cpp
|
| diff --git a/lib/Transforms/NaCl/ExpandByVal.cpp b/lib/Transforms/NaCl/ExpandByVal.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..cc2c73325eac25f33e9c85f7696556bb3d465854
|
| --- /dev/null
|
| +++ b/lib/Transforms/NaCl/ExpandByVal.cpp
|
| @@ -0,0 +1,191 @@
|
| +//===- 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->isAlignAttribute() &&
|
| + Attr->getKindAsEnum() != Attribute::ByVal &&
|
| + Attr->getKindAsEnum() != Attribute::StructRet) {
|
| + AB.addAttribute(*Attr);
|
| + }
|
| + }
|
| + 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)));
|
| + unsigned Alignment = Attrs.getParamAlignment(AttrIdx);
|
| + // 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
|
| + unsigned AllocAlignment =
|
| + std::max(Alignment, DL->getABITypeAlignment(ArgType));
|
| +
|
| + // Make a copy of the byval argument.
|
| + Instruction *CopyBuf = new AllocaInst(ArgType, 0, AllocAlignment,
|
| + 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();
|
| +}
|
|
|