aboutsummaryrefslogtreecommitdiff
path: root/lib/Transforms/NaCl/ExpandStructRegs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Transforms/NaCl/ExpandStructRegs.cpp')
-rw-r--r--lib/Transforms/NaCl/ExpandStructRegs.cpp171
1 files changed, 131 insertions, 40 deletions
diff --git a/lib/Transforms/NaCl/ExpandStructRegs.cpp b/lib/Transforms/NaCl/ExpandStructRegs.cpp
index eacad1f7f4..ac83d33e78 100644
--- a/lib/Transforms/NaCl/ExpandStructRegs.cpp
+++ b/lib/Transforms/NaCl/ExpandStructRegs.cpp
@@ -12,17 +12,17 @@
// 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 handle all possible uses of struct
-// values. It is only intended to handle the uses that Clang
-// generates. Clang generates struct loads and stores, along with
+// 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.
+// pointers, and the SROA pass sometimes converts this code to using
+// insertvalue instructions too.
//
// ExpandStructRegs does not handle:
//
-// * The insertvalue instruction, which does not appear to be
-// generated anywhere.
-// * PHI nodes of struct type.
+// * Nested struct types.
+// * 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
@@ -62,6 +62,41 @@ char ExpandStructRegs::ID = 0;
INITIALIZE_PASS(ExpandStructRegs, "expand-struct-regs",
"Expand out variables with struct types", false, false)
+static void SplitUpPHINode(PHINode *Phi) {
+ StructType *STy = cast<StructType>(Phi->getType());
+
+ Value *NewStruct = UndefValue::get(STy);
+ Instruction *NewStructInsertPt = Phi->getParent()->getFirstInsertionPt();
+
+ // Create a separate PHINode for each struct field.
+ for (unsigned Index = 0; Index < STy->getNumElements(); ++Index) {
+ SmallVector<unsigned, 1> EVIndexes;
+ EVIndexes.push_back(Index);
+
+ PHINode *NewPhi = PHINode::Create(
+ STy->getElementType(Index), 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();
+}
+
template <class InstType>
static void ProcessLoadOrStoreAttrs(InstType *Dest, InstType *Src) {
CopyDebug(Dest, Src);
@@ -77,7 +112,7 @@ static void ProcessLoadOrStoreAttrs(InstType *Dest, InstType *Src) {
Dest->setAlignment(1);
}
-static void ExpandStore(StoreInst *Store) {
+static void SplitUpStore(StoreInst *Store) {
StructType *STy = cast<StructType>(Store->getValueOperand()->getType());
// Create a separate store instruction for each struct field.
for (unsigned Index = 0; Index < STy->getNumElements(); ++Index) {
@@ -90,23 +125,19 @@ static void ExpandStore(StoreInst *Store) {
Store), Store);
SmallVector<unsigned, 1> EVIndexes;
EVIndexes.push_back(Index);
- Value *Field;
- if (Constant *C = dyn_cast<Constant>(Store->getValueOperand())) {
- Field = ConstantExpr::getExtractValue(C, EVIndexes);
- } else {
- Field = ExtractValueInst::Create(
- Store->getValueOperand(), EVIndexes, "", Store);
- }
+ Value *Field = ExtractValueInst::Create(Store->getValueOperand(),
+ EVIndexes, "", Store);
StoreInst *NewStore = new StoreInst(Field, GEP, Store);
ProcessLoadOrStoreAttrs(NewStore, Store);
}
Store->eraseFromParent();
}
-static void ExpandLoad(LoadInst *Load) {
+static void SplitUpLoad(LoadInst *Load) {
StructType *STy = cast<StructType>(Load->getType());
+ Value *NewStruct = UndefValue::get(STy);
+
// Create a separate load instruction for each struct field.
- SmallVector<Value *, 5> Fields;
for (unsigned Index = 0; Index < STy->getNumElements(); ++Index) {
SmallVector<Value *, 2> Indexes;
Indexes.push_back(ConstantInt::get(Load->getContext(), APInt(32, 0)));
@@ -116,49 +147,109 @@ static void ExpandLoad(LoadInst *Load) {
Load->getName() + ".index", Load), Load);
LoadInst *NewLoad = new LoadInst(GEP, Load->getName() + ".field", Load);
ProcessLoadOrStoreAttrs(NewLoad, Load);
- Fields.push_back(NewLoad);
+
+ // Reconstruct the struct value.
+ SmallVector<unsigned, 1> EVIndexes;
+ EVIndexes.push_back(Index);
+ NewStruct = CopyDebug(
+ InsertValueInst::Create(NewStruct, NewLoad, EVIndexes,
+ Load->getName() + ".insert", Load), Load);
}
- ReplaceUsesOfStructWithFields(Load, Fields);
+ Load->replaceAllUsesWith(NewStruct);
Load->eraseFromParent();
}
+static void ExpandExtractValue(ExtractValueInst *EV) {
+ // Search for the insertvalue instruction that inserts the struct
+ // field referenced by this extractvalue instruction.
+ Value *StructVal = EV->getAggregateOperand();
+ Value *ResultField;
+ for (;;) {
+ if (InsertValueInst *IV = dyn_cast<InsertValueInst>(StructVal)) {
+ if (EV->getNumIndices() != 1 || IV->getNumIndices() != 1) {
+ errs() << "Value: " << *EV << "\n";
+ errs() << "Value: " << *IV << "\n";
+ report_fatal_error("ExpandStructRegs does not handle nested structs");
+ }
+ if (EV->getIndices()[0] == IV->getIndices()[0]) {
+ ResultField = IV->getInsertedValueOperand();
+ break;
+ }
+ // No match. Try the next struct value in the chain.
+ StructVal = IV->getAggregateOperand();
+ } else if (Constant *C = dyn_cast<Constant>(StructVal)) {
+ ResultField = ConstantExpr::getExtractValue(C, EV->getIndices());
+ break;
+ } else {
+ errs() << "Value: " << *StructVal << "\n";
+ report_fatal_error("Unrecognized struct value");
+ }
+ }
+ EV->replaceAllUsesWith(ResultField);
+ EV->eraseFromParent();
+}
+
bool ExpandStructRegs::runOnFunction(Function &Func) {
bool Changed = false;
- // It is not safe to iterate through the basic block while
- // deleting extractvalue instructions, so make a copy of the
- // instructions we will operate on first.
- SmallVector<StoreInst *, 10> Stores;
- SmallVector<LoadInst *, 10> Loads;
+ // 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 Inst = BB->begin(), E = BB->end();
- Inst != E; ++Inst) {
+ 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()) {
- Stores.push_back(Store);
+ SplitUpStore(Store);
+ Changed = true;
}
} else if (LoadInst *Load = dyn_cast<LoadInst>(Inst)) {
if (Load->getType()->isStructTy()) {
- Loads.push_back(Load);
+ SplitUpLoad(Load);
+ Changed = true;
+ }
+ } else if (PHINode *Phi = dyn_cast<PHINode>(Inst)) {
+ if (Phi->getType()->isStructTy()) {
+ SplitUpPHINode(Phi);
+ Changed = true;
}
}
}
}
- // Expand stores first. This will introduce extractvalue
- // instructions.
- for (SmallVectorImpl<StoreInst *>::iterator Inst = Stores.begin(),
- E = Stores.end(); Inst != E; ++Inst) {
- ExpandStore(*Inst);
- Changed = true;
+ // 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.
+ SmallVector<Instruction *, 10> ToErase;
+ 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 (ExtractValueInst *EV = dyn_cast<ExtractValueInst>(Inst)) {
+ ExpandExtractValue(EV);
+ Changed = true;
+ } 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 (SmallVectorImpl<Instruction *>::iterator I = ToErase.begin(),
+ E = ToErase.end();
+ I != E; ++I) {
+ (*I)->dropAllReferences();
}
- // Expanding loads will remove the extractvalue instructions we
- // previously introduced.
- for (SmallVectorImpl<LoadInst *>::iterator Inst = Loads.begin(),
- E = Loads.end(); Inst != E; ++Inst) {
- ExpandLoad(*Inst);
- Changed = true;
+ for (SmallVectorImpl<Instruction *>::iterator I = ToErase.begin(),
+ E = ToErase.end();
+ I != E; ++I) {
+ (*I)->eraseFromParent();
}
return Changed;
}