Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(185)

Unified Diff: lib/Transforms/NaCl/ExpandByVal.cpp

Issue 13973018: PNaCl: Add ExpandByVal pass for expanding out by-value struct args and results (Closed) Base URL: http://git.chromium.org/native_client/pnacl-llvm.git@master
Patch Set: Cleanup Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/Transforms/NaCl/CMakeLists.txt ('k') | test/Transforms/NaCl/expand-byval.ll » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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();
+}
« no previous file with comments | « lib/Transforms/NaCl/CMakeLists.txt ('k') | test/Transforms/NaCl/expand-byval.ll » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698