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