llvm.org GIT mirror llvm / 6bf5b2b
[AsmPrinter] Access pointers to globals via pcrel GOT entries Front-ends could use global unnamed_addr to hold pointers to other symbols, like @gotequivalent below: @foo = global i32 42 @gotequivalent = private unnamed_addr constant i32* @foo @delta = global i32 trunc (i64 sub (i64 ptrtoint (i32** @gotequivalent to i64), i64 ptrtoint (i32* @delta to i64)) to i32) The global @delta holds a data "PC"-relative offset to @gotequivalent, an unnamed pointer to @foo. The darwin/x86-64 assembly output for this follows: .globl _foo _foo: .long 42 .globl _gotequivalent _gotequivalent: .quad _foo .globl _delta _delta: .long _gotequivalent-_delta Since unnamed_addr indicates that the address is not significant, only the content, we can optimize the case above by replacing pc-relative accesses to "GOT equivalent" globals, by a PC relative access to the GOT entry of the final symbol instead. Therefore, "delta" can contain a pc relative relocation to foo's GOT entry and we avoid the emission of "gotequivalent", yielding the assembly code below: .globl _foo _foo: .long 42 .globl _delta _delta: .long _foo@GOTPCREL+4 There are a couple of advantages of doing this: (1) Front-ends that need to emit a great deal of data to store pointers to external symbols could save space by not emitting such "got equivalent" globals and (2) IR constructs combined with this opt opens a way to represent GOT pcrel relocations by using the LLVM IR, which is something we previously had no way to express. Differential Revision: http://reviews.llvm.org/D6922 rdar://problem/18534217 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@230264 91177308-0d34-0410-b5e6-96231b3b80d8 Bruno Cardoso Lopes 5 years ago
6 changed file(s) with 346 addition(s) and 16 deletion(s). Raw diff Collapse all Expand all
9696 /// default, this is equal to CurrentFnSym.
9797 MCSymbol *CurrentFnSymForSize;
9898
99 /// Map global GOT equivalent MCSymbols to GlobalVariables and keep track of
100 /// its number of uses by other globals.
101 typedef std::pair GOTEquivUsePair;
102 DenseMap GlobalGOTEquivs;
103
99104 private:
100105 // The garbage collection metadata printer table.
101106 void *GCMetadataPrinters; // Really a DenseMap.
241246 /// \brief Print a general LLVM constant to the .s file.
242247 void EmitGlobalConstant(const Constant *CV);
243248
249 /// \brief Unnamed constant global variables solely contaning a pointer to
250 /// another globals variable act like a global variable "proxy", or GOT
251 /// equivalents, i.e., it's only used to hold the address of the latter. One
252 /// optimization is to replace accesses to these proxies by using the GOT
253 /// entry for the final global instead. Hence, we select GOT equivalent
254 /// candidates among all the module global variables, avoid emitting them
255 /// unnecessarily and finally replace references to them by pc relative
256 /// accesses to GOT entries.
257 void computeGlobalGOTEquivs(Module &M);
258
259 /// \brief Constant expressions using GOT equivalent globals may not be
260 /// eligible for PC relative GOT entry conversion, in such cases we need to
261 /// emit the proxies we previously omitted in EmitGlobalVariable.
262 void emitGlobalGOTEquivs();
263
244264 //===------------------------------------------------------------------===//
245265 // Overridable Hooks
246266 //===------------------------------------------------------------------===//
4040 const TargetLoweringObjectFile&) = delete;
4141 void operator=(const TargetLoweringObjectFile&) = delete;
4242
43 protected:
44 bool SupportIndirectSymViaGOTPCRel;
45
4346 public:
4447 MCContext &getContext() const { return *Ctx; }
4548
46 TargetLoweringObjectFile() : MCObjectFileInfo(), Ctx(nullptr), DL(nullptr) {}
49 TargetLoweringObjectFile() : MCObjectFileInfo(), Ctx(nullptr), DL(nullptr),
50 SupportIndirectSymViaGOTPCRel(false) {}
4751
4852 virtual ~TargetLoweringObjectFile();
4953
157161 return nullptr;
158162 }
159163
164 /// \brief Target supports replacing a data "PC"-relative access to a symbol
165 /// through another symbol, by accessing the later via a GOT entry instead?
166 bool supportIndirectSymViaGOTPCRel() const {
167 return SupportIndirectSymViaGOTPCRel;
168 }
169
170 /// \brief Get the target specific PC relative GOT entry relocation
171 virtual const MCExpr *getIndirectSymViaGOTPCRel(const MCSymbol *Sym,
172 int64_t Offset) const {
173 return nullptr;
174 }
175
160176 protected:
161177 virtual const MCSection *
162178 SelectSectionForGlobal(const GlobalValue *GV, SectionKind Kind,
4040 #include "llvm/MC/MCSection.h"
4141 #include "llvm/MC/MCStreamer.h"
4242 #include "llvm/MC/MCSymbol.h"
43 #include "llvm/MC/MCValue.h"
4344 #include "llvm/Support/ErrorHandling.h"
4445 #include "llvm/Support/Format.h"
4546 #include "llvm/Support/MathExtras.h"
337338 if (GV->hasInitializer()) {
338339 // Check to see if this is a special global used by LLVM, if so, emit it.
339340 if (EmitSpecialLLVMGlobal(GV))
341 return;
342
343 // Skip the emission of global equivalents. The symbol can be emitted later
344 // on by emitGlobalGOTEquivs in case it turns out to be needed.
345 if (GlobalGOTEquivs.count(getSymbol(GV)))
340346 return;
341347
342348 if (isVerbose()) {
889895 OutStreamer.AddBlankLine();
890896 }
891897
898 /// \brief Compute the number of Global Variables that uses a Constant.
899 static unsigned getNumGlobalVariableUses(const Constant *C) {
900 if (!C)
901 return 0;
902
903 if (isa(C))
904 return 1;
905
906 unsigned NumUses = 0;
907 for (auto *CU : C->users())
908 NumUses += getNumGlobalVariableUses(dyn_cast(CU));
909
910 return NumUses;
911 }
912
913 /// \brief Only consider global GOT equivalents if at least one user is a
914 /// cstexpr inside an initializer of another global variables. Also, don't
915 /// handle cstexpr inside instructions. During global variable emission,
916 /// candidates are skipped and are emitted later in case at least one cstexpr
917 /// isn't replaced by a PC relative GOT entry access.
918 static bool isGOTEquivalentCandidate(const GlobalVariable *GV,
919 unsigned &NumGOTEquivUsers) {
920 // Global GOT equivalents are unnamed private globals with a constant
921 // pointer initializer to another global symbol. They must point to a
922 // GlobalVariable or Function, i.e., as GlobalValue.
923 if (!GV->hasUnnamedAddr() || !GV->hasInitializer() || !GV->isConstant() ||
924 !GV->isDiscardableIfUnused() || !dyn_cast(GV->getOperand(0)))
925 return false;
926
927 // To be a got equivalent, at least one of its users need to be a constant
928 // expression used by another global variable.
929 for (auto *U : GV->users())
930 NumGOTEquivUsers += getNumGlobalVariableUses(cast(U));
931
932 return NumGOTEquivUsers > 0;
933 }
934
935 /// \brief Unnamed constant global variables solely contaning a pointer to
936 /// another globals variable is equivalent to a GOT table entry; it contains the
937 /// the address of another symbol. Optimize it and replace accesses to these
938 /// "GOT equivalents" by using the GOT entry for the final global instead.
939 /// Compute GOT equivalent candidates among all global variables to avoid
940 /// emitting them if possible later on, after it use is replaced by a GOT entry
941 /// access.
942 void AsmPrinter::computeGlobalGOTEquivs(Module &M) {
943 if (!getObjFileLowering().supportIndirectSymViaGOTPCRel())
944 return;
945
946 for (const auto &G : M.globals()) {
947 unsigned NumGOTEquivUsers = 0;
948 if (!isGOTEquivalentCandidate(&G, NumGOTEquivUsers))
949 continue;
950
951 const MCSymbol *GOTEquivSym = getSymbol(&G);
952 GlobalGOTEquivs[GOTEquivSym] = std::make_pair(&G, NumGOTEquivUsers);
953 }
954 }
955
956 /// \brief Constant expressions using GOT equivalent globals may not be eligible
957 /// for PC relative GOT entry conversion, in such cases we need to emit such
958 /// globals we previously omitted in EmitGlobalVariable.
959 void AsmPrinter::emitGlobalGOTEquivs() {
960 if (!getObjFileLowering().supportIndirectSymViaGOTPCRel())
961 return;
962
963 while (!GlobalGOTEquivs.empty()) {
964 DenseMap::iterator I =
965 GlobalGOTEquivs.begin();
966 const MCSymbol *S = I->first;
967 const GlobalVariable *GV = I->second.first;
968 GlobalGOTEquivs.erase(S);
969 EmitGlobalVariable(GV);
970 }
971 }
972
892973 bool AsmPrinter::doFinalization(Module &M) {
974 // Gather all GOT equivalent globals in the module. We really need two
975 // passes over the globals: one to compute and another to avoid its emission
976 // in EmitGlobalVariable, otherwise we would not be able to handle cases
977 // where the got equivalent shows up before its use.
978 computeGlobalGOTEquivs(M);
979
893980 // Emit global variables.
894981 for (const auto &G : M.globals())
895982 EmitGlobalVariable(&G);
983
984 // Emit remaining GOT equivalent globals.
985 emitGlobalGOTEquivs();
896986
897987 // Emit visibility info for declarations
898988 for (const Function &F : M) {
16781768 }
16791769 }
16801770
1681 static void emitGlobalConstantImpl(const Constant *C, AsmPrinter &AP);
1771 static void emitGlobalConstantImpl(const Constant *C, AsmPrinter &AP,
1772 const Constant *BaseCV = nullptr,
1773 uint64_t Offset = 0);
16821774
16831775 /// isRepeatedByteSequence - Determine whether the given value is
16841776 /// composed of a repeated sequence of identical bytes and return the
18071899
18081900 }
18091901
1810 static void emitGlobalConstantArray(const ConstantArray *CA, AsmPrinter &AP) {
1902 static void emitGlobalConstantArray(const ConstantArray *CA, AsmPrinter &AP,
1903 const Constant *BaseCV, uint64_t Offset) {
18111904 // See if we can aggregate some values. Make sure it can be
18121905 // represented as a series of bytes of the constant value.
18131906 int Value = isRepeatedByteSequence(CA, AP.TM);
1907 const DataLayout &DL = *AP.TM.getDataLayout();
18141908
18151909 if (Value != -1) {
1816 uint64_t Bytes =
1817 AP.TM.getDataLayout()->getTypeAllocSize(
1818 CA->getType());
1910 uint64_t Bytes = DL.getTypeAllocSize(CA->getType());
18191911 AP.OutStreamer.EmitFill(Bytes, Value);
18201912 }
18211913 else {
1822 for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i)
1823 emitGlobalConstantImpl(CA->getOperand(i), AP);
1914 for (unsigned i = 0, e = CA->getNumOperands(); i != e; ++i) {
1915 emitGlobalConstantImpl(CA->getOperand(i), AP, BaseCV, Offset);
1916 Offset += DL.getTypeAllocSize(CA->getOperand(i)->getType());
1917 }
18241918 }
18251919 }
18261920
18361930 AP.OutStreamer.EmitZeros(Padding);
18371931 }
18381932
1839 static void emitGlobalConstantStruct(const ConstantStruct *CS, AsmPrinter &AP) {
1933 static void emitGlobalConstantStruct(const ConstantStruct *CS, AsmPrinter &AP,
1934 const Constant *BaseCV, uint64_t Offset) {
18401935 // Print the fields in successive locations. Pad to align if needed!
18411936 const DataLayout *DL = AP.TM.getDataLayout();
18421937 unsigned Size = DL->getTypeAllocSize(CS->getType());
18451940 for (unsigned i = 0, e = CS->getNumOperands(); i != e; ++i) {
18461941 const Constant *Field = CS->getOperand(i);
18471942
1943 // Print the actual field value.
1944 emitGlobalConstantImpl(Field, AP, BaseCV, Offset+SizeSoFar);
1945
18481946 // Check if padding is needed and insert one or more 0s.
18491947 uint64_t FieldSize = DL->getTypeAllocSize(Field->getType());
18501948 uint64_t PadSize = ((i == e-1 ? Size : Layout->getElementOffset(i+1))
18511949 - Layout->getElementOffset(i)) - FieldSize;
18521950 SizeSoFar += FieldSize + PadSize;
1853
1854 // Now print the actual field value.
1855 emitGlobalConstantImpl(Field, AP);
18561951
18571952 // Insert padding - this may include padding to increase the size of the
18581953 // current field up to the ABI size (if the struct is not packed) as well
19692064 }
19702065 }
19712066
1972 static void emitGlobalConstantImpl(const Constant *CV, AsmPrinter &AP) {
2067 /// \brief Transform a not absolute MCExpr containing a reference to a GOT
2068 /// equivalent global, by a target specific GOT pc relative access to the
2069 /// final symbol.
2070 static void handleIndirectSymViaGOTPCRel(AsmPrinter &AP, const MCExpr **ME,
2071 const Constant *BaseCst,
2072 uint64_t Offset) {
2073 // The global @foo below illustrates a global that uses a got equivalent.
2074 //
2075 // @bar = global i32 42
2076 // @gotequiv = private unnamed_addr constant i32* @bar
2077 // @foo = i32 trunc (i64 sub (i64 ptrtoint (i32** @gotequiv to i64),
2078 // i64 ptrtoint (i32* @foo to i64))
2079 // to i32)
2080 //
2081 // The cstexpr in @foo is converted into the MCExpr `ME`, where we actually
2082 // check whether @foo is suitable to use a GOTPCREL. `ME` is usually in the
2083 // form:
2084 //
2085 // foo = cstexpr, where
2086 // cstexpr := - "." +
2087 // cstexpr := - ( - ) +
2088 //
2089 // After canonicalization by EvaluateAsRelocatable `ME` turns into:
2090 //
2091 // cstexpr := - + gotpcrelcst, where
2092 // gotpcrelcst := +
2093 //
2094 MCValue MV;
2095 if (!(*ME)->EvaluateAsRelocatable(MV, nullptr, nullptr) || MV.isAbsolute())
2096 return;
2097
2098 const MCSymbol *GOTEquivSym = &MV.getSymA()->getSymbol();
2099 if (!AP.GlobalGOTEquivs.count(GOTEquivSym))
2100 return;
2101
2102 const GlobalValue *BaseGV = dyn_cast(BaseCst);
2103 if (!BaseGV)
2104 return;
2105
2106 const MCSymbol *BaseSym = AP.getSymbol(BaseGV);
2107 if (BaseSym != &MV.getSymB()->getSymbol())
2108 return;
2109
2110 // Make sure to match:
2111 //
2112 // gotpcrelcst := +
2113 //
2114 int64_t GOTPCRelCst = Offset + MV.getConstant();
2115 if (GOTPCRelCst < 0)
2116 return;
2117
2118 // Emit the GOT PC relative to replace the got equivalent global, i.e.:
2119 //
2120 // bar:
2121 // .long 42
2122 // gotequiv:
2123 // .quad bar
2124 // foo:
2125 // .long gotequiv - "." +
2126 //
2127 // is replaced by the target specific equivalent to:
2128 //
2129 // bar:
2130 // .long 42
2131 // foo:
2132 // .long bar@GOTPCREL+
2133 //
2134 AsmPrinter::GOTEquivUsePair Result = AP.GlobalGOTEquivs[GOTEquivSym];
2135 const GlobalVariable *GV = Result.first;
2136 unsigned NumUses = Result.second;
2137 const GlobalValue *FinalGV = dyn_cast(GV->getOperand(0));
2138 const MCSymbol *FinalSym = AP.getSymbol(FinalGV);
2139 *ME = AP.getObjFileLowering().getIndirectSymViaGOTPCRel(FinalSym,
2140 GOTPCRelCst);
2141
2142 // Update GOT equivalent usage information
2143 --NumUses;
2144 if (NumUses)
2145 AP.GlobalGOTEquivs[GOTEquivSym] = std::make_pair(GV, NumUses);
2146 else
2147 AP.GlobalGOTEquivs.erase(GOTEquivSym);
2148 }
2149
2150 static void emitGlobalConstantImpl(const Constant *CV, AsmPrinter &AP,
2151 const Constant *BaseCV, uint64_t Offset) {
19732152 const DataLayout *DL = AP.TM.getDataLayout();
19742153 uint64_t Size = DL->getTypeAllocSize(CV->getType());
2154
2155 // Globals with sub-elements such as combinations of arrays and structs
2156 // are handled recursively by emitGlobalConstantImpl. Keep track of the
2157 // constant symbol base and the current position with BaseCV and Offset.
2158 if (!BaseCV && CV->hasOneUse())
2159 BaseCV = dyn_cast(CV->user_back());
2160
19752161 if (isa(CV) || isa(CV))
19762162 return AP.OutStreamer.EmitZeros(Size);
19772163
20042190 return emitGlobalConstantDataSequential(CDS, AP);
20052191
20062192 if (const ConstantArray *CVA = dyn_cast(CV))
2007 return emitGlobalConstantArray(CVA, AP);
2193 return emitGlobalConstantArray(CVA, AP, BaseCV, Offset);
20082194
20092195 if (const ConstantStruct *CVS = dyn_cast(CV))
2010 return emitGlobalConstantStruct(CVS, AP);
2196 return emitGlobalConstantStruct(CVS, AP, BaseCV, Offset);
20112197
20122198 if (const ConstantExpr *CE = dyn_cast(CV)) {
20132199 // Look through bitcasts, which might not be able to be MCExpr'ized (e.g. of
20302216
20312217 // Otherwise, it must be a ConstantExpr. Lower it to an MCExpr, then emit it
20322218 // thread the streamer with EmitValue.
2033 AP.OutStreamer.EmitValue(AP.lowerConstant(CV), Size);
2219 const MCExpr *ME = AP.lowerConstant(CV);
2220
2221 // Since lowerConstant already folded and got rid of all IR pointer and
2222 // integer casts, detect GOT equivalent accesses by looking into the MCExpr
2223 // directly.
2224 if (AP.getObjFileLowering().supportIndirectSymViaGOTPCRel())
2225 handleIndirectSymViaGOTPCRel(AP, &ME, BaseCV, Offset);
2226
2227 AP.OutStreamer.EmitValue(ME, Size);
20342228 }
20352229
20362230 /// EmitGlobalConstant - Print a general LLVM constant to the .s file.
1919
2020 using namespace llvm;
2121 using namespace dwarf;
22
23 X86_64MachoTargetObjectFile::X86_64MachoTargetObjectFile()
24 : TargetLoweringObjectFileMachO() {
25 SupportIndirectSymViaGOTPCRel = true;
26 }
2227
2328 const MCExpr *X86_64MachoTargetObjectFile::getTTypeGlobalReference(
2429 const GlobalValue *GV, unsigned Encoding, Mangler &Mang,
4348 const GlobalValue *GV, Mangler &Mang, const TargetMachine &TM,
4449 MachineModuleInfo *MMI) const {
4550 return TM.getSymbol(GV, Mang);
51 }
52
53 const MCExpr *X86_64MachoTargetObjectFile::getIndirectSymViaGOTPCRel(
54 const MCSymbol *Sym, int64_t Offset) const {
55 // On Darwin/X86-64, we need to use foo@GOTPCREL+4 to access the got entry
56 // from a data section. In case there's an additional offset, then use
57 // foo@GOTPCREL+4+.
58 const MCExpr *Res =
59 MCSymbolRefExpr::Create(Sym, MCSymbolRefExpr::VK_GOTPCREL, getContext());
60 const MCExpr *Off = MCConstantExpr::Create(Offset+4, getContext());
61 return MCBinaryExpr::CreateAdd(Res, Off, getContext());
4662 }
4763
4864 void
1818 /// x86-64.
1919 class X86_64MachoTargetObjectFile : public TargetLoweringObjectFileMachO {
2020 public:
21 X86_64MachoTargetObjectFile();
22
2123 const MCExpr *
2224 getTTypeGlobalReference(const GlobalValue *GV, unsigned Encoding,
2325 Mangler &Mang, const TargetMachine &TM,
2931 MCSymbol *getCFIPersonalitySymbol(const GlobalValue *GV, Mangler &Mang,
3032 const TargetMachine &TM,
3133 MachineModuleInfo *MMI) const override;
34
35 const MCExpr *
36 getIndirectSymViaGOTPCRel(const MCSymbol *Sym,
37 int64_t Offset) const override;
3238 };
3339
3440 /// X86LinuxTargetObjectFile - This implementation is used for linux x86
0 ; RUN: llc -mtriple=x86_64-apple-darwin %s -o %t
1 ; RUN: FileCheck %s < %t
2 ; RUN: FileCheck %s -check-prefix=GOT-EQUIV < %t
3
4 ; GOT equivalent globals references can be replaced by the GOT entry of the
5 ; final symbol instead.
6
7 %struct.data = type { i32, %struct.anon }
8 %struct.anon = type { i32, i32 }
9
10 ; Check that these got equivalent symbols are never emitted or used
11 ; GOT-EQUIV-NOT: _localgotequiv
12 ; GOT-EQUIV-NOT: _extgotequiv
13 @localfoo = global i32 42
14 @localgotequiv = private unnamed_addr constant i32* @localfoo
15
16 @extfoo = external global i32
17 @extgotequiv = private unnamed_addr constant i32* @extfoo
18
19 ; Don't replace GOT equivalent usage within instructions and emit the GOT
20 ; equivalent since it can't be replaced by the GOT entry. @bargotequiv is
21 ; used by an instruction inside @t0.
22 ;
23 ; CHECK: l_bargotequiv:
24 ; CHECK-NEXT: .quad _extbar
25 @extbar = external global i32
26 @bargotequiv = private unnamed_addr constant i32* @extbar
27
28 @table = global [4 x %struct.data] [
29 ; CHECK-LABEL: _table
30 %struct.data { i32 1, %struct.anon { i32 2, i32 3 } },
31 ; Test GOT equivalent usage inside nested constant arrays.
32 ; CHECK: .long 5
33 ; CHECK-NOT: .long _localgotequiv-(_table+20)
34 ; CHECK-NEXT: .long _localfoo@GOTPCREL+4
35 %struct.data { i32 4, %struct.anon { i32 5,
36 i32 trunc (i64 sub (i64 ptrtoint (i32** @localgotequiv to i64),
37 i64 ptrtoint (i32* getelementptr inbounds ([4 x %struct.data]* @table, i32 0, i64 1, i32 1, i32 1) to i64))
38 to i32)}
39 },
40 ; CHECK: .long 5
41 ; CHECK-NOT: _extgotequiv-(_table+32)
42 ; CHECK-NEXT: .long _extfoo@GOTPCREL+4
43 %struct.data { i32 4, %struct.anon { i32 5,
44 i32 trunc (i64 sub (i64 ptrtoint (i32** @extgotequiv to i64),
45 i64 ptrtoint (i32* getelementptr inbounds ([4 x %struct.data]* @table, i32 0, i64 2, i32 1, i32 1) to i64))
46 to i32)}
47 },
48 ; Test support for arbitrary constants into the GOTPCREL offset
49 ; CHECK: .long 5
50 ; CHECK-NOT: _extgotequiv-(_table+44)
51 ; CHECK-NEXT: .long _extfoo@GOTPCREL+28
52 %struct.data { i32 4, %struct.anon { i32 5,
53 i32 add (i32 trunc (i64 sub (i64 ptrtoint (i32** @extgotequiv to i64),
54 i64 ptrtoint (i32* getelementptr inbounds ([4 x %struct.data]* @table, i32 0, i64 3, i32 1, i32 1) to i64))
55 to i32), i32 24)}
56 }
57 ], align 16
58
59 ; Test multiple uses of GOT equivalents.
60 ; CHECK-LABEL: _delta
61 ; CHECK: .long _extfoo@GOTPCREL+4
62 @delta = global i32 trunc (i64 sub (i64 ptrtoint (i32** @extgotequiv to i64),
63 i64 ptrtoint (i32* @delta to i64))
64 to i32)
65
66 ; CHECK-LABEL: _deltaplus:
67 ; CHECK: .long _localfoo@GOTPCREL+59
68 @deltaplus = global i32 add (i32 trunc (i64 sub (i64 ptrtoint (i32** @localgotequiv to i64),
69 i64 ptrtoint (i32* @deltaplus to i64))
70 to i32), i32 55)
71
72 define i32 @t0(i32 %a) {
73 %x = add i32 trunc (i64 sub (i64 ptrtoint (i32** @bargotequiv to i64),
74 i64 ptrtoint (i32 (i32)* @t0 to i64))
75 to i32), %a
76 ret i32 %x
77 }