1 | /** |
2 | * Copyright (c) Glow Contributors. See CONTRIBUTORS file. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include "glow/LLVMIRCodeGen/LLVMBackend.h" |
18 | #include "glow/LLVMIRCodeGen/LLVMIRGen.h" |
19 | |
20 | #include "llvm/ADT/DenseMap.h" |
21 | #include "llvm/ADT/SmallVector.h" |
22 | #include "llvm/Analysis/BasicAliasAnalysis.h" |
23 | #include "llvm/Analysis/CFLAndersAliasAnalysis.h" |
24 | #include "llvm/Analysis/CFLSteensAliasAnalysis.h" |
25 | #include "llvm/Analysis/GlobalsModRef.h" |
26 | #include "llvm/Analysis/InlineCost.h" |
27 | #include "llvm/Analysis/Passes.h" |
28 | #include "llvm/Analysis/ScopedNoAliasAA.h" |
29 | #include "llvm/Analysis/TargetLibraryInfo.h" |
30 | #include "llvm/Analysis/TargetTransformInfo.h" |
31 | #include "llvm/Analysis/TypeBasedAliasAnalysis.h" |
32 | #include "llvm/IR/Constants.h" |
33 | #include "llvm/IR/DataLayout.h" |
34 | #include "llvm/IR/LegacyPassManager.h" |
35 | #include "llvm/IR/Module.h" |
36 | #include "llvm/IR/Verifier.h" |
37 | #include "llvm/Support/Casting.h" |
38 | #include "llvm/Support/CommandLine.h" |
39 | #include "llvm/Support/ManagedStatic.h" |
40 | #include "llvm/Support/TargetSelect.h" |
41 | #include "llvm/Transforms/IPO/ForceFunctionAttrs.h" |
42 | #include "llvm/Transforms/IPO/FunctionAttrs.h" |
43 | #include "llvm/Transforms/IPO/InferFunctionAttrs.h" |
44 | #include "llvm/Transforms/IPO/Internalize.h" |
45 | #include "llvm/Transforms/Instrumentation.h" |
46 | #include "llvm/Transforms/Scalar.h" |
47 | #include "llvm/Transforms/Scalar/GVN.h" |
48 | #include "llvm/Transforms/Scalar/SimpleLoopUnswitch.h" |
49 | #include "llvm/Transforms/Vectorize.h" |
50 | |
51 | using namespace glow; |
52 | using llvm::dyn_cast; |
53 | using llvm::isa; |
54 | |
55 | bool LLVMIRGen::preserveSymbol(const llvm::GlobalValue &GV) { |
56 | auto name = GV.getName(); |
57 | // Do not preserve any internal symbols, which typically have no name or |
58 | // start with libjit_. |
59 | if (name.empty() || name.startswith("libjit_" )) |
60 | return false; |
61 | return true; |
62 | } |
63 | |
64 | llvm::Attribute::AttrKind |
65 | LLVMIRGen::getInlinineAttr(const llvm::Function *F) const { |
66 | return llvm::Attribute::AttrKind::None; |
67 | } |
68 | |
69 | void LLVMIRGen::populatePassManagerBuilderOptions( |
70 | llvm::PassManagerBuilder &PMB) { |
71 | PMB.OptLevel = 2; |
72 | PMB.SizeLevel = 0; |
73 | PMB.LoopVectorize = true; |
74 | PMB.SLPVectorize = false; |
75 | PMB.Inliner = llvm::createFunctionInliningPass(); |
76 | } |
77 | |
78 | void LLVMIRGen::updateInlineAttributes(llvm::Module *M) { |
79 | for (auto &FF : *M) { |
80 | if (FF.isDeclaration()) { |
81 | continue; |
82 | } |
83 | // Check for no-inline attribute. |
84 | bool dontInline = FF.hasFnAttribute(llvm::Attribute::AttrKind::NoInline); |
85 | bool alwaysInline = |
86 | FF.hasFnAttribute(llvm::Attribute::AttrKind::AlwaysInline); |
87 | bool optnone = FF.hasFnAttribute(llvm::Attribute::AttrKind::OptimizeNone); |
88 | |
89 | bool hasOmitFramePointer = FF.hasFnAttribute("omit-frame-pointer" ); |
90 | llvm::Attribute omitFramePointerAttr; |
91 | if (hasOmitFramePointer) { |
92 | omitFramePointerAttr = FF.getFnAttribute("omit-frame-pointer" ); |
93 | } |
94 | |
95 | bool hasFramePointer = FF.hasFnAttribute("frame-pointer" ); |
96 | llvm::Attribute framePointerAttr; |
97 | if (hasFramePointer) { |
98 | framePointerAttr = FF.getFnAttribute("frame-pointer" ); |
99 | } |
100 | |
101 | bool hasNoFramePointerElim = FF.hasFnAttribute("no-frame-pointer-elim" ); |
102 | llvm::Attribute noFramePointerElimAttr; |
103 | if (hasNoFramePointerElim) { |
104 | noFramePointerElimAttr = FF.getFnAttribute("no-frame-pointer-elim" ); |
105 | } |
106 | |
107 | auto inlineAttr = getInlinineAttr(&FF); |
108 | if (inlineAttr != llvm::Attribute::AttrKind::None) { |
109 | DCHECK(inlineAttr == llvm::Attribute::AttrKind::AlwaysInline || |
110 | inlineAttr == llvm::Attribute::AttrKind::NoInline) |
111 | << "Unknown inlining attribute returned by getInlinineAttr" ; |
112 | dontInline = (inlineAttr == llvm::Attribute::AttrKind::NoInline); |
113 | } |
114 | // Replace the target-specific machine code function attributes that were |
115 | // attached by the frontend. Keep return and parameter attributes, e.g., |
116 | // noalias. |
117 | FF.setAttributes(FF.getAttributes().removeAttributes( |
118 | M->getContext(), llvm::AttributeList::FunctionIndex)); |
119 | if (hasOmitFramePointer) { |
120 | FF.addFnAttr("omit-frame-pointer" , |
121 | omitFramePointerAttr.getValueAsString()); |
122 | } |
123 | if (hasFramePointer) { |
124 | FF.addFnAttr("frame-pointer" , framePointerAttr.getValueAsString()); |
125 | } |
126 | if (hasNoFramePointerElim) { |
127 | FF.addFnAttr("no-frame-pointer-elim" , |
128 | noFramePointerElimAttr.getValueAsString()); |
129 | } |
130 | // Force inline all non-no-inline functions. |
131 | if (!dontInline || alwaysInline) { |
132 | FF.addFnAttr(llvm::Attribute::AttrKind::AlwaysInline); |
133 | continue; |
134 | } |
135 | if (dontInline || optnone) { |
136 | FF.addFnAttr(llvm::Attribute::AttrKind::NoInline); |
137 | continue; |
138 | } |
139 | } |
140 | } |
141 | |
142 | void LLVMIRGen::optimizeLLVMModule(llvm::Module *M, llvm::TargetMachine &TM) { |
143 | // Make all of the definitions from libjit and unnamed symbols internal and |
144 | // optimizable. Everything else should be preserved as is. |
145 | auto preserveSymbolCallback = [&](const llvm::GlobalValue &GV) -> bool { |
146 | // Do not internalize declarations. |
147 | if (GV.isDeclaration()) { |
148 | return true; |
149 | } |
150 | return preserveSymbol(GV); |
151 | }; |
152 | |
153 | // Internalize functions in the module using a backend-specific logic. |
154 | // Typically only the entry point would be preserved. |
155 | llvm::internalizeModule(*M, preserveSymbolCallback); |
156 | |
157 | // Next, we remove all of the 'no-inline' attributes that clang in -O0 adds to |
158 | // all functions. |
159 | for (auto &FF : *M) { |
160 | // For libjit functions that are marked as dllimport or dllexport |
161 | // This sets them as regular functions. |
162 | // This allows LLVM to eliminate unused functions, |
163 | // and speeds up compilation. |
164 | if (FF.getDLLStorageClass() != |
165 | llvm::GlobalValue::DLLStorageClassTypes::DefaultStorageClass) { |
166 | FF.setLinkage(llvm::GlobalValue::LinkageTypes::InternalLinkage); |
167 | FF.setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass); |
168 | } |
169 | |
170 | // Remove NoInline attribute. |
171 | FF.removeFnAttr(llvm::Attribute::AttrKind::NoInline); |
172 | |
173 | // LinkOnce linkage seems to cause problems to OrcJIT on some OS platforms. |
174 | // In particular, ORCJit doesn't like linkonce_odr linkage which is used for |
175 | // almost all templatized C++ functions in the LLVM module. |
176 | if (!FF.isDeclaration() && FF.isLinkOnceLinkage(FF.getLinkage())) { |
177 | FF.setLinkage(llvm::GlobalValue::LinkageTypes::InternalLinkage); |
178 | } |
179 | } |
180 | |
181 | // Perform specialization of functions for constant arguments before anything |
182 | // else. |
183 | performSpecialization(); |
184 | |
185 | // Add instrumentation into the code for better debugging experience. |
186 | performDebugInstrumentation(); |
187 | |
188 | M->setTargetTriple(TM.getTargetTriple().normalize()); |
189 | M->setDataLayout(TM.createDataLayout()); |
190 | |
191 | // Properly set inline attributes. |
192 | updateInlineAttributes(M); |
193 | |
194 | // Add no-frame-pointer-elim=true attribute. It helps with profiling and |
195 | // debugging the produced code. |
196 | for (auto &FF : *M) { |
197 | if (FF.isDeclaration()) { |
198 | continue; |
199 | } |
200 | if (FF.hasFnAttribute("no-frame-pointer-elim" ) || |
201 | FF.hasFnAttribute("frame-pointer" ) || |
202 | FF.hasFnAttribute("omit-frame-pointer" )) { |
203 | continue; |
204 | } |
205 | FF.addFnAttr("no-frame-pointer-elim" , "true" ); |
206 | } |
207 | |
208 | // The "main" function is parameterized by the base addresses of memory areas |
209 | // and it is always invoked from either the "jitmain" function or the AOT |
210 | // entry point. To enable better LLVM optimizations "main" should always be |
211 | // inlined. |
212 | getLLVMFunction()->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline); |
213 | |
214 | // Add an appropriate TargetLibraryInfo pass for the module's triple. |
215 | llvm::TargetLibraryInfoImpl TLII(llvm::Triple(M->getTargetTriple())); |
216 | // Disable optimizations of some builtin functions. They cause issues on some |
217 | // targets. |
218 | llvm::LibFunc libFunc; |
219 | if (TLII.getLibFunc(llvm::StringRef("printf" ), libFunc)) { |
220 | TLII.setUnavailable(libFunc); |
221 | } |
222 | |
223 | auto *TLIWP = new llvm::TargetLibraryInfoWrapperPass(TLII); |
224 | |
225 | llvm::legacy::FunctionPassManager FPM(M); |
226 | llvm::legacy::PassManager PM; |
227 | llvm::PassManagerBuilder PMB; |
228 | populatePassManagerBuilderOptions(PMB); |
229 | |
230 | PM.add(TLIWP); |
231 | |
232 | // Add internal analysis passes from the target machine. |
233 | PM.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis())); |
234 | FPM.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis())); |
235 | |
236 | PMB.populateFunctionPassManager(FPM); |
237 | PMB.populateModulePassManager(PM); |
238 | |
239 | FPM.doInitialization(); |
240 | PM.run(*M); |
241 | for (auto &FF : *M) { |
242 | FPM.run(FF); |
243 | } |
244 | FPM.doFinalization(); |
245 | PM.run(*M); |
246 | } |
247 | |