Index: include/llvm/Transforms/Scalar/ConstantHoisting.h =================================================================== --- include/llvm/Transforms/Scalar/ConstantHoisting.h +++ include/llvm/Transforms/Scalar/ConstantHoisting.h @@ -38,6 +38,7 @@ #define LLVM_TRANSFORMS_SCALAR_CONSTANTHOISTING_H #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/IR/PassManager.h" @@ -50,8 +51,10 @@ class BlockFrequencyInfo; class Constant; class ConstantInt; +class ConstantExpr; class DominatorTree; class Function; +class GlobalVariable; class Instruction; class TargetTransformInfo; @@ -75,9 +78,11 @@ struct ConstantCandidate { ConstantUseListType Uses; ConstantInt *ConstInt; + ConstantExpr *ConstExpr; unsigned CumulativeCost = 0; - ConstantCandidate(ConstantInt *ConstInt) : ConstInt(ConstInt) {} + ConstantCandidate(ConstantInt *ConstInt, ConstantExpr *ConstExpr=nullptr) : + ConstInt(ConstInt), ConstExpr(ConstExpr) {} /// Add the user to the use list and update the cost. void addUser(Instruction *Inst, unsigned Idx, unsigned Cost) { @@ -91,16 +96,19 @@ struct RebasedConstantInfo { ConstantUseListType Uses; Constant *Offset; + Type *Ty; - RebasedConstantInfo(ConstantUseListType &&Uses, Constant *Offset) - : Uses(std::move(Uses)), Offset(Offset) {} + RebasedConstantInfo(ConstantUseListType &&Uses, Constant *Offset, + Type *Ty=nullptr) : Uses(std::move(Uses)), Offset(Offset), Ty(Ty) {} }; using RebasedConstantListType = SmallVector; /// A base constant and all its rebased constants. struct ConstantInfo { - ConstantInt *BaseConstant; + ConstantInt *BaseInt; + ConstantExpr *BaseExpr; + Type *BaseTy; RebasedConstantListType RebasedConstants; }; @@ -115,29 +123,43 @@ BlockFrequencyInfo *BFI, BasicBlock &Entry); void releaseMemory() { - ConstantVec.clear(); ClonedCastMap.clear(); - ConstCandVec.clear(); + ConstIntCandVec.clear(); + for (auto MapEntry : ConstGEPCandMap) + MapEntry.second.clear(); + ConstGEPCandMap.clear(); + ConstIntInfoVec.clear(); + for (auto MapEntry : ConstGEPInfoMap) + MapEntry.second.clear(); + ConstGEPInfoMap.clear(); } private: - using ConstCandMapType = DenseMap; - using ConstCandVecType = std::vector; + using ConstPtrUnionType = PointerUnion; + using ConstCandMapType = DenseMap; const TargetTransformInfo *TTI; DominatorTree *DT; BlockFrequencyInfo *BFI; + LLVMContext *Ctx; + const DataLayout *DL; BasicBlock *Entry; /// Keeps track of constant candidates found in the function. - ConstCandVecType ConstCandVec; + using ConstCandVecType = std::vector; + using GVCandVecMapType = DenseMap; + ConstCandVecType ConstIntCandVec; + GVCandVecMapType ConstGEPCandMap; + + /// These are the final constants we decided to hoist. + using ConstInfoVecType = SmallVector; + using GVInfoVecMapType = DenseMap; + ConstInfoVecType ConstIntInfoVec; + GVInfoVecMapType ConstGEPInfoMap; /// Keep track of cast instructions we already cloned. SmallDenseMap ClonedCastMap; - /// These are the final constants we decided to hoist. - SmallVector ConstantVec; - Instruction *findMatInsertPt(Instruction *Inst, unsigned Idx = ~0U) const; SmallPtrSet findConstantInsertionPoint(const consthoist::ConstantInfo &ConstInfo) const; @@ -145,19 +167,24 @@ Instruction *Inst, unsigned Idx, ConstantInt *ConstInt); void collectConstantCandidates(ConstCandMapType &ConstCandMap, + Instruction *Inst, unsigned Idx, + ConstantExpr *ConstExpr); + void collectConstantCandidates(ConstCandMapType &ConstCandMap, Instruction *Inst, unsigned Idx); void collectConstantCandidates(ConstCandMapType &ConstCandMap, Instruction *Inst); void collectConstantCandidates(Function &Fn); + void computeGEPOffset(ConstantExpr *ConstExpr, Type *Ty, APInt &Offset); void findAndMakeBaseConstant(ConstCandVecType::iterator S, - ConstCandVecType::iterator E); + ConstCandVecType::iterator E, + SmallVectorImpl &ConstInfoVec); unsigned maximizeConstantsInRange(ConstCandVecType::iterator S, ConstCandVecType::iterator E, ConstCandVecType::iterator &MaxCostItr); - void findBaseConstants(); - void emitBaseConstants(Instruction *Base, Constant *Offset, + void findBaseConstants(GlobalVariable *BaseGV = nullptr); + void emitBaseConstants(Instruction *Base, Constant *Offset, Type *Ty, const consthoist::ConstantUser &ConstUser); - bool emitBaseConstants(); + bool emitBaseConstants(GlobalVariable *BaseGV = nullptr); void deleteDeadCastInst() const; bool optimizeConstants(Function &Fn); }; Index: lib/Transforms/Scalar/ConstantHoisting.cpp =================================================================== --- lib/Transforms/Scalar/ConstantHoisting.cpp +++ lib/Transforms/Scalar/ConstantHoisting.cpp @@ -82,6 +82,10 @@ "chance to execute const materialization more frequently than " "without hoisting.")); +static cl::opt ConstHoistGEP( + "consthoist-gep", cl::init(false), cl::Hidden, + cl::desc("Try hoisting constant gep expressions")); + namespace { /// The constant hoisting pass. @@ -340,7 +344,7 @@ /// /// The operand at index Idx is not necessarily the constant integer itself. It /// could also be a cast instruction or a constant expression that uses the -// constant integer. +/// constant integer. void ConstantHoistingPass::collectConstantCandidates( ConstCandMapType &ConstCandMap, Instruction *Inst, unsigned Idx, ConstantInt *ConstInt) { @@ -358,12 +362,13 @@ if (Cost > TargetTransformInfo::TCC_Basic) { ConstCandMapType::iterator Itr; bool Inserted; - std::tie(Itr, Inserted) = ConstCandMap.insert(std::make_pair(ConstInt, 0)); + ConstPtrUnionType Cand = ConstInt; + std::tie(Itr, Inserted) = ConstCandMap.insert(std::make_pair(Cand, 0)); if (Inserted) { - ConstCandVec.push_back(ConstantCandidate(ConstInt)); - Itr->second = ConstCandVec.size() - 1; + ConstIntCandVec.push_back(ConstantCandidate(ConstInt)); + Itr->second = ConstIntCandVec.size() - 1; } - ConstCandVec[Itr->second].addUser(Inst, Idx, Cost); + ConstIntCandVec[Itr->second].addUser(Inst, Idx, Cost); LLVM_DEBUG(if (isa(Inst->getOperand(Idx))) dbgs() << "Collect constant " << *ConstInt << " from " << *Inst << " with cost " << Cost << '\n'; @@ -374,6 +379,53 @@ } } +/// Record constant GEP expression for instruction Inst at operand index Idx. +void ConstantHoistingPass::collectConstantCandidates( + ConstCandMapType &ConstCandMap, Instruction *Inst, unsigned Idx, + ConstantExpr *ConstExpr) { + // TODO: Handle vector GEPs + if (ConstExpr->getType()->isVectorTy()) + return; + + GlobalVariable *BaseGV = dyn_cast(ConstExpr->getOperand(0)); + if (!BaseGV) + return; + + // Get offset from the base GV. + PointerType *GVPtrTy = dyn_cast(BaseGV->getType()); + IntegerType *PtrIntTy = DL->getIntPtrType(*Ctx, GVPtrTy->getAddressSpace()); + APInt Offset(DL->getTypeSizeInBits(PtrIntTy), /*val*/0, /*isSigned*/true); + auto *GEPO = cast(ConstExpr); + if (!GEPO->accumulateConstantOffset(*DL, Offset)) + return; + + if (!Offset.isIntN(32)) + return; + + // Initial cost is 2, one constant pool entry plus one Ld instruction. + int Cost = 2; + // Bump SizeCost to 4 to reflect code size impact. + int SizeCost = 4; + + // Ignore cheap integer constants. + if (Cost > TTI->getIntImmCost(Instruction::Add, 1, Offset, PtrIntTy) || + SizeCost > TTI->getIntImmCodeSizeCost( + Instruction::Add, 1, Offset, PtrIntTy)) { + ConstCandVecType &ExprCandVec = ConstGEPCandMap[BaseGV]; + ConstCandMapType::iterator Itr; + bool Inserted; + ConstPtrUnionType Cand = ConstExpr; + std::tie(Itr, Inserted) = ConstCandMap.insert(std::make_pair(Cand, 0)); + if (Inserted) { + ExprCandVec.push_back(ConstantCandidate( + ConstantInt::get(Type::getInt32Ty(*Ctx), Offset.getLimitedValue()), + ConstExpr)); + Itr->second = ExprCandVec.size() - 1; + } + ExprCandVec[Itr->second].addUser(Inst, Idx, Cost); + } +} + /// Check the operand for instruction Inst at index Idx. void ConstantHoistingPass::collectConstantCandidates( ConstCandMapType &ConstCandMap, Instruction *Inst, unsigned Idx) { @@ -402,6 +454,10 @@ // Visit constant expressions that have constant integers. if (auto ConstExpr = dyn_cast(Opnd)) { + // Handle constant gep expressions. + if (ConstHoistGEP && ConstExpr->isGEPWithNoNotionalOverIndexing()) + collectConstantCandidates(ConstCandMap, Inst, Idx, ConstExpr); + // Only visit constant cast expressions. if (!ConstExpr->isCast()) return; @@ -544,7 +600,8 @@ /// Find the base constant within the given range and rebase all other /// constants with respect to the base constant. void ConstantHoistingPass::findAndMakeBaseConstant( - ConstCandVecType::iterator S, ConstCandVecType::iterator E) { + ConstCandVecType::iterator S, ConstCandVecType::iterator E, + SmallVectorImpl &ConstInfoVec) { auto MaxCostItr = S; unsigned NumUses = maximizeConstantsInRange(S, E, MaxCostItr); @@ -552,24 +609,38 @@ if (NumUses <= 1) return; + ConstantInt *ConstInt = MaxCostItr->ConstInt; + ConstantExpr *ConstExpr = MaxCostItr->ConstExpr; ConstantInfo ConstInfo; - ConstInfo.BaseConstant = MaxCostItr->ConstInt; - Type *Ty = ConstInfo.BaseConstant->getType(); + ConstInfo.BaseInt = ConstInt; + // If ConstInfo.BaseExpr is null, the base is a ConstantInt. Its type is + // available by ConstInt->getType() + ConstInfo.BaseExpr = ConstExpr; + ConstInfo.BaseTy = ConstExpr ? ConstExpr->getType() : nullptr; + Type *IntTy = ConstInt->getType(); // Rebase the constants with respect to the base constant. for (auto ConstCand = S; ConstCand != E; ++ConstCand) { - APInt Diff = ConstCand->ConstInt->getValue() - - ConstInfo.BaseConstant->getValue(); - Constant *Offset = Diff == 0 ? nullptr : ConstantInt::get(Ty, Diff); + APInt Diff = ConstCand->ConstInt->getValue() - ConstInt->getValue(); + Constant *Offset = Diff == 0 ? nullptr : ConstantInt::get(IntTy, Diff); + Type *ConstTy = + ConstCand->ConstExpr ? ConstCand->ConstExpr->getType() : nullptr; ConstInfo.RebasedConstants.push_back( - RebasedConstantInfo(std::move(ConstCand->Uses), Offset)); + RebasedConstantInfo(std::move(ConstCand->Uses), Offset, ConstTy)); } - ConstantVec.push_back(std::move(ConstInfo)); + ConstInfoVec.push_back(std::move(ConstInfo)); } /// Finds and combines constant candidates that can be easily /// rematerialized with an add from a common base constant. -void ConstantHoistingPass::findBaseConstants() { +void ConstantHoistingPass::findBaseConstants(GlobalVariable *BaseGV) { + // If BaseGV is nullptr, find base among candidate constant integers; + // Otherwise find base among constant GEPs that share the same BaseGV. + ConstCandVecType &ConstCandVec = BaseGV ? + ConstGEPCandMap[BaseGV] : ConstIntCandVec; + ConstInfoVecType &ConstInfoVec = BaseGV ? + ConstGEPInfoMap[BaseGV] : ConstIntInfoVec; + // Sort the constants by value and type. This invalidates the mapping! llvm::sort(ConstCandVec.begin(), ConstCandVec.end(), [](const ConstantCandidate &LHS, const ConstantCandidate &RHS) { @@ -613,12 +684,12 @@ } // We either have now a different constant type or the constant is not in // range of an add with immediate anymore. - findAndMakeBaseConstant(MinValItr, CC); + findAndMakeBaseConstant(MinValItr, CC, ConstInfoVec); // Start a new base constant search. MinValItr = CC; } // Finalize the last base constant search. - findAndMakeBaseConstant(MinValItr, ConstCandVec.end()); + findAndMakeBaseConstant(MinValItr, ConstCandVec.end(), ConstInfoVec); } /// Updates the operand at Idx in instruction Inst with the result of @@ -653,12 +724,28 @@ /// users. void ConstantHoistingPass::emitBaseConstants(Instruction *Base, Constant *Offset, + Type *Ty, const ConstantUser &ConstUser) { Instruction *Mat = Base; + + // The same offset can be dereferenced to different types in nested struct. + if (!Offset && Ty && Ty != Base->getType()) + Offset = ConstantInt::get(Type::getInt32Ty(*Ctx), 0); + if (Offset) { Instruction *InsertionPt = findMatInsertPt(ConstUser.Inst, ConstUser.OpndIdx); - Mat = BinaryOperator::Create(Instruction::Add, Base, Offset, + if (Ty) { + // Constant being rebased is a ConstantExpr. + Type *Int64Ty = Type::getInt64Ty(*Ctx); + Base = new PtrToIntInst(Base, Int64Ty, "base_ptrtoint", InsertionPt); + Mat = new SExtInst(Offset, Int64Ty, "offset_sext", InsertionPt); + Mat = BinaryOperator::Create(Instruction::Add, Base, Mat, + "const_mat", InsertionPt); + Mat = new IntToPtrInst(Mat, Ty, "mat_inttoptr", InsertionPt); + } else + // Constant being rebased is a ConstantInt. + Mat = BinaryOperator::Create(Instruction::Add, Base, Offset, "const_mat", InsertionPt); LLVM_DEBUG(dbgs() << "Materialize constant (" << *Base->getOperand(0) @@ -702,6 +789,11 @@ // Visit constant expression. if (auto ConstExpr = dyn_cast(Opnd)) { + if (ConstExpr->isGEPWithNoNotionalOverIndexing()) { + // Operand is a ConstantGEP, replace it. + updateOperand(ConstUser.Inst, ConstUser.OpndIdx, Mat); + return; + } Instruction *ConstExprInst = ConstExpr->getAsInstruction(); ConstExprInst->setOperand(0, Mat); ConstExprInst->insertBefore(findMatInsertPt(ConstUser.Inst, @@ -725,23 +817,31 @@ /// Hoist and hide the base constant behind a bitcast and emit /// materialization code for derived constants. -bool ConstantHoistingPass::emitBaseConstants() { +bool ConstantHoistingPass::emitBaseConstants(GlobalVariable *BaseGV) { bool MadeChange = false; - for (auto const &ConstInfo : ConstantVec) { - // Hoist and hide the base constant behind a bitcast. + SmallVectorImpl &ConstInfoVec = + BaseGV ? ConstGEPInfoMap[BaseGV] : ConstIntInfoVec; + for (auto const &ConstInfo : ConstInfoVec) { SmallPtrSet IPSet = findConstantInsertionPoint(ConstInfo); assert(!IPSet.empty() && "IPSet is empty"); unsigned UsesNum = 0; unsigned ReBasesNum = 0; for (Instruction *IP : IPSet) { - IntegerType *Ty = ConstInfo.BaseConstant->getType(); - Instruction *Base = - new BitCastInst(ConstInfo.BaseConstant, Ty, "const", IP); + Instruction *Base = nullptr; + // Hoist and hide the base constant behind a bitcast. + if (ConstInfo.BaseExpr) { + assert(BaseGV && "A base constant expression must have an base GV"); + Type *Ty = ConstInfo.BaseExpr->getType(); + Base = new BitCastInst(ConstInfo.BaseExpr, Ty, "const", IP); + } else { + IntegerType *Ty = ConstInfo.BaseInt->getType(); + Base = new BitCastInst(ConstInfo.BaseInt, Ty, "const", IP); + } Base->setDebugLoc(IP->getDebugLoc()); - LLVM_DEBUG(dbgs() << "Hoist constant (" << *ConstInfo.BaseConstant + LLVM_DEBUG(dbgs() << "Hoist constant (" << *ConstInfo.BaseInt << ") to BB " << IP->getParent()->getName() << '\n' << *Base << '\n'); @@ -756,11 +856,12 @@ // generate rebase for U using the Base dominating U. if (IPSet.size() == 1 || DT->dominates(Base->getParent(), OrigMatInsertBB)) { - emitBaseConstants(Base, RCI.Offset, U); + emitBaseConstants(Base, RCI.Offset, RCI.Ty, U); ReBasesNum++; } - Base->setDebugLoc(DILocation::getMergedLocation(Base->getDebugLoc(), U.Inst->getDebugLoc())); + Base->setDebugLoc(DILocation::getMergedLocation( + Base->getDebugLoc(), U.Inst->getDebugLoc())); } } UsesNum = Uses; @@ -779,7 +880,7 @@ // Base constant is also included in ConstInfo.RebasedConstants, so // deduct 1 from ConstInfo.RebasedConstants.size(). - NumConstantsRebased = ConstInfo.RebasedConstants.size() - 1; + NumConstantsRebased += ConstInfo.RebasedConstants.size() - 1; MadeChange = true; } @@ -801,25 +902,29 @@ this->TTI = &TTI; this->DT = &DT; this->BFI = BFI; + this->DL = &Fn.getParent()->getDataLayout(); + this->Ctx = &Fn.getContext(); this->Entry = &Entry; // Collect all constant candidates. collectConstantCandidates(Fn); - // There are no constant candidates to worry about. - if (ConstCandVec.empty()) - return false; - // Combine constants that can be easily materialized with an add from a common // base constant. - findBaseConstants(); - - // There are no constants to emit. - if (ConstantVec.empty()) - return false; + if (!ConstIntCandVec.empty()) + findBaseConstants(); + for (auto &MapEntry : ConstGEPCandMap) + if (!MapEntry.second.empty()) + findBaseConstants(MapEntry.first); // Finally hoist the base constant and emit materialization code for dependent // constants. - bool MadeChange = emitBaseConstants(); + bool MadeChange = false; + if (!ConstIntInfoVec.empty()) + MadeChange = emitBaseConstants(); + for (auto MapEntry : ConstGEPInfoMap) + if (!MapEntry.second.empty()) + MadeChange |= emitBaseConstants(MapEntry.first); + // Cleanup dead instructions. deleteDeadCastInst(); Index: test/Transforms/ConstantHoisting/AArch64/const-hoist-gep.ll =================================================================== --- /dev/null +++ test/Transforms/ConstantHoisting/AArch64/const-hoist-gep.ll @@ -0,0 +1,44 @@ +; RUN: opt -consthoist -consthoist-gep -S -o - %s | FileCheck %s + +target triple = "aarch64-none--musleabi" + +; Check that constant gep expressions are materialized in the form of +; (Base + Offset), where Base is a constant gep, Offset is an integer. +; CHECK: %const = bitcast i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 0, i32 0) to i32* +; CHECK-NEXT: store i32 undef, i32* %const, align 4 +; CHECK-NEXT: %base_ptrtoint = ptrtoint i32* %const to i64 +; CHECK-NEXT: %offset_sext = sext i32 4 to i64 +; CHECK-NEXT: %const_mat = add i64 %base_ptrtoint, %offset_sext +; CHECK-NEXT: %mat_inttoptr = inttoptr i64 %const_mat to i32* +; CHECK-NEXT: store i32 undef, i32* %mat_inttoptr, align 4 +; CHECK-NEXT: %base_ptrtoint1 = ptrtoint i32* %const to i64 +; CHECK-NEXT: %offset_sext2 = sext i32 160 to i64 +; CHECK-NEXT: %const_mat3 = add i64 %base_ptrtoint1, %offset_sext2 +; CHECK-NEXT: %mat_inttoptr4 = inttoptr i64 %const_mat3 to i32* +; CHECK-NEXT: store i32 undef, i32* %mat_inttoptr4, align 4 +; CHECK-NEXT: %base_ptrtoint5 = ptrtoint i32* %const to i64 +; CHECK-NEXT: %offset_sext6 = sext i32 164 to i64 +; CHECK-NEXT: %const_mat7 = add i64 %base_ptrtoint5, %offset_sext6 +; CHECK-NEXT: %mat_inttoptr8 = inttoptr i64 %const_mat7 to i32* +; CHECK-NEXT: store i32 undef, i32* %mat_inttoptr8, align 4 + +%0 = type { %1, %2, [9 x i16], %6, %7 } +%1 = type { i32, i32, i32, i32, i32, i32, i16, i16, i8, i8, i16, i32, i32, i16, i8, i8 } +%2 = type { i32, %3, i8, i8, i8, i8, i32, %4, %5, [16 x i8], i16, i16, i8, i8, i8, i8, i32, i32, i32 } +%3 = type { i16, i8, i8 } +%4 = type { i16, i8, i8 } +%5 = type { i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8 } +%6 = type { i8, i8 } +%7 = type { [5 x i32], [3 x i32], [6 x i32], [3 x i32], [2 x i32], [4 x i32], [3 x i32], [2 x i32], [4 x i32], [5 x i32], [3 x i32], [6 x i32], [1 x i32], i32, i32, i32, i32, i32, i32 } + +@global = external dso_local local_unnamed_addr global %0, align 4 + +define dso_local void @zot() { +bb: + store i32 undef, i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 0, i32 0), align 4 + store i32 undef, i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 0, i32 1), align 4 + store i32 undef, i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 11, i32 0), align 4 + store i32 undef, i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 11, i32 1), align 4 + ret void +} + Index: test/Transforms/ConstantHoisting/ARM/const-hoist-gep.ll =================================================================== --- /dev/null +++ test/Transforms/ConstantHoisting/ARM/const-hoist-gep.ll @@ -0,0 +1,42 @@ +; RUN: opt -consthoist -consthoist-gep -S -o - %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "thumbv6m-none--musleabi" + +; Check that constant gep expressions are materialized in the form of +; (Base + Offset), where Base is a constant gep, Offset is an integer. +; CHECK-DAG: %const[[C1:[0-9]?]] = bitcast i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 11, i32 0) to i32* +; CHECK-DAG: %const[[C2:[0-9]?]] = bitcast i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 0, i32 0) to i32* +; CHECK: store i32 undef, i32* %const[[C2]], align 4 +; CHECK-NEXT: %[[INT1:[a-z0-9_]+]] = ptrtoint i32* %const[[C2]] to i64 +; CHECK-NEXT: %[[OFF1:[a-z0-9_]+]] = sext i32 4 to i64 +; CHECK-NEXT: %[[MAT1:[a-z0-9_]+]] = add i64 %[[INT1]], %[[OFF1]] +; CHECK-NEXT: %[[PTR1:[a-z0-9_]+]] = inttoptr i64 %[[MAT1]] to i32* +; CHECK-NEXT: store i32 undef, i32* %[[PTR1]], align 4 +; CHECK: store i32 undef, i32* %const[[C1]], align 4 +; CHECK-NEXT: %[[INT2:[a-z0-9_]+]] = ptrtoint i32* %const[[C1]] to i64 +; CHECK-NEXT: %[[OFF2:[a-z0-9_]+]] = sext i32 4 to i64 +; CHECK-NEXT: %[[MAT2:[a-z0-9_]+]] = add i64 %[[INT2]], %[[OFF2]] +; CHECK-NEXT: %[[PTR2:[a-z0-9_]+]] = inttoptr i64 %[[MAT2]] to i32* +; CHECK-NEXT: store i32 undef, i32* %[[PTR2]], align 4 + +%0 = type { %1, %2, [9 x i16], %6, %7 } +%1 = type { i32, i32, i32, i32, i32, i32, i16, i16, i8, i8, i16, i32, i32, i16, i8, i8 } +%2 = type { i32, %3, i8, i8, i8, i8, i32, %4, %5, [16 x i8], i16, i16, i8, i8, i8, i8, i32, i32, i32 } +%3 = type { i16, i8, i8 } +%4 = type { i16, i8, i8 } +%5 = type { i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8 } +%6 = type { i8, i8 } +%7 = type { [5 x i32], [3 x i32], [6 x i32], [3 x i32], [2 x i32], [4 x i32], [3 x i32], [2 x i32], [4 x i32], [5 x i32], [3 x i32], [6 x i32], [1 x i32], i32, i32, i32, i32, i32, i32 } + +@global = external dso_local local_unnamed_addr global %0, align 4 + +define dso_local void @zot() { +bb: + store i32 undef, i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 0, i32 0), align 4 + store i32 undef, i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 0, i32 1), align 4 + store i32 undef, i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 11, i32 0), align 4 + store i32 undef, i32* getelementptr inbounds (%0, %0* @global, i32 0, i32 4, i32 11, i32 1), align 4 + ret void +} + Index: test/Transforms/ConstantHoisting/ARM/same-offset-multi-types.ll =================================================================== --- /dev/null +++ test/Transforms/ConstantHoisting/ARM/same-offset-multi-types.ll @@ -0,0 +1,46 @@ +; RUN: opt -consthoist -consthoist-gep -S -o - %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "thumbv6m-none--musleabi" + +; Check that for the same offset from the base constant, different types are materialized separately. +; CHECK: %const = bitcast %5** getelementptr inbounds (%0, %0* @global, i32 0, i32 2, i32 0) to %5** +; CHECK: %tmp = load %5*, %5** %const, align 4 +; CHECK: %base_ptrtoint = ptrtoint %5** %const to i64 +; CHECK: %offset_sext = sext i32 0 to i64 +; CHECK: %const_mat = add i64 %base_ptrtoint, %offset_sext +; CHECK: %mat_inttoptr = inttoptr i64 %const_mat to %4* +; CHECK: tail call void undef(%5* nonnull %tmp, %4* %mat_inttoptr) + + +%0 = type { [16 x %1], %2, %4, [16 x %5], %6, %7, i32, [4 x i32], [8 x %3], i8, i8, i8, i8, i8, i8, i8, %8, %11, %11*, i32, i16, i8, i8, i8, i8, i8, i8, [15 x i16], i8, i8, [23 x %12], i8, i8*, i8, %13, i8, i8 } +%1 = type { i32, i32, i8, i8, i8, i8, i8, i8, i8, i8 } +%2 = type { %3*, i16, i16, i16 } +%3 = type { [4 x i32] } +%4 = type { %5*, %5*, i8 } +%5 = type { [4 x i32], i8*, i8, i8 } +%6 = type { i8, [4 x i32] } +%7 = type { i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 } +%8 = type { [16 x %9], %9*, %9*, %9*, %9*, %11, %11, %11, i8, i8, i8, i8 } +%9 = type { %1, %11, %11, %9*, %9*, %10, i8, i8, i8, i8 } +%10 = type { i32, i16 } +%11 = type { %11*, %11* } +%12 = type { i8, i16, i32 } +%13 = type { i32, i32, i8 } + +@global = external dso_local global %0, align 4 + +; Function Attrs: nounwind optsize ssp +define dso_local void @zot() { +bb: + br i1 undef, label %bb2, label %bb1 + +bb1: ; preds = %bb + %tmp = load %5*, %5** getelementptr inbounds (%0, %0* @global, i32 0, i32 2, i32 0), align 4 + tail call void undef(%5* nonnull %tmp, %4* getelementptr inbounds (%0, %0* @global, i32 0, i32 2)) + unreachable + +bb2: ; preds = %bb + ret void +} +