From 1ce54d6be2bfba92c82372186a22aad6b0e09fbc Mon Sep 17 00:00:00 2001 From: James Molloy Date: Fri, 23 Sep 2016 12:15:58 +0000 Subject: [PATCH] [ARM] Promote small global constants to constant pools If a constant is unamed_addr and is only used within one function, we can save on the code size and runtime cost of an indirection by changing the global's storage to inside the constant pool. For example, instead of: ldr r0, .CPI0 bl printf bx lr .CPI0: &format_string format_string: .asciz "hello, world!\n" We can emit: adr r0, .CPI0 bl printf bx lr .CPI0: .asciz "hello, world!\n" This can cause significant code size savings when many small strings are used in one function (4 bytes per string). This recommit contains fixes for a nasty bug related to fast-isel fallback - because fast-isel doesn't know about this optimization, if it runs and emits references to a string that we inline (because fast-isel fell back to SDAG) we will end up with an inlined string and also an out-of-line string, and we won't emit the out-of-line string, causing backend failures. It also contains fixes for emitting .text relocations which made the sanitizer bots unhappy. llvm-svn: 282241 --- llvm/lib/Target/ARM/ARMAsmPrinter.cpp | 33 ++++ llvm/lib/Target/ARM/ARMAsmPrinter.h | 13 +- llvm/lib/Target/ARM/ARMConstantIslandPass.cpp | 6 + llvm/lib/Target/ARM/ARMConstantPoolValue.cpp | 12 ++ llvm/lib/Target/ARM/ARMConstantPoolValue.h | 20 ++- llvm/lib/Target/ARM/ARMISelLowering.cpp | 170 ++++++++++++++++++ .../lib/Target/ARM/ARMMachineFunctionInfo.cpp | 2 +- llvm/lib/Target/ARM/ARMMachineFunctionInfo.h | 25 ++- .../CodeGen/ARM/constantpool-promote-dbg.ll | 44 +++++ llvm/test/CodeGen/ARM/constantpool-promote.ll | 141 +++++++++++++++ 10 files changed, 460 insertions(+), 6 deletions(-) create mode 100644 llvm/test/CodeGen/ARM/constantpool-promote-dbg.ll create mode 100644 llvm/test/CodeGen/ARM/constantpool-promote.ll diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp index db6eefdb9999..27d2c4b356d7 100644 --- a/llvm/lib/Target/ARM/ARMAsmPrinter.cpp +++ b/llvm/lib/Target/ARM/ARMAsmPrinter.cpp @@ -97,6 +97,13 @@ void ARMAsmPrinter::EmitXXStructor(const DataLayout &DL, const Constant *CV) { OutStreamer->EmitValue(E, Size); } +void ARMAsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { + if (PromotedGlobals.count(GV)) + // The global was promoted into a constant pool. It should not be emitted. + return; + AsmPrinter::EmitGlobalVariable(GV); +} + /// runOnMachineFunction - This uses the EmitInstruction() /// method to print assembly for each instruction. /// @@ -109,6 +116,12 @@ bool ARMAsmPrinter::runOnMachineFunction(MachineFunction &MF) { const Function* F = MF.getFunction(); const TargetMachine& TM = MF.getTarget(); + // Collect all globals that had their storage promoted to a constant pool. + // Functions are emitted before variables, so this accumulates promoted + // globals from all functions in PromotedGlobals. + for (auto *GV : AFI->getGlobalsPromotedToConstantPool()) + PromotedGlobals.insert(GV); + // Calculate this function's optimization goal. unsigned OptimizationGoal; if (F->hasFnAttribute(Attribute::OptimizeNone)) @@ -1002,6 +1015,26 @@ EmitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) { ARMConstantPoolValue *ACPV = static_cast(MCPV); + if (ACPV->isPromotedGlobal()) { + // This constant pool entry is actually a global whose storage has been + // promoted into the constant pool. This global may be referenced still + // by debug information, and due to the way AsmPrinter is set up, the debug + // info is immutable by the time we decide to promote globals to constant + // pools. Because of this, we need to ensure we emit a symbol for the global + // with private linkage (the default) so debug info can refer to it. + // + // However, if this global is promoted into several functions we must ensure + // we don't try and emit duplicate symbols! + auto *ACPC = cast(ACPV); + auto *GV = ACPC->getPromotedGlobal(); + if (!EmittedPromotedGlobalLabels.count(GV)) { + MCSymbol *GVSym = getSymbol(GV); + OutStreamer->EmitLabel(GVSym); + EmittedPromotedGlobalLabels.insert(GV); + } + return EmitGlobalConstant(DL, ACPC->getPromotedGlobalInit()); + } + MCSymbol *MCSym; if (ACPV->isLSDA()) { MCSym = getCurExceptionSym(); diff --git a/llvm/lib/Target/ARM/ARMAsmPrinter.h b/llvm/lib/Target/ARM/ARMAsmPrinter.h index fe4a2bfca352..9eafde55d4dd 100644 --- a/llvm/lib/Target/ARM/ARMAsmPrinter.h +++ b/llvm/lib/Target/ARM/ARMAsmPrinter.h @@ -56,6 +56,16 @@ class LLVM_LIBRARY_VISIBILITY ARMAsmPrinter : public AsmPrinter { /// -1 if uninitialized, 0 if conflicting goals int OptimizationGoals; + /// List of globals that have had their storage promoted to a constant + /// pool. This lives between calls to runOnMachineFunction and collects + /// data from every MachineFunction. It is used during doFinalization + /// when all non-function globals are emitted. + SmallPtrSet PromotedGlobals; + /// Set of globals in PromotedGlobals that we've emitted labels for. + /// We need to emit labels even for promoted globals so that DWARF + /// debug info can link properly. + SmallPtrSet EmittedPromotedGlobalLabels; + public: explicit ARMAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer); @@ -90,7 +100,8 @@ public: void EmitStartOfAsmFile(Module &M) override; void EmitEndOfAsmFile(Module &M) override; void EmitXXStructor(const DataLayout &DL, const Constant *CV) override; - + void EmitGlobalVariable(const GlobalVariable *GV) override; + // lowerOperand - Convert a MachineOperand into the equivalent MCOperand. bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp); diff --git a/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp b/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp index fd679f0f6b0d..d2e0725581d1 100644 --- a/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp +++ b/llvm/lib/Target/ARM/ARMConstantIslandPass.cpp @@ -767,6 +767,7 @@ initializeFunctionInfo(const std::vector &CPEMIs) { case ARM::LDRi12: case ARM::LDRcp: case ARM::t2LDRpci: + case ARM::t2LDRHpci: Bits = 12; // +-offset_12 NegOk = true; break; @@ -782,6 +783,11 @@ initializeFunctionInfo(const std::vector &CPEMIs) { Scale = 4; // +-(offset_8*4) NegOk = true; break; + + case ARM::tLDRHi: + Bits = 5; + Scale = 2; // +(offset_5*2) + break; } // Remember that this is a user of a CP entry. diff --git a/llvm/lib/Target/ARM/ARMConstantPoolValue.cpp b/llvm/lib/Target/ARM/ARMConstantPoolValue.cpp index f13ae481bdbe..05d1b84b3286 100644 --- a/llvm/lib/Target/ARM/ARMConstantPoolValue.cpp +++ b/llvm/lib/Target/ARM/ARMConstantPoolValue.cpp @@ -131,12 +131,24 @@ ARMConstantPoolConstant::ARMConstantPoolConstant(const Constant *C, AddCurrentAddress), CVal(C) {} +ARMConstantPoolConstant::ARMConstantPoolConstant(const GlobalVariable *GV, + const Constant *C) + : ARMConstantPoolValue((Type *)C->getType(), 0, ARMCP::CPPromotedGlobal, 0, + ARMCP::no_modifier, false), + CVal(C), GVar(GV) {} + ARMConstantPoolConstant * ARMConstantPoolConstant::Create(const Constant *C, unsigned ID) { return new ARMConstantPoolConstant(C, ID, ARMCP::CPValue, 0, ARMCP::no_modifier, false); } +ARMConstantPoolConstant * +ARMConstantPoolConstant::Create(const GlobalVariable *GVar, + const Constant *Initializer) { + return new ARMConstantPoolConstant(GVar, Initializer); +} + ARMConstantPoolConstant * ARMConstantPoolConstant::Create(const GlobalValue *GV, ARMCP::ARMCPModifier Modifier) { diff --git a/llvm/lib/Target/ARM/ARMConstantPoolValue.h b/llvm/lib/Target/ARM/ARMConstantPoolValue.h index fae64dc2455c..581bf8b1d745 100644 --- a/llvm/lib/Target/ARM/ARMConstantPoolValue.h +++ b/llvm/lib/Target/ARM/ARMConstantPoolValue.h @@ -24,6 +24,7 @@ namespace llvm { class BlockAddress; class Constant; class GlobalValue; +class GlobalVariable; class LLVMContext; class MachineBasicBlock; @@ -33,7 +34,8 @@ namespace ARMCP { CPExtSymbol, CPBlockAddress, CPLSDA, - CPMachineBasicBlock + CPMachineBasicBlock, + CPPromotedGlobal }; enum ARMCPModifier { @@ -103,7 +105,8 @@ public: bool isBlockAddress() const { return Kind == ARMCP::CPBlockAddress; } bool isLSDA() const { return Kind == ARMCP::CPLSDA; } bool isMachineBasicBlock() const{ return Kind == ARMCP::CPMachineBasicBlock; } - + bool isPromotedGlobal() const{ return Kind == ARMCP::CPPromotedGlobal; } + int getExistingMachineCPValue(MachineConstantPool *CP, unsigned Alignment) override; @@ -133,6 +136,7 @@ inline raw_ostream &operator<<(raw_ostream &O, const ARMConstantPoolValue &V) { /// Functions, and BlockAddresses. class ARMConstantPoolConstant : public ARMConstantPoolValue { const Constant *CVal; // Constant being loaded. + const GlobalVariable *GVar = nullptr; ARMConstantPoolConstant(const Constant *C, unsigned ID, @@ -146,11 +150,14 @@ class ARMConstantPoolConstant : public ARMConstantPoolValue { unsigned char PCAdj, ARMCP::ARMCPModifier Modifier, bool AddCurrentAddress); + ARMConstantPoolConstant(const GlobalVariable *GV, const Constant *Init); public: static ARMConstantPoolConstant *Create(const Constant *C, unsigned ID); static ARMConstantPoolConstant *Create(const GlobalValue *GV, ARMCP::ARMCPModifier Modifier); + static ARMConstantPoolConstant *Create(const GlobalVariable *GV, + const Constant *Initializer); static ARMConstantPoolConstant *Create(const Constant *C, unsigned ID, ARMCP::ARMCPKind Kind, unsigned char PCAdj); @@ -162,6 +169,12 @@ public: const GlobalValue *getGV() const; const BlockAddress *getBlockAddress() const; + const GlobalVariable *getPromotedGlobal() const { + return dyn_cast_or_null(GVar); + } + const Constant *getPromotedGlobalInit() const { + return CVal; + } int getExistingMachineCPValue(MachineConstantPool *CP, unsigned Alignment) override; @@ -174,7 +187,8 @@ public: void print(raw_ostream &O) const override; static bool classof(const ARMConstantPoolValue *APV) { - return APV->isGlobalValue() || APV->isBlockAddress() || APV->isLSDA(); + return APV->isGlobalValue() || APV->isBlockAddress() || APV->isLSDA() || + APV->isPromotedGlobal(); } bool equals(const ARMConstantPoolConstant *A) const { diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp index 434b45464833..018fb1617d69 100644 --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -37,6 +37,7 @@ #include "llvm/IR/CallingConv.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instruction.h" @@ -59,12 +60,28 @@ using namespace llvm; STATISTIC(NumTailCalls, "Number of tail calls"); STATISTIC(NumMovwMovt, "Number of GAs materialized with movw + movt"); STATISTIC(NumLoopByVals, "Number of loops generated for byval arguments"); +STATISTIC(NumConstpoolPromoted, + "Number of constants with their storage promoted into constant pools"); static cl::opt ARMInterworking("arm-interworking", cl::Hidden, cl::desc("Enable / disable ARM interworking (for debugging only)"), cl::init(true)); +static cl::opt EnableConstpoolPromotion( + "arm-promote-constant", cl::Hidden, + cl::desc("Enable / disable promotion of unnamed_addr constants into " + "constant pools"), + cl::init(true)); +static cl::opt ConstpoolPromotionMaxSize( + "arm-promote-constant-max-size", cl::Hidden, + cl::desc("Maximum size of constant to promote into a constant pool"), + cl::init(64)); +static cl::opt ConstpoolPromotionMaxTotal( + "arm-promote-constant-max-total", cl::Hidden, + cl::desc("Maximum size of ALL constants to promote into a constant pool"), + cl::init(128)); + namespace { class ARMCCState : public CCState { public: @@ -2963,6 +2980,154 @@ ARMTargetLowering::LowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const { llvm_unreachable("bogus TLS model"); } +/// Return true if all users of V are within function F, looking through +/// ConstantExprs. +static bool allUsersAreInFunction(const Value *V, const Function *F) { + SmallVector Worklist; + for (auto *U : V->users()) + Worklist.push_back(U); + while (!Worklist.empty()) { + auto *U = Worklist.pop_back_val(); + if (isa(U)) { + for (auto *UU : U->users()) + Worklist.push_back(UU); + continue; + } + + auto *I = dyn_cast(U); + if (!I || I->getParent()->getParent() != F) + return false; + } + return true; +} + +/// Return true if all users of V are within some (any) function, looking through +/// ConstantExprs. In other words, are there any global constant users? +static bool allUsersAreInFunctions(const Value *V) { + SmallVector Worklist; + for (auto *U : V->users()) + Worklist.push_back(U); + while (!Worklist.empty()) { + auto *U = Worklist.pop_back_val(); + if (isa(U)) { + for (auto *UU : U->users()) + Worklist.push_back(UU); + continue; + } + + if (!isa(U)) + return false; + } + return true; +} + +// Return true if T is an integer, float or an array/vector of either. +static bool isSimpleType(Type *T) { + if (T->isIntegerTy() || T->isFloatingPointTy()) + return true; + Type *SubT = nullptr; + if (T->isArrayTy()) + SubT = T->getArrayElementType(); + else if (T->isVectorTy()) + SubT = T->getVectorElementType(); + else + return false; + return SubT->isIntegerTy() || SubT->isFloatingPointTy(); +} + +static SDValue promoteToConstantPool(const GlobalValue *GV, SelectionDAG &DAG, + EVT PtrVT, SDLoc dl) { + // If we're creating a pool entry for a constant global with unnamed address, + // and the global is small enough, we can emit it inline into the constant pool + // to save ourselves an indirection. + // + // This is a win if the constant is only used in one function (so it doesn't + // need to be duplicated) or duplicating the constant wouldn't increase code + // size (implying the constant is no larger than 4 bytes). + const Function *F = DAG.getMachineFunction().getFunction(); + + // We rely on this decision to inline being idemopotent and unrelated to the + // use-site. We know that if we inline a variable at one use site, we'll + // inline it elsewhere too (and reuse the constant pool entry). Fast-isel + // doesn't know about this optimization, so bail out if it's enabled else + // we could decide to inline here (and thus never emit the GV) but require + // the GV from fast-isel generated code. + if (!EnableConstpoolPromotion || + DAG.getMachineFunction().getTarget().Options.EnableFastISel) + return SDValue(); + + auto *GVar = dyn_cast(GV); + if (!GVar || !GVar->hasInitializer() || + !GVar->isConstant() || !GVar->hasGlobalUnnamedAddr() || + !GVar->hasLocalLinkage()) + return SDValue(); + + // Ensure that we don't try and inline any type that contains pointers. If + // we inline a value that contains relocations, we move the relocations from + // .data to .text which is not ideal. + auto *Init = GVar->getInitializer(); + if (!isSimpleType(Init->getType())) + return SDValue(); + + // The constant islands pass can only really deal with alignment requests + // <= 4 bytes and cannot pad constants itself. Therefore we cannot promote + // any type wanting greater alignment requirements than 4 bytes. We also + // can only promote constants that are multiples of 4 bytes in size or + // are paddable to a multiple of 4. Currently we only try and pad constants + // that are strings for simplicity. + auto *CDAInit = dyn_cast(Init); + unsigned Size = DAG.getDataLayout().getTypeAllocSize(Init->getType()); + unsigned Align = DAG.getDataLayout().getABITypeAlignment(Init->getType()); + unsigned RequiredPadding = 4 - (Size % 4); + bool PaddingPossible = + RequiredPadding == 4 || (CDAInit && CDAInit->isString()); + if (!PaddingPossible || Align > 4 || Size > ConstpoolPromotionMaxSize) + return SDValue(); + + unsigned PaddedSize = Size + ((RequiredPadding == 4) ? 0 : RequiredPadding); + MachineFunction &MF = DAG.getMachineFunction(); + ARMFunctionInfo *AFI = MF.getInfo(); + + // We can't bloat the constant pool too much, else the ConstantIslands pass + // may fail to converge. If we haven't promoted this global yet (it may have + // multiple uses), and promoting it would increase the constant pool size (Sz + // > 4), ensure we have space to do so up to MaxTotal. + if (!AFI->getGlobalsPromotedToConstantPool().count(GVar) && Size > 4) + if (AFI->getPromotedConstpoolIncrease() + PaddedSize - 4 >= + ConstpoolPromotionMaxTotal) + return SDValue(); + + // This is only valid if all users are in a single function OR it has users + // in multiple functions but it no larger than a pointer. We also check if + // GVar has constant (non-ConstantExpr) users. If so, it essentially has its + // address taken. + if (!allUsersAreInFunction(GVar, F) && + !(Size <= 4 && allUsersAreInFunctions(GVar))) + return SDValue(); + + // We're going to inline this global. Pad it out if needed. + if (RequiredPadding != 4) { + StringRef S = CDAInit->getAsString(); + + SmallVector V(S.size()); + std::copy(S.bytes_begin(), S.bytes_end(), V.begin()); + while (RequiredPadding--) + V.push_back(0); + Init = ConstantDataArray::get(*DAG.getContext(), V); + } + + auto CPVal = ARMConstantPoolConstant::Create(GVar, Init); + SDValue CPAddr = + DAG.getTargetConstantPool(CPVal, PtrVT, /*Align=*/4); + if (!AFI->getGlobalsPromotedToConstantPool().count(GVar)) { + AFI->markGlobalAsPromotedToConstantPool(GVar); + AFI->setPromotedConstpoolIncrease(AFI->getPromotedConstpoolIncrease() + + PaddedSize - 4); + } + ++NumConstpoolPromoted; + return DAG.getNode(ARMISD::Wrapper, dl, MVT::i32, CPAddr); +} + SDValue ARMTargetLowering::LowerGlobalAddressELF(SDValue Op, SelectionDAG &DAG) const { EVT PtrVT = getPointerTy(DAG.getDataLayout()); @@ -2974,6 +3139,11 @@ SDValue ARMTargetLowering::LowerGlobalAddressELF(SDValue Op, bool IsRO = (isa(GV) && cast(GV)->isConstant()) || isa(GV); + + if (TM.shouldAssumeDSOLocal(*GV->getParent(), GV)) + if (SDValue V = promoteToConstantPool(GV, DAG, PtrVT, dl)) + return V; + if (isPositionIndependent()) { bool UseGOT_PREL = !TM.shouldAssumeDSOLocal(*GV->getParent(), GV); diff --git a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.cpp b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.cpp index b6dee9ff8385..72e37a384256 100644 --- a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.cpp +++ b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.cpp @@ -21,4 +21,4 @@ ARMFunctionInfo::ARMFunctionInfo(MachineFunction &MF) FramePtrSpillOffset(0), GPRCS1Offset(0), GPRCS2Offset(0), DPRCSOffset(0), GPRCS1Size(0), GPRCS2Size(0), DPRCSSize(0), PICLabelUId(0), VarArgsFrameIndex(0), HasITBlocks(false), - ArgumentStackSize(0), IsSplitCSR(false) {} + ArgumentStackSize(0), IsSplitCSR(false), PromotedGlobalsIncrease(0) {} diff --git a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h index f71497240ff3..8c485e89bf54 100644 --- a/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h +++ b/llvm/lib/Target/ARM/ARMMachineFunctionInfo.h @@ -121,6 +121,12 @@ class ARMFunctionInfo : public MachineFunctionInfo { /// copies. bool IsSplitCSR; + /// Globals that have had their storage promoted into the constant pool. + SmallPtrSet PromotedGlobals; + + /// The amount the literal pool has been increasedby due to promoted globals. + int PromotedGlobalsIncrease; + public: ARMFunctionInfo() : isThumb(false), @@ -131,7 +137,8 @@ public: FramePtrSpillOffset(0), GPRCS1Offset(0), GPRCS2Offset(0), DPRCSOffset(0), GPRCS1Size(0), GPRCS2Size(0), DPRCSAlignGapSize(0), DPRCSSize(0), NumAlignedDPRCS2Regs(0), PICLabelUId(0), - VarArgsFrameIndex(0), HasITBlocks(false), IsSplitCSR(false) {} + VarArgsFrameIndex(0), HasITBlocks(false), IsSplitCSR(false), + PromotedGlobalsIncrease(0) {} explicit ARMFunctionInfo(MachineFunction &MF); @@ -226,6 +233,22 @@ public: } return It; } + + /// Indicate to the backend that \c GV has had its storage changed to inside + /// a constant pool. This means it no longer needs to be emitted as a + /// global variable. + void markGlobalAsPromotedToConstantPool(const GlobalVariable *GV) { + PromotedGlobals.insert(GV); + } + SmallPtrSet& getGlobalsPromotedToConstantPool() { + return PromotedGlobals; + } + int getPromotedConstpoolIncrease() const { + return PromotedGlobalsIncrease; + } + void setPromotedConstpoolIncrease(int Sz) { + PromotedGlobalsIncrease = Sz; + } }; } // End llvm namespace diff --git a/llvm/test/CodeGen/ARM/constantpool-promote-dbg.ll b/llvm/test/CodeGen/ARM/constantpool-promote-dbg.ll new file mode 100644 index 000000000000..ae765d26dcac --- /dev/null +++ b/llvm/test/CodeGen/ARM/constantpool-promote-dbg.ll @@ -0,0 +1,44 @@ +; RUN: llc -relocation-model=static < %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "thumbv7m--linux-gnu" + +@.str = private unnamed_addr constant [4 x i8] c"abc\00", align 1 + +; CHECK-LABEL: fn1 +; CHECK: .str: +define arm_aapcscc i8* @fn1() local_unnamed_addr #0 !dbg !8 { +entry: + ret i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), !dbg !14 +} + +; CHECK-LABEL: fn2 +; CHECK-NOT: .str: +define arm_aapcscc i8* @fn2() local_unnamed_addr #0 !dbg !15 { +entry: + ret i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 1), !dbg !16 +} + +attributes #0 = { minsize norecurse nounwind optsize readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="cortex-m3" "target-features"="+hwdiv,+soft-float,-crypto,-neon" "unsafe-fp-math"="false" "use-soft-float"="true" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.9.0 (http://llvm.org/git/clang.git 075a2bc2570dfcbb6d6aed6c836e4c62b37afea6)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "/Users/jammol01/Code/test.c", directory: "/Users/jammol01/Code/llvm-git/build") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 1, !"min_enum_size", i32 4} +!7 = !{!"clang version 3.9.0 (http://llvm.org/git/clang.git 075a2bc2570dfcbb6d6aed6c836e4c62b37afea6)"} +!8 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, variables: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{!11} +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 32, align: 32) +!12 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !13) +!13 = !DIBasicType(name: "char", size: 8, align: 8, encoding: DW_ATE_unsigned_char) +!14 = !DILocation(line: 2, column: 5, scope: !8) +!15 = distinct !DISubprogram(name: "fn2", scope: !1, file: !1, line: 4, type: !9, isLocal: false, isDefinition: true, scopeLine: 4, isOptimized: true, unit: !0, variables: !2) +!16 = !DILocation(line: 5, column: 5, scope: !15) diff --git a/llvm/test/CodeGen/ARM/constantpool-promote.ll b/llvm/test/CodeGen/ARM/constantpool-promote.ll new file mode 100644 index 000000000000..c70f08cff17b --- /dev/null +++ b/llvm/test/CodeGen/ARM/constantpool-promote.ll @@ -0,0 +1,141 @@ +; RUN: llc -relocation-model=static < %s | FileCheck %s +; RUN: llc -relocation-model=pic < %s | FileCheck %s +; RUN: llc -relocation-model=ropi < %s | FileCheck %s +; RUN: llc -relocation-model=rwpi < %s | FileCheck %s + +target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-n32-S64" +target triple = "armv7--linux-gnueabihf" + +@.str = private unnamed_addr constant [2 x i8] c"s\00", align 1 +@.str1 = private unnamed_addr constant [69 x i8] c"this string is far too long to fit in a literal pool by far and away\00", align 1 +@.str2 = private unnamed_addr constant [27 x i8] c"this string is just right!\00", align 1 +@.str3 = private unnamed_addr constant [26 x i8] c"this string is used twice\00", align 1 +@.str4 = private unnamed_addr constant [29 x i8] c"same string in two functions\00", align 1 +@.arr1 = private unnamed_addr constant [2 x i16] [i16 3, i16 4], align 2 +@.arr2 = private unnamed_addr constant [2 x i16] [i16 7, i16 8], align 2 +@.arr3 = private unnamed_addr constant [2 x i16*] [i16* null, i16* null], align 4 +@.ptr = private unnamed_addr constant [2 x i16*] [i16* getelementptr inbounds ([2 x i16], [2 x i16]* @.arr2, i32 0, i32 0), i16* null], align 2 + +; CHECK-LABEL: @test1 +; CHECK: adr r0, [[x:.*]] +; CHECK: [[x]]: +; CHECK: .asciz "s\000\000" +define void @test1() #0 { + tail call void @a(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str, i32 0, i32 0)) #2 + ret void +} + +declare void @a(i8*) #1 + +; CHECK-LABEL: @test2 +; CHECK-NOT: .asci +; CHECK: .fnend +define void @test2() #0 { + tail call void @a(i8* getelementptr inbounds ([69 x i8], [69 x i8]* @.str1, i32 0, i32 0)) #2 + ret void +} + +; CHECK-LABEL: @test3 +; CHECK: adr r0, [[x:.*]] +; CHECK: [[x]]: +; CHECK: .asciz "this string is just right!\000" +define void @test3() #0 { + tail call void @a(i8* getelementptr inbounds ([27 x i8], [27 x i8]* @.str2, i32 0, i32 0)) #2 + ret void +} + + +; CHECK-LABEL: @test4 +; CHECK: adr r{{.*}}, [[x:.*]] +; CHECK: [[x]]: +; CHECK: .asciz "this string is used twice\000\000" +define void @test4() #0 { + tail call void @a(i8* getelementptr inbounds ([26 x i8], [26 x i8]* @.str3, i32 0, i32 0)) #2 + tail call void @a(i8* getelementptr inbounds ([26 x i8], [26 x i8]* @.str3, i32 0, i32 0)) #2 + ret void +} + +; CHECK-LABEL: @test5a +; CHECK-NOT: adr +define void @test5a() #0 { + tail call void @a(i8* getelementptr inbounds ([29 x i8], [29 x i8]* @.str4, i32 0, i32 0)) #2 + ret void +} + +define void @test5b() #0 { + tail call void @b(i8* getelementptr inbounds ([29 x i8], [29 x i8]* @.str4, i32 0, i32 0)) #2 + ret void +} + +; CHECK-LABEL: @test6a +; CHECK: adr r0, [[x:.*]] +; CHECK: [[x]]: +; CHECK: .short 3 +; CHECK: .short 4 +define void @test6a() #0 { + tail call void @c(i16* getelementptr inbounds ([2 x i16], [2 x i16]* @.arr1, i32 0, i32 0)) #2 + ret void +} + +; CHECK-LABEL: @test6b +; CHECK: adr r0, [[x:.*]] +; CHECK: [[x]]: +; CHECK: .short 3 +; CHECK: .short 4 +define void @test6b() #0 { + tail call void @c(i16* getelementptr inbounds ([2 x i16], [2 x i16]* @.arr1, i32 0, i32 0)) #2 + ret void +} + +; This shouldn't be promoted, as the string is used by another global. +; CHECK-LABEL: @test7 +; CHECK-NOT: adr +define void @test7() #0 { + tail call void @c(i16* getelementptr inbounds ([2 x i16], [2 x i16]* @.arr2, i32 0, i32 0)) #2 + ret void +} + +; This shouldn't be promoted, because the array contains pointers. +; CHECK-LABEL: @test8 +; CHECK-NOT: .zero +; CHECK: .fnend +define void @test8() #0 { + %a = load i16*, i16** getelementptr inbounds ([2 x i16*], [2 x i16*]* @.arr3, i32 0, i32 0) + tail call void @c(i16* %a) #2 + ret void +} + +@fn1.a = private unnamed_addr constant [4 x i16] [i16 4, i16 0, i16 0, i16 0], align 2 +@fn2.a = private unnamed_addr constant [8 x i8] [i8 4, i8 0, i8 0, i8 0, i8 23, i8 0, i8 6, i8 0], align 1 + +; Just check these don't crash. +define void @fn1() "target-features"="+strict-align" { +entry: + %a = alloca [4 x i16], align 2 + %0 = bitcast [4 x i16]* %a to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %0, i8* bitcast ([4 x i16]* @fn1.a to i8*), i32 8, i32 2, i1 false) + ret void +} + +define void @fn2() "target-features"="+strict-align" { +entry: + %a = alloca [8 x i8], align 2 + %0 = bitcast [8 x i8]* %a to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %0, i8* bitcast ([8 x i8]* @fn2.a to i8*), i32 16, i32 1, i1 false) + ret void +} + +declare void @b(i8*) #1 +declare void @c(i16*) #1 +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture writeonly, i8* nocapture readonly, i32, i32, i1) + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 1, !"min_enum_size", i32 4} +!2 = !{!"Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)"}