Index: lib/Transforms/NaCl/ExpandLargeIntegers.cpp |
diff --git a/lib/Transforms/NaCl/ExpandLargeIntegers.cpp b/lib/Transforms/NaCl/ExpandLargeIntegers.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9fb3a13d28016845f3774b689701ac94eb1252ba |
--- /dev/null |
+++ b/lib/Transforms/NaCl/ExpandLargeIntegers.cpp |
@@ -0,0 +1,616 @@ |
+//===- ExpandLargeIntegers.cpp - Expand illegal integers for PNaCl ABI ----===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+// A limited set of transformations to expand illegal-sized int types. |
+// |
+//===----------------------------------------------------------------------===// |
+// |
+// Legal sizes for the purposes of expansion are anything 64 bits or less. |
+// Operations on large integers are split into operations on smaller-sized |
+// integers. The low parts should always be powers of 2, but the high parts may |
+// not be. A subsequent pass can promote those. For now this pass only intends |
+// to support the uses generated by clang, which is basically just for large |
+// bitfields. |
+// |
+// Limitations: |
+// 1) It can't change function signatures or global variables. |
+// 3) Doesn't support mul, div/rem, switch. |
+// 4) Doesn't handle arrays or structs (or GEPs) with illegal types. |
+// 5) Doesn't handle constant expressions (it also doesn't produce them, so it |
+// can run after ExpandConstantExpr). |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#include "llvm/ADT/DenseMap.h" |
+#include "llvm/ADT/PostOrderIterator.h" |
+#include "llvm/ADT/STLExtras.h" |
+#include "llvm/ADT/SmallVector.h" |
+#include "llvm/IR/CFG.h" |
+#include "llvm/IR/DataLayout.h" |
+#include "llvm/IR/DerivedTypes.h" |
+#include "llvm/IR/Function.h" |
+#include "llvm/IR/IRBuilder.h" |
+#include "llvm/IR/Instructions.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; |
+ |
+#define DEBUG_TYPE "nacl-expand-ints" |
+ |
+// Break instructions up into no larger than 64-bit chunks. |
+static const unsigned kChunkBits = 64; |
+static const unsigned kChunkBytes = kChunkBits / CHAR_BIT; |
+ |
+namespace { |
+class ExpandLargeIntegers : public FunctionPass { |
+public: |
+ static char ID; |
+ ExpandLargeIntegers() : FunctionPass(ID) { |
+ initializeExpandLargeIntegersPass(*PassRegistry::getPassRegistry()); |
+ } |
+ bool runOnFunction(Function &F) override; |
+}; |
+ |
+template <typename T> struct LoHiPair { |
+ T Lo, Hi; |
+ LoHiPair() : Lo(), Hi() {} |
+ LoHiPair(T Lo, T Hi) : Lo(Lo), Hi(Hi) {} |
+}; |
+template <typename T> struct LoHiBitTriple { |
+ T Lo, Hi, Bit; |
+ LoHiBitTriple() : Lo(), Hi(), Bit() {} |
+ LoHiBitTriple(T Lo, T Hi, T Bit) : Lo(Lo), Hi(Hi), Bit(Bit) {} |
+}; |
+typedef LoHiPair<IntegerType *> TypePair; |
+typedef LoHiPair<Value *> ValuePair; |
+typedef LoHiPair<unsigned> AlignPair; |
+typedef LoHiBitTriple<Value *> ValueTriple; |
+ |
+// Information needed to patch a phi node which forward-references a value. |
+struct ForwardPHI { |
+ Value *Val; |
+ PHINode *Lo, *Hi; |
+ unsigned ValueNumber; |
+ ForwardPHI(Value *Val, PHINode *Lo, PHINode *Hi, unsigned ValueNumber) |
+ : Val(Val), Lo(Lo), Hi(Hi), ValueNumber(ValueNumber) {} |
+}; |
+} |
+ |
+char ExpandLargeIntegers::ID = 0; |
+INITIALIZE_PASS(ExpandLargeIntegers, "nacl-expand-ints", |
+ "Expand integer types that are illegal in PNaCl", false, false) |
+ |
+#define DIE_IF(COND, VAL, MSG) \ |
+ do { \ |
+ if (COND) { \ |
+ errs() << "Unsupported: " << *(VAL) << '\n'; \ |
+ report_fatal_error( \ |
+ MSG " not yet supported for integer types larger than 64 bits"); \ |
+ } \ |
+ } while (0) |
+ |
+static bool isLegalBitSize(unsigned Bits) { |
+ assert(Bits && "Can't have zero-size integers"); |
+ return Bits <= kChunkBits; |
+} |
+ |
+static TypePair getExpandedIntTypes(Type *Ty) { |
+ unsigned BitWidth = Ty->getIntegerBitWidth(); |
+ assert(!isLegalBitSize(BitWidth)); |
+ return {IntegerType::get(Ty->getContext(), kChunkBits), |
+ IntegerType::get(Ty->getContext(), BitWidth - kChunkBits)}; |
+} |
+ |
+// Return true if Val is an int which should be converted. |
+static bool shouldConvert(const Value *Val) { |
+ Type *Ty = Val->getType(); |
+ if (IntegerType *ITy = dyn_cast<IntegerType>(Ty)) |
+ return !isLegalBitSize(ITy->getBitWidth()); |
+ return false; |
+} |
+ |
+// Return a pair of constants expanded from C. |
+static ValuePair expandConstant(Constant *C) { |
+ assert(shouldConvert(C)); |
+ TypePair ExpandedTypes = getExpandedIntTypes(C->getType()); |
+ if (isa<UndefValue>(C)) { |
+ return {UndefValue::get(ExpandedTypes.Lo), |
+ UndefValue::get(ExpandedTypes.Hi)}; |
+ } else if (ConstantInt *CInt = dyn_cast<ConstantInt>(C)) { |
+ Constant *ShiftAmt = ConstantInt::get( |
+ CInt->getType(), ExpandedTypes.Lo->getBitWidth(), false); |
+ return {ConstantExpr::getTrunc(CInt, ExpandedTypes.Lo), |
+ ConstantExpr::getTrunc(ConstantExpr::getLShr(CInt, ShiftAmt), |
+ ExpandedTypes.Hi)}; |
+ } |
+ DIE_IF(true, C, "Constant value"); |
+} |
+ |
+template <typename T> |
+static AlignPair getAlign(const DataLayout &DL, T *I, Type *PrefAlignTy) { |
+ unsigned LoAlign = I->getAlignment(); |
+ if (LoAlign == 0) |
+ LoAlign = DL.getPrefTypeAlignment(PrefAlignTy); |
+ unsigned HiAlign = MinAlign(LoAlign, kChunkBytes); |
+ return {LoAlign, HiAlign}; |
+} |
+ |
+static ValuePair createBit(IRBuilder<> *IRB, const BinaryOperator *Binop, |
+ const ValuePair &Lhs, const ValuePair &Rhs, |
+ const TypePair &Tys, const StringRef &Name) { |
+ auto Op = Binop->getOpcode(); |
+ Value *Lo = IRB->CreateBinOp(Op, Lhs.Lo, Rhs.Lo, Twine(Name, ".lo")); |
+ Value *Hi = IRB->CreateBinOp(Op, Lhs.Hi, Rhs.Hi, Twine(Name, ".hi")); |
+ return {Lo, Hi}; |
+} |
+ |
+static ValuePair createShl(IRBuilder<> *IRB, const BinaryOperator *Binop, |
+ const ValuePair &Lhs, const ValuePair &Rhs, |
+ const TypePair &Tys, const StringRef &Name) { |
+ ConstantInt *ShlAmount = dyn_cast<ConstantInt>(Rhs.Lo); |
+ // TODO(dschuff): Expansion of variable-sized shifts isn't supported |
+ // because the behavior depends on whether the shift amount is less than |
+ // the size of the low part of the expanded type, and I haven't yet |
+ // figured out a way to do it for variable-sized shifts without splitting |
+ // the basic block. I don't believe it's actually necessary for |
+ // bitfields. Likewise for LShr below. |
+ DIE_IF(!ShlAmount, Binop, "Expansion of variable-sized shifts"); |
+ unsigned ShiftAmount = ShlAmount->getZExtValue(); |
+ if (ShiftAmount >= Binop->getType()->getIntegerBitWidth()) |
+ ShiftAmount = 0; // Undefined behavior. |
+ unsigned HiBits = Tys.Hi->getIntegerBitWidth(); |
+ // |<------------Hi---------->|<-------Lo------>| |
+ // | | | |
+ // +--------+--------+--------+--------+--------+ |
+ // |abcdefghijklmnopqrstuvwxyz|ABCDEFGHIJKLMNOPQ| |
+ // +--------+--------+--------+--------+--------+ |
+ // Possible shifts: |
+ // |efghijklmnopqrstuvwxyzABCD|EFGHIJKLMNOPQ0000| Some Lo into Hi. |
+ // |vwxyzABCDEFGHIJKLMNOPQ0000|00000000000000000| Lo is 0, keep some Hi. |
+ // |DEFGHIJKLMNOPQ000000000000|00000000000000000| Lo is 0, no Hi left. |
+ Value *Lo, *Hi; |
+ if (ShiftAmount < kChunkBits) { |
+ Lo = IRB->CreateShl(Lhs.Lo, ShiftAmount, Twine(Name, ".lo")); |
+ Hi = |
+ IRB->CreateZExtOrTrunc(IRB->CreateLShr(Lhs.Lo, kChunkBits - ShiftAmount, |
+ Twine(Name, ".lo.shr")), |
+ Tys.Hi, Twine(Name, ".lo.ext")); |
+ } else { |
+ Lo = ConstantInt::get(Tys.Lo, 0); |
+ Hi = IRB->CreateShl( |
+ IRB->CreateZExtOrTrunc(Lhs.Lo, Tys.Hi, Twine(Name, ".lo.ext")), |
+ ShiftAmount - kChunkBits, Twine(Name, ".lo.shl")); |
+ } |
+ if (ShiftAmount < HiBits) |
+ Hi = IRB->CreateOr( |
+ Hi, IRB->CreateShl(Lhs.Hi, ShiftAmount, Twine(Name, ".hi.shl")), |
+ Twine(Name, ".or")); |
+ return {Lo, Hi}; |
+} |
+ |
+static ValuePair createShr(IRBuilder<> *IRB, const BinaryOperator *Binop, |
+ const ValuePair &Lhs, const ValuePair &Rhs, |
+ const TypePair &Tys, const StringRef &Name) { |
+ auto Op = Binop->getOpcode(); |
+ ConstantInt *ShrAmount = dyn_cast<ConstantInt>(Rhs.Lo); |
+ // TODO(dschuff): Expansion of variable-sized shifts isn't supported |
+ // because the behavior depends on whether the shift amount is less than |
+ // the size of the low part of the expanded type, and I haven't yet |
+ // figured out a way to do it for variable-sized shifts without splitting |
+ // the basic block. I don't believe it's actually necessary for bitfields. |
+ DIE_IF(!ShrAmount, Binop, "Expansion of variable-sized shifts"); |
+ bool IsArith = Op == Instruction::AShr; |
+ unsigned ShiftAmount = ShrAmount->getZExtValue(); |
+ if (ShiftAmount >= Binop->getType()->getIntegerBitWidth()) |
+ ShiftAmount = 0; // Undefined behavior. |
+ unsigned HiBitWidth = Tys.Hi->getIntegerBitWidth(); |
+ // |<--Hi-->|<-------Lo------>| |
+ // | | | |
+ // +--------+--------+--------+ |
+ // |abcdefgh|ABCDEFGHIJKLMNOPQ| |
+ // +--------+--------+--------+ |
+ // Possible shifts (0 is sign when doing AShr): |
+ // |0000abcd|defgABCDEFGHIJKLM| Some Hi into Lo. |
+ // |00000000|00abcdefgABCDEFGH| Hi is 0, keep some Lo. |
+ // |00000000|000000000000abcde| Hi is 0, no Lo left. |
+ Value *Lo, *Hi; |
+ if (ShiftAmount < kChunkBits) { |
+ Lo = IRB->CreateShl( |
+ IsArith |
+ ? IRB->CreateSExtOrTrunc(Lhs.Hi, Tys.Lo, Twine(Name, ".hi.ext")) |
+ : IRB->CreateZExtOrTrunc(Lhs.Hi, Tys.Lo, Twine(Name, ".hi.ext")), |
+ kChunkBits - ShiftAmount, Twine(Name, ".hi.shl")); |
+ Lo = IRB->CreateOr( |
+ Lo, IRB->CreateLShr(Lhs.Lo, ShiftAmount, Twine(Name, ".lo.shr")), |
+ Twine(Name, ".lo")); |
+ } else { |
+ Lo = IRB->CreateBinOp(Op, Lhs.Hi, |
+ ConstantInt::get(Tys.Hi, ShiftAmount - kChunkBits), |
+ Twine(Name, ".hi.shr")); |
+ Lo = IsArith ? IRB->CreateSExtOrTrunc(Lo, Tys.Lo, Twine(Name, ".lo.ext")) |
+ : IRB->CreateZExtOrTrunc(Lo, Tys.Lo, Twine(Name, ".lo.ext")); |
+ } |
+ if (ShiftAmount < HiBitWidth) { |
+ Hi = IRB->CreateBinOp(Op, Lhs.Hi, ConstantInt::get(Tys.Hi, ShiftAmount), |
+ Twine(Name, ".hi")); |
+ } else { |
+ Hi = IsArith ? IRB->CreateAShr(Lhs.Hi, HiBitWidth - 1, Twine(Name, ".hi")) |
+ : ConstantInt::get(Tys.Hi, 0); |
+ } |
+ return {Lo, Hi}; |
+} |
+ |
+static Value *createCarry(IRBuilder<> *IRB, Value *Lhs, Value *Rhs, |
+ Value *Added, Type *Ty, const StringRef &Name) { |
+ return IRB->CreateZExt( |
+ IRB->CreateICmpULT( |
+ Added, |
+ IRB->CreateSelect(IRB->CreateICmpULT(Lhs, Rhs, Twine(Name, ".cmp")), |
+ Rhs, Lhs, Twine(Name, ".limit")), |
+ Twine(Name, ".overflowed")), |
+ Ty, Twine(Name, ".carry")); |
+} |
+ |
+static ValueTriple createAdd(IRBuilder<> *IRB, const ValuePair &Lhs, |
+ const ValuePair &Rhs, const TypePair &Tys, |
+ const StringRef &Name, Type *HiCarryTy) { |
+ auto Op = Instruction::Add; |
+ // Don't propagate NUW/NSW to the lo operation: it can overflow. |
+ Value *Lo = IRB->CreateBinOp(Op, Lhs.Lo, Rhs.Lo, Twine(Name, ".lo")); |
+ Value *LoCarry = createCarry(IRB, Lhs.Lo, Rhs.Lo, Lo, Tys.Hi, Name); |
+ // TODO(jfb) The hi operation could be tagged with NUW/NSW. |
+ Value *HiAdd = IRB->CreateBinOp(Op, Lhs.Hi, Rhs.Hi, Twine(Name, ".hi")); |
+ Value *Hi = IRB->CreateBinOp(Op, HiAdd, LoCarry, Twine(Name, ".carried")); |
+ Value *HiCarry = HiCarryTy |
+ ? createCarry(IRB, Lhs.Hi, Rhs.Hi, Hi, HiCarryTy, Name) |
+ : nullptr; |
+ return {Lo, Hi, HiCarry}; |
+} |
+ |
+static ValuePair createSub(IRBuilder<> *IRB, const ValuePair &Lhs, |
+ const ValuePair &Rhs, const TypePair &Tys, |
+ const StringRef &Name) { |
+ auto Op = Instruction::Sub; |
+ Value *Borrowed = IRB->CreateSExt( |
+ IRB->CreateICmpULT(Lhs.Lo, Rhs.Lo, Twine(Name, ".borrow")), Tys.Hi, |
+ Twine(Name, ".borrowing")); |
+ Value *Lo = IRB->CreateBinOp(Op, Lhs.Lo, Rhs.Lo, Twine(Name, ".lo")); |
+ Value *Hi = |
+ IRB->CreateBinOp(Instruction::Add, |
+ IRB->CreateBinOp(Op, Lhs.Hi, Rhs.Hi, Twine(Name, ".hi")), |
+ Borrowed, Twine(Name, ".borrowed")); |
+ return {Lo, Hi}; |
+} |
+ |
+static Value *createICmpEquality(IRBuilder<> *IRB, CmpInst::Predicate Pred, |
+ const ValuePair &Lhs, const ValuePair &Rhs, |
+ const StringRef &Name) { |
+ assert(Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE); |
+ Value *Lo = IRB->CreateICmp(Pred, Lhs.Lo, Rhs.Lo, Twine(Name, ".lo")); |
+ Value *Hi = IRB->CreateICmp(Pred, Lhs.Hi, Rhs.Hi, Twine(Name, ".hi")); |
+ return IRB->CreateBinOp( |
+ Instruction::And, Lo, Hi, |
+ Twine(Name, Pred == CmpInst::ICMP_EQ ? ".eq" : ".ne")); |
+} |
+ |
+static Value *createICmp(IRBuilder<> *IRB, const ICmpInst *ICmp, |
+ const ValuePair &Lhs, const ValuePair &Rhs, |
+ const TypePair &Tys, const StringRef &Name) { |
+ auto Pred = ICmp->getPredicate(); |
+ switch (Pred) { |
+ case CmpInst::ICMP_EQ: |
+ case CmpInst::ICMP_NE: |
+ return createICmpEquality(IRB, ICmp->getPredicate(), Lhs, Rhs, Name); |
+ |
+ case CmpInst::ICMP_UGT: // C == 1 and Z == 0 |
+ case CmpInst::ICMP_UGE: // C == 1 |
+ case CmpInst::ICMP_ULT: // C == 0 and Z == 0 |
+ case CmpInst::ICMP_ULE: // C == 0 |
+ { |
+ Value *Carry = createAdd(IRB, Lhs, Rhs, Tys, Name, ICmp->getType()).Bit; |
+ if (Pred == CmpInst::ICMP_ULT || Pred == CmpInst::ICMP_ULE) |
+ Carry = IRB->CreateNot(Carry, Name); |
+ if (Pred == CmpInst::ICMP_UGT || Pred == CmpInst::ICMP_ULT) |
+ Carry = IRB->CreateBinOp( |
+ Instruction::And, Carry, |
+ createICmpEquality(IRB, CmpInst::ICMP_EQ, Lhs, Rhs, Name), Name); |
+ return Carry; |
+ } |
+ |
+ case CmpInst::ICMP_SGT: // N == V and Z == 0 |
+ case CmpInst::ICMP_SGE: // N == V |
+ case CmpInst::ICMP_SLT: // N != V |
+ case CmpInst::ICMP_SLE: // N != V or Z == 1 |
+ DIE_IF(true, ICmp, "Signed comparisons"); |
+ default: |
+ llvm_unreachable("Invalid integer comparison"); |
+ } |
+} |
+ |
+static ValuePair createLoad(IRBuilder<> *IRB, const DataLayout &DL, |
+ LoadInst *Load) { |
+ DIE_IF(!Load->isSimple(), Load, "Volatile and atomic loads"); |
+ Value *Op = Load->getPointerOperand(); |
+ TypePair Tys = getExpandedIntTypes(Load->getType()); |
+ AlignPair Align = getAlign(DL, Load, Load->getType()); |
+ Value *Loty = IRB->CreateBitCast(Op, Tys.Lo->getPointerTo(), |
+ Twine(Op->getName(), ".loty")); |
+ Value *Lo = |
+ IRB->CreateAlignedLoad(Loty, Align.Lo, Twine(Load->getName(), ".lo")); |
+ Value *HiAddr = |
+ IRB->CreateConstGEP1_32(Loty, 1, Twine(Op->getName(), ".hi.gep")); |
+ Value *HiTy = IRB->CreateBitCast(HiAddr, Tys.Hi->getPointerTo(), |
+ Twine(Op->getName(), ".hity")); |
+ Value *Hi = |
+ IRB->CreateAlignedLoad(HiTy, Align.Hi, Twine(Load->getName(), ".hi")); |
+ return {Lo, Hi}; |
+} |
+ |
+static ValuePair createStore(IRBuilder<> *IRB, const DataLayout &DL, |
+ StoreInst *Store, const ValuePair &StoreVals) { |
+ DIE_IF(!Store->isSimple(), Store, "Volatile and atomic stores"); |
+ Value *Ptr = Store->getPointerOperand(); |
+ TypePair Tys = getExpandedIntTypes(Store->getValueOperand()->getType()); |
+ AlignPair Align = getAlign(DL, Store, Store->getValueOperand()->getType()); |
+ Value *Loty = IRB->CreateBitCast(Ptr, Tys.Lo->getPointerTo(), |
+ Twine(Ptr->getName(), ".loty")); |
+ Value *Lo = IRB->CreateAlignedStore(StoreVals.Lo, Loty, Align.Lo); |
+ Value *HiAddr = |
+ IRB->CreateConstGEP1_32(Loty, 1, Twine(Ptr->getName(), ".hi.gep")); |
+ Value *HiTy = IRB->CreateBitCast(HiAddr, Tys.Hi->getPointerTo(), |
+ Twine(Ptr->getName(), ".hity")); |
+ Value *Hi = IRB->CreateAlignedStore(StoreVals.Hi, HiTy, Align.Hi); |
+ return {Lo, Hi}; |
+} |
+ |
+namespace { |
+// Holds the state for converting/replacing values. We visit instructions in |
+// reverse post-order, phis are therefore the only instructions which can be |
+// visited before the value they use. |
+class ConversionState { |
+public: |
+ // Return the expanded values for Val. |
+ ValuePair getConverted(Value *Val) { |
+ assert(shouldConvert(Val)); |
+ // Directly convert constants. |
+ if (Constant *C = dyn_cast<Constant>(Val)) |
+ return expandConstant(C); |
+ if (RewrittenIllegals.count(Val)) { |
+ ValuePair Found = RewrittenIllegals[Val]; |
+ if (RewrittenLegals.count(Found.Lo)) |
+ Found.Lo = RewrittenLegals[Found.Lo]; |
+ if (RewrittenLegals.count(Found.Hi)) |
+ Found.Hi = RewrittenLegals[Found.Hi]; |
+ return Found; |
+ } |
+ errs() << "Value: " << *Val << "\n"; |
+ report_fatal_error("Expanded value not found in map"); |
+ } |
+ |
+ // Returns whether a converted value has been recorded. This is only useful |
+ // for phi instructions: they can be encountered before the incoming |
+ // instruction, whereas RPO order guarantees that other instructions always |
+ // use converted values. |
+ bool hasConverted(Value *Val) { |
+ assert(shouldConvert(Val)); |
+ return dyn_cast<Constant>(Val) || RewrittenIllegals.count(Val); |
+ } |
+ |
+ // Record a forward phi, temporarily setting it to use Undef. This will be |
+ // patched up at the end of RPO. |
+ ValuePair recordForwardPHI(Value *Val, PHINode *Lo, PHINode *Hi, |
+ unsigned ValueNumber) { |
+ DEBUG(dbgs() << "\tRecording as forward PHI\n"); |
+ ForwardPHIs.push_back(ForwardPHI(Val, Lo, Hi, ValueNumber)); |
+ return {UndefValue::get(Lo->getType()), UndefValue::get(Hi->getType())}; |
+ } |
+ |
+ void recordConverted(Instruction *From, const ValuePair &To) { |
+ DEBUG(dbgs() << "\tTo: " << *To.Lo << "\n"); |
+ DEBUG(dbgs() << "\tAnd: " << *To.Hi << "\n"); |
+ ToErase.push_back(From); |
+ RewrittenIllegals[From] = To; |
+ } |
+ |
+ // Replace the uses of From with To, give From's name to To, and mark To for |
+ // deletion. |
+ void recordConverted(Instruction *From, Value *To) { |
+ assert(!shouldConvert(From)); |
+ DEBUG(dbgs() << "\tTo: " << *To << "\n"); |
+ ToErase.push_back(From); |
+ // From does not produce an illegal value, update its users in place. |
+ From->replaceAllUsesWith(To); |
+ To->takeName(From); |
+ RewrittenLegals[From] = To; |
+ } |
+ |
+ void patchForwardPHIs() { |
+ DEBUG(if (!ForwardPHIs.empty()) dbgs() << "Patching forward PHIs:\n"); |
+ for (ForwardPHI &F : ForwardPHIs) { |
+ ValuePair Ops = getConverted(F.Val); |
+ F.Lo->setIncomingValue(F.ValueNumber, Ops.Lo); |
+ F.Hi->setIncomingValue(F.ValueNumber, Ops.Hi); |
+ DEBUG(dbgs() << "\t" << *F.Lo << "\n\t" << *F.Hi << "\n"); |
+ } |
+ } |
+ |
+ void eraseReplacedInstructions() { |
+ for (Instruction *I : ToErase) |
+ I->dropAllReferences(); |
+ for (Instruction *I : ToErase) |
+ I->eraseFromParent(); |
+ } |
+ |
+private: |
+ // Maps illegal values to their new converted lo/hi values. |
+ DenseMap<Value *, ValuePair> RewrittenIllegals; |
+ // Maps legal values to their new converted value. |
+ DenseMap<Value *, Value *> RewrittenLegals; |
+ // Illegal values which have already been converted, will be erased. |
+ SmallVector<Instruction *, 32> ToErase; |
+ // PHIs which were encountered but had forward references. They need to get |
+ // patched up after RPO traversal. |
+ SmallVector<ForwardPHI, 32> ForwardPHIs; |
+}; |
+} // Anonymous namespace |
+ |
+static void convertInstruction(Instruction *Inst, ConversionState &State, |
+ const DataLayout &DL) { |
+ DEBUG(dbgs() << "Expanding Large Integer: " << *Inst << "\n"); |
+ // Set the insert point *after* Inst, so that any instructions inserted here |
+ // will be visited again. That allows iterative expansion of types > i128. |
+ BasicBlock::iterator InsertPos(Inst); |
+ IRBuilder<> IRB(++InsertPos); |
+ StringRef Name = Inst->getName(); |
+ |
+ if (PHINode *Phi = dyn_cast<PHINode>(Inst)) { |
+ unsigned N = Phi->getNumIncomingValues(); |
+ TypePair OpTys = getExpandedIntTypes(Phi->getIncomingValue(0)->getType()); |
+ PHINode *Lo = IRB.CreatePHI(OpTys.Lo, N, Twine(Name + ".lo")); |
+ PHINode *Hi = IRB.CreatePHI(OpTys.Hi, N, Twine(Name + ".hi")); |
+ for (unsigned I = 0; I != N; ++I) { |
+ Value *InVal = Phi->getIncomingValue(I); |
+ BasicBlock *InBB = Phi->getIncomingBlock(I); |
+ // If the value hasn't already been converted then this is a |
+ // forward-reference PHI which needs to be patched up after RPO traversal. |
+ ValuePair Ops = State.hasConverted(InVal) |
+ ? State.getConverted(InVal) |
+ : State.recordForwardPHI(InVal, Lo, Hi, I); |
+ Lo->addIncoming(Ops.Lo, InBB); |
+ Hi->addIncoming(Ops.Hi, InBB); |
+ } |
+ State.recordConverted(Phi, {Lo, Hi}); |
+ |
+ } else if (ZExtInst *ZExt = dyn_cast<ZExtInst>(Inst)) { |
+ Value *Operand = ZExt->getOperand(0); |
+ Type *OpTy = Operand->getType(); |
+ TypePair Tys = getExpandedIntTypes(Inst->getType()); |
+ Value *Lo, *Hi; |
+ if (OpTy->getIntegerBitWidth() <= kChunkBits) { |
+ Lo = IRB.CreateZExt(Operand, Tys.Lo, Twine(Name, ".lo")); |
+ Hi = ConstantInt::get(Tys.Hi, 0); |
+ } else { |
+ ValuePair Ops = State.getConverted(Operand); |
+ Lo = Ops.Lo; |
+ Hi = IRB.CreateZExt(Ops.Hi, Tys.Hi, Twine(Name, ".hi")); |
+ } |
+ State.recordConverted(ZExt, {Lo, Hi}); |
+ |
+ } else if (TruncInst *Trunc = dyn_cast<TruncInst>(Inst)) { |
+ Value *Operand = Trunc->getOperand(0); |
+ assert(shouldConvert(Operand) && "TruncInst is expandable but not its op"); |
+ ValuePair Ops = State.getConverted(Operand); |
+ if (!shouldConvert(Inst)) { |
+ Value *NewInst = IRB.CreateTrunc(Ops.Lo, Trunc->getType(), Name); |
+ State.recordConverted(Trunc, NewInst); |
+ } else { |
+ TypePair Tys = getExpandedIntTypes(Trunc->getType()); |
+ assert(Tys.Lo == getExpandedIntTypes(Operand->getType()).Lo); |
+ Value *Lo = Ops.Lo; |
+ Value *Hi = IRB.CreateTrunc(Ops.Hi, Tys.Hi, Twine(Name, ".hi")); |
+ State.recordConverted(Trunc, {Lo, Hi}); |
+ } |
+ |
+ } else if (BinaryOperator *Binop = dyn_cast<BinaryOperator>(Inst)) { |
+ ValuePair Lhs = State.getConverted(Binop->getOperand(0)); |
+ ValuePair Rhs = State.getConverted(Binop->getOperand(1)); |
+ TypePair Tys = getExpandedIntTypes(Binop->getType()); |
+ ValuePair Conv; |
+ switch (Binop->getOpcode()) { |
+ case Instruction::And: |
+ case Instruction::Or: |
+ case Instruction::Xor: |
+ Conv = createBit(&IRB, Binop, Lhs, Rhs, Tys, Name); |
+ break; |
+ case Instruction::Shl: |
+ Conv = createShl(&IRB, Binop, Lhs, Rhs, Tys, Name); |
+ break; |
+ case Instruction::AShr: |
+ case Instruction::LShr: |
+ Conv = createShr(&IRB, Binop, Lhs, Rhs, Tys, Name); |
+ break; |
+ case Instruction::Add: { |
+ ValueTriple VT = |
+ createAdd(&IRB, Lhs, Rhs, Tys, Name, /*HiCarryTy=*/nullptr); |
+ Conv = {VT.Lo, VT.Hi}; // Ignore Hi carry. |
+ } break; |
+ case Instruction::Sub: |
+ Conv = createSub(&IRB, Lhs, Rhs, Tys, Name); |
+ break; |
+ default: |
+ DIE_IF(true, Binop, "Binary operator type"); |
+ } |
+ State.recordConverted(Binop, Conv); |
+ |
+ } else if (ICmpInst *ICmp = dyn_cast<ICmpInst>(Inst)) { |
+ ValuePair Lhs = State.getConverted(ICmp->getOperand(0)); |
+ ValuePair Rhs = State.getConverted(ICmp->getOperand(1)); |
+ TypePair Tys = getExpandedIntTypes(ICmp->getOperand(0)->getType()); |
+ State.recordConverted(ICmp, createICmp(&IRB, ICmp, Lhs, Rhs, Tys, Name)); |
+ |
+ } else if (LoadInst *Load = dyn_cast<LoadInst>(Inst)) { |
+ State.recordConverted(Load, createLoad(&IRB, DL, Load)); |
+ |
+ } else if (StoreInst *Store = dyn_cast<StoreInst>(Inst)) { |
+ ValuePair StoreVals = State.getConverted(Store->getValueOperand()); |
+ State.recordConverted(Store, createStore(&IRB, DL, Store, StoreVals)); |
+ |
+ } else if (SelectInst *Select = dyn_cast<SelectInst>(Inst)) { |
+ Value *Cond = Select->getCondition(); |
+ ValuePair True = State.getConverted(Select->getTrueValue()); |
+ ValuePair False = State.getConverted(Select->getFalseValue()); |
+ Value *Lo = IRB.CreateSelect(Cond, True.Lo, False.Lo, Twine(Name, ".lo")); |
+ Value *Hi = IRB.CreateSelect(Cond, True.Hi, False.Hi, Twine(Name, ".hi")); |
+ State.recordConverted(Select, {Lo, Hi}); |
+ |
+ } else { |
+ DIE_IF(true, Inst, "Instruction"); |
+ } |
+} |
+ |
+bool ExpandLargeIntegers::runOnFunction(Function &F) { |
+ // Don't support changing the function arguments. Illegal function arguments |
+ // should not be generated by clang. |
+ for (const Argument &Arg : F.args()) |
+ if (shouldConvert(&Arg)) |
+ report_fatal_error("Function " + F.getName() + |
+ " has illegal integer argument"); |
+ |
+ // TODO(jfb) This should loop to handle nested forward PHIs. |
+ |
+ ConversionState State; |
+ DataLayout DL(F.getParent()); |
+ bool Modified = false; |
+ ReversePostOrderTraversal<Function *> RPOT(&F); |
+ for (ReversePostOrderTraversal<Function *>::rpo_iterator FI = RPOT.begin(), |
+ FE = RPOT.end(); |
+ FI != FE; ++FI) { |
+ BasicBlock *BB = *FI; |
+ for (Instruction &I : *BB) { |
+ // Only attempt to convert an instruction if its result or any of its |
+ // operands are illegal. |
+ bool ShouldConvert = shouldConvert(&I); |
+ for (Value *Op : I.operands()) |
+ ShouldConvert |= shouldConvert(Op); |
+ if (ShouldConvert) { |
+ convertInstruction(&I, State, DL); |
+ Modified = true; |
+ } |
+ } |
+ } |
+ State.patchForwardPHIs(); |
+ State.eraseReplacedInstructions(); |
+ return Modified; |
+} |
+ |
+FunctionPass *llvm::createExpandLargeIntegersPass() { |
+ return new ExpandLargeIntegers(); |
+} |