xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp (revision d686ce931cab72612a9e1ada9fe99d65e11a32a3)
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