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