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

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

Issue 939073008: Rebased PNaCl localmods in LLVM to 223109 (Closed)
Patch Set: undo localmod Created 5 years, 10 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/ExpandSmallArguments.cpp ('k') | lib/Transforms/NaCl/ExpandTls.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/Transforms/NaCl/ExpandStructRegs.cpp
diff --git a/lib/Transforms/NaCl/ExpandStructRegs.cpp b/lib/Transforms/NaCl/ExpandStructRegs.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cb1e0fafdd91309844cba6d6ce6b40452403ed5b
--- /dev/null
+++ b/lib/Transforms/NaCl/ExpandStructRegs.cpp
@@ -0,0 +1,461 @@
+//===- ExpandStructRegs.cpp - Expand out variables with struct type--------===//
+//
+// 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 some uses of LLVM variables
+// (a.k.a. registers) of struct type. It replaces loads and stores of
+// structs with separate loads and stores of the structs' fields. The
+// motivation is to omit struct types from PNaCl's stable ABI.
+//
+// ExpandStructRegs does not yet handle all possible uses of struct
+// values. It is intended to handle the uses that Clang and the SROA
+// pass generate. Clang generates struct loads and stores, along with
+// extractvalue instructions, in its implementation of C++ method
+// pointers, and the SROA pass sometimes converts this code to using
+// insertvalue instructions too.
+//
+// ExpandStructRegs does not handle:
+//
+// * Array types.
+// * Function types containing arguments or return values of struct
+// type without the "byval" or "sret" attributes. Since by-value
+// struct-passing generally uses "byval"/"sret", this does not
+// matter.
+//
+// Other limitations:
+//
+// * ExpandStructRegs does not attempt to use memcpy() where that
+// might be more appropriate than copying fields individually.
+// * ExpandStructRegs does not preserve the contents of padding
+// between fields when copying structs. However, the contents of
+// padding fields are not defined anyway.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/DataLayout.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/NaCl.h"
+
+using namespace llvm;
+
+namespace {
+struct ExpandStructRegs : public FunctionPass {
+ static char ID; // Pass identification, replacement for typeid
+ ExpandStructRegs() : FunctionPass(ID) {
+ initializeExpandStructRegsPass(*PassRegistry::getPassRegistry());
+ }
+
+ virtual bool runOnFunction(Function &F);
+};
+}
+
+char ExpandStructRegs::ID = 0;
+INITIALIZE_PASS(ExpandStructRegs, "expand-struct-regs",
+ "Expand out variables with struct types", false, false)
+
+static bool DoAnotherPass(Type *Ty) { return isa<StructType>(Ty); }
+static bool DoAnotherPass(Value *V) { return DoAnotherPass(V->getType()); }
+
+static bool SplitUpPHINode(PHINode *Phi) {
+ StructType *STy = cast<StructType>(Phi->getType());
+
+ Value *NewStruct = UndefValue::get(STy);
+ Instruction *NewStructInsertPt = Phi->getParent()->getFirstInsertionPt();
+
+ bool NeedsAnotherPass = false;
+
+ // Create a separate PHINode for each struct field.
+ for (unsigned Index = 0; Index < STy->getNumElements(); ++Index) {
+ SmallVector<unsigned, 1> EVIndexes;
+ EVIndexes.push_back(Index);
+
+ Type *ElemTy = STy->getElementType(Index);
+ NeedsAnotherPass = NeedsAnotherPass || DoAnotherPass(ElemTy);
+
+ PHINode *NewPhi = PHINode::Create(ElemTy, Phi->getNumIncomingValues(),
+ Phi->getName() + ".index", Phi);
+ CopyDebug(NewPhi, Phi);
+ for (unsigned PhiIndex = 0; PhiIndex < Phi->getNumIncomingValues();
+ ++PhiIndex) {
+ BasicBlock *IncomingBB = Phi->getIncomingBlock(PhiIndex);
+ Value *EV = CopyDebug(
+ ExtractValueInst::Create(Phi->getIncomingValue(PhiIndex), EVIndexes,
+ Phi->getName() + ".extract",
+ IncomingBB->getTerminator()),
+ Phi);
+ NewPhi->addIncoming(EV, IncomingBB);
+ }
+
+ // Reconstruct the original struct value.
+ NewStruct = CopyDebug(InsertValueInst::Create(NewStruct, NewPhi, EVIndexes,
+ Phi->getName() + ".insert",
+ NewStructInsertPt),
+ Phi);
+ }
+ Phi->replaceAllUsesWith(NewStruct);
+ Phi->eraseFromParent();
+
+ return NeedsAnotherPass;
+}
+
+static bool SplitUpSelect(SelectInst *Select) {
+ StructType *STy = cast<StructType>(Select->getType());
+ Value *NewStruct = UndefValue::get(STy);
+
+ bool NeedsAnotherPass = false;
+ // Create a separate SelectInst for each struct field.
+ for (unsigned Index = 0; Index < STy->getNumElements(); ++Index) {
+ SmallVector<unsigned, 1> EVIndexes;
+ EVIndexes.push_back(Index);
+
+ Value *TrueVal = CopyDebug(
+ ExtractValueInst::Create(Select->getTrueValue(), EVIndexes,
+ Select->getName() + ".extract", Select),
+ Select);
+ Value *FalseVal = CopyDebug(
+ ExtractValueInst::Create(Select->getFalseValue(), EVIndexes,
+ Select->getName() + ".extract", Select),
+ Select);
+ Value *NewSelect =
+ CopyDebug(SelectInst::Create(Select->getCondition(), TrueVal, FalseVal,
+ Select->getName() + ".index", Select),
+ Select);
+
+ NeedsAnotherPass = NeedsAnotherPass || DoAnotherPass(NewSelect);
+
+ // Reconstruct the original struct value.
+ NewStruct = CopyDebug(
+ InsertValueInst::Create(NewStruct, NewSelect, EVIndexes,
+ Select->getName() + ".insert", Select),
+ Select);
+ }
+ Select->replaceAllUsesWith(NewStruct);
+ Select->eraseFromParent();
+
+ return NeedsAnotherPass;
+}
+
+template <class InstType>
+static void ProcessLoadOrStoreAttrs(InstType *Dest, InstType *Src,
+ StructType* STy, const unsigned Index) {
+ CopyDebug(Dest, Src);
+ Dest->setVolatile(Src->isVolatile());
+ if (Src->isAtomic()) {
+ errs() << "Use: " << *Src << "\n";
+ report_fatal_error("Atomic struct loads/stores not supported");
+ }
+
+ if (!Src->getAlignment()) {
+ return;
+ }
+
+ const DataLayout *DL = Src->getParent()->getDataLayout();
+ if (!DL) {
+ report_fatal_error("Need DataLayout");
+ }
+ const StructLayout *SL = DL->getStructLayout(STy);
+ const unsigned Alignment = Src->getAlignment();
+ Dest->setAlignment(MinAlign(Alignment, SL->getElementOffset(Index)));
+}
+
+static bool SplitUpStore(StoreInst *Store) {
+ StructType *STy = cast<StructType>(Store->getValueOperand()->getType());
+
+ bool NeedsAnotherPass = false;
+ // Create a separate store instruction for each struct field.
+ for (unsigned Index = 0; Index < STy->getNumElements(); ++Index) {
+ SmallVector<Value *, 2> Indexes;
+ Indexes.push_back(ConstantInt::get(Store->getContext(), APInt(32, 0)));
+ Indexes.push_back(ConstantInt::get(Store->getContext(), APInt(32, Index)));
+ Value *GEP =
+ CopyDebug(GetElementPtrInst::Create(
+ Store->getPointerOperand(), Indexes,
+ Store->getPointerOperand()->getName() + ".index", Store),
+ Store);
+ NeedsAnotherPass =
+ NeedsAnotherPass || DoAnotherPass(GEP->getType()->getContainedType(0));
+
+ SmallVector<unsigned, 1> EVIndexes;
+ EVIndexes.push_back(Index);
+ Value *Field = ExtractValueInst::Create(Store->getValueOperand(), EVIndexes,
+ "", Store);
+ StoreInst *NewStore = new StoreInst(Field, GEP, Store);
+ ProcessLoadOrStoreAttrs(NewStore, Store, STy, Index);
+ }
+ Store->eraseFromParent();
+
+ return NeedsAnotherPass;
+}
+
+static bool SplitUpLoad(LoadInst *Load) {
+ StructType *STy = cast<StructType>(Load->getType());
+ Value *NewStruct = UndefValue::get(STy);
+
+ bool NeedsAnotherPass = false;
+ // Create a separate load instruction for each struct field.
+ for (unsigned Index = 0; Index < STy->getNumElements(); ++Index) {
+ SmallVector<Value *, 2> Indexes;
+ Indexes.push_back(ConstantInt::get(Load->getContext(), APInt(32, 0)));
+ Indexes.push_back(ConstantInt::get(Load->getContext(), APInt(32, Index)));
+ Value *GEP =
+ CopyDebug(GetElementPtrInst::Create(Load->getPointerOperand(), Indexes,
+ Load->getName() + ".index", Load),
+ Load);
+ LoadInst *NewLoad = new LoadInst(GEP, Load->getName() + ".field", Load);
+
+ NeedsAnotherPass = NeedsAnotherPass || DoAnotherPass(NewLoad);
+ ProcessLoadOrStoreAttrs(NewLoad, Load, STy, Index);
+
+ // Reconstruct the struct value.
+ SmallVector<unsigned, 1> EVIndexes;
+ EVIndexes.push_back(Index);
+ NewStruct =
+ CopyDebug(InsertValueInst::Create(NewStruct, NewLoad, EVIndexes,
+ Load->getName() + ".insert", Load),
+ Load);
+ }
+ Load->replaceAllUsesWith(NewStruct);
+ Load->eraseFromParent();
+
+ return NeedsAnotherPass;
+}
+
+static bool ExpandExtractValue(ExtractValueInst *EV,
+ SmallVectorImpl<Instruction *> *ToErase) {
+ // Search for the insertvalue instruction that inserts the struct field
+ // referenced by this extractvalue instruction, excluding CmpXchg which
+ // returns a struct and is handled by RewriteAtomics.
+ Value *StructVal = EV->getAggregateOperand();
+ Value *ResultField = nullptr;
+
+ // The current depth of the search. It's impossible to backtrack in our search
+ // tree (all prior (not in the CFG sense) extractvalues will already be
+ // expanded), so this variable is never reset to zero.
+ size_t EVIndex = 0;
+
+ if (isa<AtomicCmpXchgInst>(StructVal))
+ return false;
+
+ for (;;) {
+ if (InsertValueInst *IV = dyn_cast<InsertValueInst>(StructVal)) {
+
+ size_t IVIndex = 0;
+ for (; EVIndex < EV->getIndices().size() &&
+ IVIndex < IV->getIndices().size();
+ ++IVIndex, ++EVIndex) {
+
+ const bool Equal =
+ (EV->getIndices()[EVIndex] == IV->getIndices()[IVIndex]);
+
+ if (IVIndex + 1 == IV->getIndices().size() && Equal) {
+ if (EVIndex + 1 == EV->getIndices().size()) {
+ // Exact match. We break out of all loops and ResultField will
+ // replace EV.
+ ResultField = IV->getInsertedValueOperand();
+ } else {
+ // We've found a match, but haven't reached the end of EV's indexes.
+ // We continue looping through the outermost loop, and search for
+ // indices on the next level down (ie we increment EVIndex).
+ // This branch is common when encountering nested insertvalues; for
+ // example:
+ // ```llvm
+ // %1 = insertvalue { i32 } undef, i32 1, 0
+ // %2 = insertvalue { { i32 } } %1, { i32 } %1, 0
+ // %3 = extractvalue { { i32 } } %2, 0, 0
+ // ```
+ StructVal = IV->getInsertedValueOperand();
+ ++EVIndex;
+ }
+ break;
+ } else if (!Equal) {
+ // No match. Try the next struct value in the chain.
+ // For example:
+ // ```llvm
+ // %1 = insertvalue { i32, i32, i32 } undef, i32 5, 0
+ // %2 = insertvalue { i32, i32, i32 } %1, i32 10, 1
+ // %3 = insertvalue { i32, i32, i32 } %2, i32 15, 2
+ // %4 = extractvalue { i32, i32, i32 } %3, 0
+ // ```
+ // In this case, to expand %4, this branch will hit insertvalues %3
+ // and %2 before
+ // it finds the solution, %1.
+ StructVal = IV->getAggregateOperand();
+ break;
+ }
+
+ // One last case worth mentioning:
+ // ```llvm
+ // %aa = alloca { i32 }
+ // %a = insertvalue { i32 } undef, i32 1, 0
+ // %b = insertvalue { { i32 } } undef, { i32 } %a, 0
+ // %c = extractvalue { { i32 } } %b, 0
+ // store { i32 } %c, { i32 }* %aa
+ // ```
+ // In the case of %c, the condition of our inner loop will be false, and
+ // we will fall into (EVIndex == EV->getIndices().size())
+ // Note that in this case, SplitStore will have inserted an extra
+ // extractvalue and GEP:
+ // ```llvm
+ // %aa = alloca { i32 }
+ // %a = insertvalue { i32 } undef, i32 1, 0
+ // %b = insertvalue { { i32 } } undef, { i32 } %a, 0
+ // %c.extractval = extractvalue { i32 } %a, 0
+ // %aa.index = getelementptr { i32 }* %aa, i32 0, i32 0
+ // store i32 %c, i32* %aa.index
+ // ```
+ }
+ if (ResultField) {
+ // \O/ We're done with this ExtractValueInst!
+ break;
+ } else if (EVIndex == EV->getIndices().size()) {
+ // We've found an insertvalue that inserts at one or more levels deeper
+ // than this extractvalue. For example (borrowed from the tests), where
+ // %h is EV && %e is IV:
+ // ```llvm
+ // %e = insertvalue { { { i32, i64 } }, i64 } undef, { i32, i64 } %b, 0, 0
+ // %h = extractvalue { { { i32, i64 } }, i64 } %e, 0
+ // ; later on..
+ // %1 = extractvalue { { i32, i64 } } %h, 0
+ // ```
+ // This expands to:
+ // ```llvm
+ // %e = insertvalue { { { i32, i64 } }, i64 } undef, { i32, i64 } %b, 0, 0
+ // %1 = insertvalue { { i32, i64 } } undef, { i32, i64 } %b, 0
+ // %h = extractvalue { { { i32, i64 } }, i64 } %e, 0
+ // %2 = extractvalue { { i32, i64 } } %h, 0
+ // ```
+ // Then, outside the outer loop, %h is deleted:
+ // ```llvm
+ // %e = insertvalue { { { i32, i64 } }, i64 } undef, { i32, i64 } %b, 0, 0
+ // %1 = insertvalue { { i32, i64 } } undef, { i32, i64 } %b, 0
+ // %2 = extractvalue { { i32, i64 } } %1, 0
+ // ```
+ // %2 will be expanded at a later point.
+ // This branch used the second index in %e to create %1 (because %2 &&
+ // %e's first indices where equal).
+ //
+ // Additionally, it's impossible to not change StructVal && not hit this
+ // branch (but the reverse is not true!).
+
+ SmallVector<unsigned, 4> Indices(IV->getIndices().begin() + IVIndex,
+ IV->getIndices().end());
+
+ InsertValueInst *Insert = InsertValueInst::Create(
+ UndefValue::get(EV->getType()), IV->getInsertedValueOperand(),
+ Indices, "", EV);
+ ToErase->push_back(Insert);
+ ResultField = CopyDebug(Insert, EV);
+ break;
+ }
+
+ // At this point, StructVal must be changed.
+ } else if (Constant *C = dyn_cast<Constant>(StructVal)) {
+ SmallVector<unsigned, 4> Indices(EV->getIndices().begin() + EVIndex,
+ EV->getIndices().end());
+ ResultField = ConstantExpr::getExtractValue(C, Indices);
+ break;
+ } else if (isa<LoadInst>(StructVal)) {
+ ResultField = StructVal;
+ break;
+ } else {
+ errs() << "Value: " << *StructVal << "\n";
+ report_fatal_error("Unrecognized struct value");
+ }
+ }
+
+ assert(ResultField); // Failsafe.
+ EV->replaceAllUsesWith(ResultField);
+ EV->eraseFromParent();
+ return true;
+}
+
+static bool ExpandExtractValues(Function &Func) {
+ bool Changed = false;
+
+ SmallVector<Instruction *, 10> ToErase;
+ // Expand out all the extractvalue instructions. Also collect up
+ // the insertvalue instructions for later deletion so that we do not
+ // need to make extra passes across the whole function.
+
+ for (auto &BB : Func) {
+ for (BasicBlock::iterator Iter = BB.begin(), E = BB.end(); Iter != E;) {
+ Instruction *Inst = Iter++;
+ if (ExtractValueInst *EV = dyn_cast<ExtractValueInst>(Inst)) {
+ Changed |= ExpandExtractValue(EV, &ToErase);
+ } else if (isa<InsertValueInst>(Inst)) {
+ ToErase.push_back(Inst);
+ Changed = true;
+ }
+ }
+ }
+
+ // Delete the insertvalue instructions. These can reference each
+ // other, so we must do dropAllReferences() before doing
+ // eraseFromParent(), otherwise we will try to erase instructions
+ // that are still referenced.
+ for (Instruction *I : ToErase) {
+ I->dropAllReferences();
+ }
+
+ for (Instruction *I : ToErase) {
+ I->eraseFromParent();
+ }
+
+ return Changed;
+}
+
+bool ExpandStructRegs::runOnFunction(Function &Func) {
+ bool Changed = false;
+ bool NeedsAnotherPass;
+
+ do {
+ NeedsAnotherPass = false;
+ // Split up aggregate loads, stores and phi nodes into operations on
+ // scalar types. This inserts extractvalue and insertvalue
+ // instructions which we will expand out later.
+ 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 (StoreInst *Store = dyn_cast<StoreInst>(Inst)) {
+ if (Store->getValueOperand()->getType()->isStructTy()) {
+ NeedsAnotherPass |= SplitUpStore(Store);
+ Changed = true;
+ }
+ } else if (LoadInst *Load = dyn_cast<LoadInst>(Inst)) {
+ if (Load->getType()->isStructTy()) {
+ NeedsAnotherPass |= SplitUpLoad(Load);
+ Changed = true;
+ }
+ } else if (PHINode *Phi = dyn_cast<PHINode>(Inst)) {
+ if (Phi->getType()->isStructTy()) {
+ NeedsAnotherPass |= SplitUpPHINode(Phi);
+ Changed = true;
+ }
+ } else if (SelectInst *Select = dyn_cast<SelectInst>(Inst)) {
+ if (Select->getType()->isStructTy()) {
+ NeedsAnotherPass |= SplitUpSelect(Select);
+ Changed = true;
+ }
+ }
+ }
+ }
+ } while (NeedsAnotherPass);
+
+ Changed |= ExpandExtractValues(Func);
+
+ return Changed;
+}
+
+FunctionPass *llvm::createExpandStructRegsPass() {
+ return new ExpandStructRegs();
+}
« no previous file with comments | « lib/Transforms/NaCl/ExpandSmallArguments.cpp ('k') | lib/Transforms/NaCl/ExpandTls.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698