1 //===- WebAssemblyTargetMachine.cpp - Define TargetMachine for WebAssembly -==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file defines the WebAssembly-specific subclass of TargetMachine.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "WebAssemblyTargetMachine.h"
15 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16 #include "TargetInfo/WebAssemblyTargetInfo.h"
17 #include "WebAssembly.h"
18 #include "WebAssemblyISelLowering.h"
19 #include "WebAssemblyMachineFunctionInfo.h"
20 #include "WebAssemblyTargetObjectFile.h"
21 #include "WebAssemblyTargetTransformInfo.h"
22 #include "WebAssemblyUtilities.h"
23 #include "llvm/CodeGen/MIRParser/MIParser.h"
24 #include "llvm/CodeGen/MachineFunctionPass.h"
25 #include "llvm/CodeGen/Passes.h"
26 #include "llvm/CodeGen/RegAllocRegistry.h"
27 #include "llvm/CodeGen/TargetPassConfig.h"
28 #include "llvm/IR/Function.h"
29 #include "llvm/InitializePasses.h"
30 #include "llvm/MC/MCAsmInfo.h"
31 #include "llvm/MC/TargetRegistry.h"
32 #include "llvm/Target/TargetOptions.h"
33 #include "llvm/Transforms/Scalar.h"
34 #include "llvm/Transforms/Scalar/LowerAtomicPass.h"
35 #include "llvm/Transforms/Utils.h"
36 #include <optional>
37 using namespace llvm;
38
39 #define DEBUG_TYPE "wasm"
40
41 // A command-line option to keep implicit locals
42 // for the purpose of testing with lit/llc ONLY.
43 // This produces output which is not valid WebAssembly, and is not supported
44 // by assemblers/disassemblers and other MC based tools.
45 static cl::opt<bool> WasmDisableExplicitLocals(
46 "wasm-disable-explicit-locals", cl::Hidden,
47 cl::desc("WebAssembly: output implicit locals in"
48 " instruction output for test purposes only."),
49 cl::init(false));
50
51 static cl::opt<bool> WasmDisableFixIrreducibleControlFlowPass(
52 "wasm-disable-fix-irreducible-control-flow-pass", cl::Hidden,
53 cl::desc("webassembly: disables the fix "
54 " irreducible control flow optimization pass"),
55 cl::init(false));
56
LLVMInitializeWebAssemblyTarget()57 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeWebAssemblyTarget() {
58 // Register the target.
59 RegisterTargetMachine<WebAssemblyTargetMachine> X(
60 getTheWebAssemblyTarget32());
61 RegisterTargetMachine<WebAssemblyTargetMachine> Y(
62 getTheWebAssemblyTarget64());
63
64 // Register backend passes
65 auto &PR = *PassRegistry::getPassRegistry();
66 initializeWebAssemblyAddMissingPrototypesPass(PR);
67 initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR);
68 initializeLowerGlobalDtorsLegacyPassPass(PR);
69 initializeFixFunctionBitcastsPass(PR);
70 initializeOptimizeReturnedPass(PR);
71 initializeWebAssemblyRefTypeMem2LocalPass(PR);
72 initializeWebAssemblyArgumentMovePass(PR);
73 initializeWebAssemblySetP2AlignOperandsPass(PR);
74 initializeWebAssemblyReplacePhysRegsPass(PR);
75 initializeWebAssemblyOptimizeLiveIntervalsPass(PR);
76 initializeWebAssemblyMemIntrinsicResultsPass(PR);
77 initializeWebAssemblyRegStackifyPass(PR);
78 initializeWebAssemblyRegColoringPass(PR);
79 initializeWebAssemblyNullifyDebugValueListsPass(PR);
80 initializeWebAssemblyFixIrreducibleControlFlowPass(PR);
81 initializeWebAssemblyLateEHPreparePass(PR);
82 initializeWebAssemblyExceptionInfoPass(PR);
83 initializeWebAssemblyCFGSortPass(PR);
84 initializeWebAssemblyCFGStackifyPass(PR);
85 initializeWebAssemblyExplicitLocalsPass(PR);
86 initializeWebAssemblyLowerBrUnlessPass(PR);
87 initializeWebAssemblyRegNumberingPass(PR);
88 initializeWebAssemblyDebugFixupPass(PR);
89 initializeWebAssemblyPeepholePass(PR);
90 initializeWebAssemblyMCLowerPrePassPass(PR);
91 initializeWebAssemblyLowerRefTypesIntPtrConvPass(PR);
92 initializeWebAssemblyFixBrTableDefaultsPass(PR);
93 initializeWebAssemblyDAGToDAGISelLegacyPass(PR);
94 }
95
96 //===----------------------------------------------------------------------===//
97 // WebAssembly Lowering public interface.
98 //===----------------------------------------------------------------------===//
99
getEffectiveRelocModel(std::optional<Reloc::Model> RM,const Triple & TT)100 static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM,
101 const Triple &TT) {
102 if (!RM) {
103 // Default to static relocation model. This should always be more optimial
104 // than PIC since the static linker can determine all global addresses and
105 // assume direct function calls.
106 return Reloc::Static;
107 }
108
109 return *RM;
110 }
111
112 /// Create an WebAssembly architecture model.
113 ///
WebAssemblyTargetMachine(const Target & T,const Triple & TT,StringRef CPU,StringRef FS,const TargetOptions & Options,std::optional<Reloc::Model> RM,std::optional<CodeModel::Model> CM,CodeGenOptLevel OL,bool JIT)114 WebAssemblyTargetMachine::WebAssemblyTargetMachine(
115 const Target &T, const Triple &TT, StringRef CPU, StringRef FS,
116 const TargetOptions &Options, std::optional<Reloc::Model> RM,
117 std::optional<CodeModel::Model> CM, CodeGenOptLevel OL, bool JIT)
118 : LLVMTargetMachine(
119 T,
120 TT.isArch64Bit()
121 ? (TT.isOSEmscripten() ? "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-"
122 "f128:64-n32:64-S128-ni:1:10:20"
123 : "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-"
124 "n32:64-S128-ni:1:10:20")
125 : (TT.isOSEmscripten() ? "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-"
126 "f128:64-n32:64-S128-ni:1:10:20"
127 : "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-"
128 "n32:64-S128-ni:1:10:20"),
129 TT, CPU, FS, Options, getEffectiveRelocModel(RM, TT),
130 getEffectiveCodeModel(CM, CodeModel::Large), OL),
131 TLOF(new WebAssemblyTargetObjectFile()),
132 UsesMultivalueABI(Options.MCOptions.getABIName() == "experimental-mv") {
133 // WebAssembly type-checks instructions, but a noreturn function with a return
134 // type that doesn't match the context will cause a check failure. So we lower
135 // LLVM 'unreachable' to ISD::TRAP and then lower that to WebAssembly's
136 // 'unreachable' instructions which is meant for that case.
137 this->Options.TrapUnreachable = true;
138 this->Options.NoTrapAfterNoreturn = false;
139
140 // WebAssembly treats each function as an independent unit. Force
141 // -ffunction-sections, effectively, so that we can emit them independently.
142 this->Options.FunctionSections = true;
143 this->Options.DataSections = true;
144 this->Options.UniqueSectionNames = true;
145
146 initAsmInfo();
147
148 // Note that we don't use setRequiresStructuredCFG(true). It disables
149 // optimizations than we're ok with, and want, such as critical edge
150 // splitting and tail merging.
151 }
152
153 WebAssemblyTargetMachine::~WebAssemblyTargetMachine() = default; // anchor.
154
getSubtargetImpl() const155 const WebAssemblySubtarget *WebAssemblyTargetMachine::getSubtargetImpl() const {
156 return getSubtargetImpl(std::string(getTargetCPU()),
157 std::string(getTargetFeatureString()));
158 }
159
160 const WebAssemblySubtarget *
getSubtargetImpl(std::string CPU,std::string FS) const161 WebAssemblyTargetMachine::getSubtargetImpl(std::string CPU,
162 std::string FS) const {
163 auto &I = SubtargetMap[CPU + FS];
164 if (!I) {
165 I = std::make_unique<WebAssemblySubtarget>(TargetTriple, CPU, FS, *this);
166 }
167 return I.get();
168 }
169
170 const WebAssemblySubtarget *
getSubtargetImpl(const Function & F) const171 WebAssemblyTargetMachine::getSubtargetImpl(const Function &F) const {
172 Attribute CPUAttr = F.getFnAttribute("target-cpu");
173 Attribute FSAttr = F.getFnAttribute("target-features");
174
175 std::string CPU =
176 CPUAttr.isValid() ? CPUAttr.getValueAsString().str() : TargetCPU;
177 std::string FS =
178 FSAttr.isValid() ? FSAttr.getValueAsString().str() : TargetFS;
179
180 // This needs to be done before we create a new subtarget since any
181 // creation will depend on the TM and the code generation flags on the
182 // function that reside in TargetOptions.
183 resetTargetOptions(F);
184
185 return getSubtargetImpl(CPU, FS);
186 }
187
188 namespace {
189
190 class CoalesceFeaturesAndStripAtomics final : public ModulePass {
191 // Take the union of all features used in the module and use it for each
192 // function individually, since having multiple feature sets in one module
193 // currently does not make sense for WebAssembly. If atomics are not enabled,
194 // also strip atomic operations and thread local storage.
195 static char ID;
196 WebAssemblyTargetMachine *WasmTM;
197
198 public:
CoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine * WasmTM)199 CoalesceFeaturesAndStripAtomics(WebAssemblyTargetMachine *WasmTM)
200 : ModulePass(ID), WasmTM(WasmTM) {}
201
runOnModule(Module & M)202 bool runOnModule(Module &M) override {
203 FeatureBitset Features = coalesceFeatures(M);
204
205 std::string FeatureStr = getFeatureString(Features);
206 WasmTM->setTargetFeatureString(FeatureStr);
207 for (auto &F : M)
208 replaceFeatures(F, FeatureStr);
209
210 bool StrippedAtomics = false;
211 bool StrippedTLS = false;
212
213 if (!Features[WebAssembly::FeatureAtomics]) {
214 StrippedAtomics = stripAtomics(M);
215 StrippedTLS = stripThreadLocals(M);
216 } else if (!Features[WebAssembly::FeatureBulkMemory]) {
217 StrippedTLS |= stripThreadLocals(M);
218 }
219
220 if (StrippedAtomics && !StrippedTLS)
221 stripThreadLocals(M);
222 else if (StrippedTLS && !StrippedAtomics)
223 stripAtomics(M);
224
225 recordFeatures(M, Features, StrippedAtomics || StrippedTLS);
226
227 // Conservatively assume we have made some change
228 return true;
229 }
230
231 private:
coalesceFeatures(const Module & M)232 FeatureBitset coalesceFeatures(const Module &M) {
233 FeatureBitset Features =
234 WasmTM
235 ->getSubtargetImpl(std::string(WasmTM->getTargetCPU()),
236 std::string(WasmTM->getTargetFeatureString()))
237 ->getFeatureBits();
238 for (auto &F : M)
239 Features |= WasmTM->getSubtargetImpl(F)->getFeatureBits();
240 return Features;
241 }
242
getFeatureString(const FeatureBitset & Features)243 static std::string getFeatureString(const FeatureBitset &Features) {
244 std::string Ret;
245 for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
246 if (Features[KV.Value])
247 Ret += (StringRef("+") + KV.Key + ",").str();
248 else
249 Ret += (StringRef("-") + KV.Key + ",").str();
250 }
251 return Ret;
252 }
253
replaceFeatures(Function & F,const std::string & Features)254 void replaceFeatures(Function &F, const std::string &Features) {
255 F.removeFnAttr("target-features");
256 F.removeFnAttr("target-cpu");
257 F.addFnAttr("target-features", Features);
258 }
259
stripAtomics(Module & M)260 bool stripAtomics(Module &M) {
261 // Detect whether any atomics will be lowered, since there is no way to tell
262 // whether the LowerAtomic pass lowers e.g. stores.
263 bool Stripped = false;
264 for (auto &F : M) {
265 for (auto &B : F) {
266 for (auto &I : B) {
267 if (I.isAtomic()) {
268 Stripped = true;
269 goto done;
270 }
271 }
272 }
273 }
274
275 done:
276 if (!Stripped)
277 return false;
278
279 LowerAtomicPass Lowerer;
280 FunctionAnalysisManager FAM;
281 for (auto &F : M)
282 Lowerer.run(F, FAM);
283
284 return true;
285 }
286
stripThreadLocals(Module & M)287 bool stripThreadLocals(Module &M) {
288 bool Stripped = false;
289 for (auto &GV : M.globals()) {
290 if (GV.isThreadLocal()) {
291 // replace `@llvm.threadlocal.address.pX(GV)` with `GV`.
292 for (Use &U : make_early_inc_range(GV.uses())) {
293 if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(U.getUser())) {
294 if (II->getIntrinsicID() == Intrinsic::threadlocal_address &&
295 II->getArgOperand(0) == &GV) {
296 II->replaceAllUsesWith(&GV);
297 II->eraseFromParent();
298 }
299 }
300 }
301
302 Stripped = true;
303 GV.setThreadLocal(false);
304 }
305 }
306 return Stripped;
307 }
308
recordFeatures(Module & M,const FeatureBitset & Features,bool Stripped)309 void recordFeatures(Module &M, const FeatureBitset &Features, bool Stripped) {
310 for (const SubtargetFeatureKV &KV : WebAssemblyFeatureKV) {
311 if (Features[KV.Value]) {
312 // Mark features as used
313 std::string MDKey = (StringRef("wasm-feature-") + KV.Key).str();
314 M.addModuleFlag(Module::ModFlagBehavior::Error, MDKey,
315 wasm::WASM_FEATURE_PREFIX_USED);
316 }
317 }
318 // Code compiled without atomics or bulk-memory may have had its atomics or
319 // thread-local data lowered to nonatomic operations or non-thread-local
320 // data. In that case, we mark the pseudo-feature "shared-mem" as disallowed
321 // to tell the linker that it would be unsafe to allow this code ot be used
322 // in a module with shared memory.
323 if (Stripped) {
324 M.addModuleFlag(Module::ModFlagBehavior::Error, "wasm-feature-shared-mem",
325 wasm::WASM_FEATURE_PREFIX_DISALLOWED);
326 }
327 }
328 };
329 char CoalesceFeaturesAndStripAtomics::ID = 0;
330
331 /// WebAssembly Code Generator Pass Configuration Options.
332 class WebAssemblyPassConfig final : public TargetPassConfig {
333 public:
WebAssemblyPassConfig(WebAssemblyTargetMachine & TM,PassManagerBase & PM)334 WebAssemblyPassConfig(WebAssemblyTargetMachine &TM, PassManagerBase &PM)
335 : TargetPassConfig(TM, PM) {}
336
getWebAssemblyTargetMachine() const337 WebAssemblyTargetMachine &getWebAssemblyTargetMachine() const {
338 return getTM<WebAssemblyTargetMachine>();
339 }
340
341 FunctionPass *createTargetRegisterAllocator(bool) override;
342
343 void addIRPasses() override;
344 void addISelPrepare() override;
345 bool addInstSelector() override;
346 void addOptimizedRegAlloc() override;
347 void addPostRegAlloc() override;
addGCPasses()348 bool addGCPasses() override { return false; }
349 void addPreEmitPass() override;
350 bool addPreISel() override;
351
352 // No reg alloc
addRegAssignAndRewriteFast()353 bool addRegAssignAndRewriteFast() override { return false; }
354
355 // No reg alloc
addRegAssignAndRewriteOptimized()356 bool addRegAssignAndRewriteOptimized() override { return false; }
357 };
358 } // end anonymous namespace
359
createMachineFunctionInfo(BumpPtrAllocator & Allocator,const Function & F,const TargetSubtargetInfo * STI) const360 MachineFunctionInfo *WebAssemblyTargetMachine::createMachineFunctionInfo(
361 BumpPtrAllocator &Allocator, const Function &F,
362 const TargetSubtargetInfo *STI) const {
363 return WebAssemblyFunctionInfo::create<WebAssemblyFunctionInfo>(Allocator, F,
364 STI);
365 }
366
367 TargetTransformInfo
getTargetTransformInfo(const Function & F) const368 WebAssemblyTargetMachine::getTargetTransformInfo(const Function &F) const {
369 return TargetTransformInfo(WebAssemblyTTIImpl(this, F));
370 }
371
372 TargetPassConfig *
createPassConfig(PassManagerBase & PM)373 WebAssemblyTargetMachine::createPassConfig(PassManagerBase &PM) {
374 return new WebAssemblyPassConfig(*this, PM);
375 }
376
createTargetRegisterAllocator(bool)377 FunctionPass *WebAssemblyPassConfig::createTargetRegisterAllocator(bool) {
378 return nullptr; // No reg alloc
379 }
380
381 using WebAssembly::WasmEnableEH;
382 using WebAssembly::WasmEnableEmEH;
383 using WebAssembly::WasmEnableEmSjLj;
384 using WebAssembly::WasmEnableExnref;
385 using WebAssembly::WasmEnableSjLj;
386
basicCheckForEHAndSjLj(TargetMachine * TM)387 static void basicCheckForEHAndSjLj(TargetMachine *TM) {
388
389 // You can't enable two modes of EH at the same time
390 if (WasmEnableEmEH && WasmEnableEH)
391 report_fatal_error(
392 "-enable-emscripten-cxx-exceptions not allowed with -wasm-enable-eh");
393 // You can't enable two modes of SjLj at the same time
394 if (WasmEnableEmSjLj && WasmEnableSjLj)
395 report_fatal_error(
396 "-enable-emscripten-sjlj not allowed with -wasm-enable-sjlj");
397 // You can't mix Emscripten EH with Wasm SjLj.
398 if (WasmEnableEmEH && WasmEnableSjLj)
399 report_fatal_error(
400 "-enable-emscripten-cxx-exceptions not allowed with -wasm-enable-sjlj");
401 if (WasmEnableExnref && !WasmEnableEH)
402 report_fatal_error(
403 "-wasm-enable-exnref should be used with -wasm-enable-eh");
404
405 // Here we make sure TargetOptions.ExceptionModel is the same as
406 // MCAsmInfo.ExceptionsType. Normally these have to be the same, because clang
407 // stores the exception model info in LangOptions, which is later transferred
408 // to TargetOptions and MCAsmInfo. But when clang compiles bitcode directly,
409 // clang's LangOptions is not used and thus the exception model info is not
410 // correctly transferred to TargetOptions and MCAsmInfo, so we make sure we
411 // have the correct exception model in WebAssemblyMCAsmInfo constructor. But
412 // in this case TargetOptions is still not updated, so we make sure they are
413 // the same.
414 TM->Options.ExceptionModel = TM->getMCAsmInfo()->getExceptionHandlingType();
415
416 // Basic Correctness checking related to -exception-model
417 if (TM->Options.ExceptionModel != ExceptionHandling::None &&
418 TM->Options.ExceptionModel != ExceptionHandling::Wasm)
419 report_fatal_error("-exception-model should be either 'none' or 'wasm'");
420 if (WasmEnableEmEH && TM->Options.ExceptionModel == ExceptionHandling::Wasm)
421 report_fatal_error("-exception-model=wasm not allowed with "
422 "-enable-emscripten-cxx-exceptions");
423 if (WasmEnableEH && TM->Options.ExceptionModel != ExceptionHandling::Wasm)
424 report_fatal_error(
425 "-wasm-enable-eh only allowed with -exception-model=wasm");
426 if (WasmEnableSjLj && TM->Options.ExceptionModel != ExceptionHandling::Wasm)
427 report_fatal_error(
428 "-wasm-enable-sjlj only allowed with -exception-model=wasm");
429 if ((!WasmEnableEH && !WasmEnableSjLj) &&
430 TM->Options.ExceptionModel == ExceptionHandling::Wasm)
431 report_fatal_error(
432 "-exception-model=wasm only allowed with at least one of "
433 "-wasm-enable-eh or -wasm-enable-sjlj");
434
435 // Currently it is allowed to mix Wasm EH with Emscripten SjLj as an interim
436 // measure, but some code will error out at compile time in this combination.
437 // See WebAssemblyLowerEmscriptenEHSjLj pass for details.
438 }
439
440 //===----------------------------------------------------------------------===//
441 // The following functions are called from lib/CodeGen/Passes.cpp to modify
442 // the CodeGen pass sequence.
443 //===----------------------------------------------------------------------===//
444
addIRPasses()445 void WebAssemblyPassConfig::addIRPasses() {
446 // Add signatures to prototype-less function declarations
447 addPass(createWebAssemblyAddMissingPrototypes());
448
449 // Lower .llvm.global_dtors into .llvm.global_ctors with __cxa_atexit calls.
450 addPass(createLowerGlobalDtorsLegacyPass());
451
452 // Fix function bitcasts, as WebAssembly requires caller and callee signatures
453 // to match.
454 addPass(createWebAssemblyFixFunctionBitcasts());
455
456 // Optimize "returned" function attributes.
457 if (getOptLevel() != CodeGenOptLevel::None)
458 addPass(createWebAssemblyOptimizeReturned());
459
460 basicCheckForEHAndSjLj(TM);
461
462 // If exception handling is not enabled and setjmp/longjmp handling is
463 // enabled, we lower invokes into calls and delete unreachable landingpad
464 // blocks. Lowering invokes when there is no EH support is done in
465 // TargetPassConfig::addPassesToHandleExceptions, but that runs after these IR
466 // passes and Emscripten SjLj handling expects all invokes to be lowered
467 // before.
468 if (!WasmEnableEmEH && !WasmEnableEH) {
469 addPass(createLowerInvokePass());
470 // The lower invoke pass may create unreachable code. Remove it in order not
471 // to process dead blocks in setjmp/longjmp handling.
472 addPass(createUnreachableBlockEliminationPass());
473 }
474
475 // Handle exceptions and setjmp/longjmp if enabled. Unlike Wasm EH preparation
476 // done in WasmEHPrepare pass, Wasm SjLj preparation shares libraries and
477 // transformation algorithms with Emscripten SjLj, so we run
478 // LowerEmscriptenEHSjLj pass also when Wasm SjLj is enabled.
479 if (WasmEnableEmEH || WasmEnableEmSjLj || WasmEnableSjLj)
480 addPass(createWebAssemblyLowerEmscriptenEHSjLj());
481
482 // Expand indirectbr instructions to switches.
483 addPass(createIndirectBrExpandPass());
484
485 TargetPassConfig::addIRPasses();
486 }
487
addISelPrepare()488 void WebAssemblyPassConfig::addISelPrepare() {
489 // We need to move reference type allocas to WASM_ADDRESS_SPACE_VAR so that
490 // loads and stores are promoted to local.gets/local.sets.
491 addPass(createWebAssemblyRefTypeMem2Local());
492 // Lower atomics and TLS if necessary
493 addPass(new CoalesceFeaturesAndStripAtomics(&getWebAssemblyTargetMachine()));
494
495 // This is a no-op if atomics are not used in the module
496 addPass(createAtomicExpandLegacyPass());
497
498 TargetPassConfig::addISelPrepare();
499 }
500
addInstSelector()501 bool WebAssemblyPassConfig::addInstSelector() {
502 (void)TargetPassConfig::addInstSelector();
503 addPass(
504 createWebAssemblyISelDag(getWebAssemblyTargetMachine(), getOptLevel()));
505 // Run the argument-move pass immediately after the ScheduleDAG scheduler
506 // so that we can fix up the ARGUMENT instructions before anything else
507 // sees them in the wrong place.
508 addPass(createWebAssemblyArgumentMove());
509 // Set the p2align operands. This information is present during ISel, however
510 // it's inconvenient to collect. Collect it now, and update the immediate
511 // operands.
512 addPass(createWebAssemblySetP2AlignOperands());
513
514 // Eliminate range checks and add default targets to br_table instructions.
515 addPass(createWebAssemblyFixBrTableDefaults());
516
517 // unreachable is terminator, non-terminator instruction after it is not
518 // allowed.
519 addPass(createWebAssemblyCleanCodeAfterTrap());
520
521 return false;
522 }
523
addOptimizedRegAlloc()524 void WebAssemblyPassConfig::addOptimizedRegAlloc() {
525 // Currently RegisterCoalesce degrades wasm debug info quality by a
526 // significant margin. As a quick fix, disable this for -O1, which is often
527 // used for debugging large applications. Disabling this increases code size
528 // of Emscripten core benchmarks by ~5%, which is acceptable for -O1, which is
529 // usually not used for production builds.
530 // TODO Investigate why RegisterCoalesce degrades debug info quality and fix
531 // it properly
532 if (getOptLevel() == CodeGenOptLevel::Less)
533 disablePass(&RegisterCoalescerID);
534 TargetPassConfig::addOptimizedRegAlloc();
535 }
536
addPostRegAlloc()537 void WebAssemblyPassConfig::addPostRegAlloc() {
538 // TODO: The following CodeGen passes don't currently support code containing
539 // virtual registers. Consider removing their restrictions and re-enabling
540 // them.
541
542 // These functions all require the NoVRegs property.
543 disablePass(&MachineLateInstrsCleanupID);
544 disablePass(&MachineCopyPropagationID);
545 disablePass(&PostRAMachineSinkingID);
546 disablePass(&PostRASchedulerID);
547 disablePass(&FuncletLayoutID);
548 disablePass(&StackMapLivenessID);
549 disablePass(&PatchableFunctionID);
550 disablePass(&ShrinkWrapID);
551
552 // This pass hurts code size for wasm because it can generate irreducible
553 // control flow.
554 disablePass(&MachineBlockPlacementID);
555
556 TargetPassConfig::addPostRegAlloc();
557 }
558
addPreEmitPass()559 void WebAssemblyPassConfig::addPreEmitPass() {
560 TargetPassConfig::addPreEmitPass();
561
562 // Nullify DBG_VALUE_LISTs that we cannot handle.
563 addPass(createWebAssemblyNullifyDebugValueLists());
564
565 // Eliminate multiple-entry loops.
566 if (!WasmDisableFixIrreducibleControlFlowPass)
567 addPass(createWebAssemblyFixIrreducibleControlFlow());
568
569 // Do various transformations for exception handling.
570 // Every CFG-changing optimizations should come before this.
571 if (TM->Options.ExceptionModel == ExceptionHandling::Wasm)
572 addPass(createWebAssemblyLateEHPrepare());
573
574 // Now that we have a prologue and epilogue and all frame indices are
575 // rewritten, eliminate SP and FP. This allows them to be stackified,
576 // colored, and numbered with the rest of the registers.
577 addPass(createWebAssemblyReplacePhysRegs());
578
579 // Preparations and optimizations related to register stackification.
580 if (getOptLevel() != CodeGenOptLevel::None) {
581 // Depend on LiveIntervals and perform some optimizations on it.
582 addPass(createWebAssemblyOptimizeLiveIntervals());
583
584 // Prepare memory intrinsic calls for register stackifying.
585 addPass(createWebAssemblyMemIntrinsicResults());
586
587 // Mark registers as representing wasm's value stack. This is a key
588 // code-compression technique in WebAssembly. We run this pass (and
589 // MemIntrinsicResults above) very late, so that it sees as much code as
590 // possible, including code emitted by PEI and expanded by late tail
591 // duplication.
592 addPass(createWebAssemblyRegStackify());
593
594 // Run the register coloring pass to reduce the total number of registers.
595 // This runs after stackification so that it doesn't consider registers
596 // that become stackified.
597 addPass(createWebAssemblyRegColoring());
598 }
599
600 // Sort the blocks of the CFG into topological order, a prerequisite for
601 // BLOCK and LOOP markers.
602 addPass(createWebAssemblyCFGSort());
603
604 // Insert BLOCK and LOOP markers.
605 addPass(createWebAssemblyCFGStackify());
606
607 // Insert explicit local.get and local.set operators.
608 if (!WasmDisableExplicitLocals)
609 addPass(createWebAssemblyExplicitLocals());
610
611 // Lower br_unless into br_if.
612 addPass(createWebAssemblyLowerBrUnless());
613
614 // Perform the very last peephole optimizations on the code.
615 if (getOptLevel() != CodeGenOptLevel::None)
616 addPass(createWebAssemblyPeephole());
617
618 // Create a mapping from LLVM CodeGen virtual registers to wasm registers.
619 addPass(createWebAssemblyRegNumbering());
620
621 // Fix debug_values whose defs have been stackified.
622 if (!WasmDisableExplicitLocals)
623 addPass(createWebAssemblyDebugFixup());
624
625 // Collect information to prepare for MC lowering / asm printing.
626 addPass(createWebAssemblyMCLowerPrePass());
627 }
628
addPreISel()629 bool WebAssemblyPassConfig::addPreISel() {
630 TargetPassConfig::addPreISel();
631 addPass(createWebAssemblyLowerRefTypesIntPtrConv());
632 return false;
633 }
634
635 yaml::MachineFunctionInfo *
createDefaultFuncInfoYAML() const636 WebAssemblyTargetMachine::createDefaultFuncInfoYAML() const {
637 return new yaml::WebAssemblyFunctionInfo();
638 }
639
convertFuncInfoToYAML(const MachineFunction & MF) const640 yaml::MachineFunctionInfo *WebAssemblyTargetMachine::convertFuncInfoToYAML(
641 const MachineFunction &MF) const {
642 const auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
643 return new yaml::WebAssemblyFunctionInfo(MF, *MFI);
644 }
645
parseMachineFunctionInfo(const yaml::MachineFunctionInfo & MFI,PerFunctionMIParsingState & PFS,SMDiagnostic & Error,SMRange & SourceRange) const646 bool WebAssemblyTargetMachine::parseMachineFunctionInfo(
647 const yaml::MachineFunctionInfo &MFI, PerFunctionMIParsingState &PFS,
648 SMDiagnostic &Error, SMRange &SourceRange) const {
649 const auto &YamlMFI = static_cast<const yaml::WebAssemblyFunctionInfo &>(MFI);
650 MachineFunction &MF = PFS.MF;
651 MF.getInfo<WebAssemblyFunctionInfo>()->initializeBaseYamlFields(MF, YamlMFI);
652 return false;
653 }
654