1//===----------------------------------------------------------------------===//
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#include "WebAssembly.h"
10#include "WebAssemblyTargetMachine.h"
11#include "llvm/CodeGen/AtomicExpand.h"
12#include "llvm/CodeGen/IndirectBrExpand.h"
13#include "llvm/CodeGen/MachineBlockPlacement.h"
14#include "llvm/CodeGen/MachineCopyPropagation.h"
15#include "llvm/CodeGen/MachineLateInstrsCleanup.h"
16#include "llvm/CodeGen/PatchableFunction.h"
17#include "llvm/CodeGen/PostRAMachineSink.h"
18#include "llvm/CodeGen/PostRASchedulerList.h"
19#include "llvm/CodeGen/RegisterCoalescerPass.h"
20#include "llvm/CodeGen/RemoveLoadsIntoFakeUses.h"
21#include "llvm/CodeGen/ShrinkWrap.h"
22#include "llvm/CodeGen/UnreachableBlockElim.h"
23#include "llvm/IR/PassInstrumentation.h"
24#include "llvm/MC/MCStreamer.h"
25#include "llvm/Passes/CodeGenPassBuilder.h"
26#include "llvm/Passes/PassBuilder.h"
27#include "llvm/Support/CodeGen.h"
28#include "llvm/Target/CGPassBuilderOption.h"
29#include "llvm/Transforms/Utils/LowerGlobalDtors.h"
30#include "llvm/Transforms/Utils/LowerInvoke.h"
31
32using namespace llvm;
33
34namespace WebAssembly {
35extern cl::opt<bool> WasmDisableExplicitLocals;
36extern cl::opt<bool> WasmEnableEH;
37extern cl::opt<bool> WasmEnableEmEH;
38extern cl::opt<bool> WasmEnableEmSjLj;
39extern cl::opt<bool> WasmEnableSjLj;
40} // namespace WebAssembly
41
42using llvm::WebAssembly::WasmDisableExplicitLocals;
43using llvm::WebAssembly::WasmEnableEH;
44using llvm::WebAssembly::WasmEnableEmEH;
45using llvm::WebAssembly::WasmEnableEmSjLj;
46using llvm::WebAssembly::WasmEnableSjLj;
47
48namespace {
49
50class WebAssemblyCodeGenPassBuilder
51 : public CodeGenPassBuilder<WebAssemblyCodeGenPassBuilder,
52 WebAssemblyTargetMachine> {
53 using Base = CodeGenPassBuilder<WebAssemblyCodeGenPassBuilder,
54 WebAssemblyTargetMachine>;
55
56public:
57 explicit WebAssemblyCodeGenPassBuilder(WebAssemblyTargetMachine &TM,
58 const CGPassBuilderOption &Opts,
59 PassInstrumentationCallbacks *PIC)
60 : CodeGenPassBuilder(TM, Opts, PIC) {
61 disablePass<MachineLateInstrsCleanupPass, MachineCopyPropagationPass,
62 PostRAMachineSinkingPass, PostRASchedulerPass,
63 FuncletLayoutPass, StackMapLivenessPass, PatchableFunctionPass,
64 ShrinkWrapPass, RemoveLoadsIntoFakeUsesPass,
65 MachineBlockPlacementPass>();
66
67 // Currently RegisterCoalesce degrades wasm debug info quality by a
68 // significant margin. As a quick fix, disable this for -O1, which is often
69 // used for debugging large applications. Disabling this increases code size
70 // of Emscripten core benchmarks by ~5%, which is acceptable for -O1, which
71 // is usually not used for production builds.
72 // TODO Investigate why RegisterCoalesce degrades debug info quality and fix
73 // it properly
74 if (getOptLevel() == CodeGenOptLevel::Less)
75 disablePass<RegisterCoalescerPass>();
76 }
77
78 void addIRPasses(PassManagerWrapper &PMW) const;
79 void addISelPrepare(PassManagerWrapper &PMW) const;
80 Error addInstSelector(PassManagerWrapper &PMW) const;
81 void addPreEmitPass(PassManagerWrapper &PMW) const;
82};
83
84void WebAssemblyCodeGenPassBuilder::addIRPasses(PassManagerWrapper &PMW) const {
85 // Add signatures to prototype-less function declarations
86 flushFPMsToMPM(PMW);
87 addModulePass(Pass: WebAssemblyAddMissingPrototypesPass(), PMW);
88
89 // Lower .llvm.global_dtors into .llvm.global_ctors with __cxa_atexit calls.
90 addModulePass(Pass: LowerGlobalDtorsPass(), PMW);
91
92 // Fix function bitcasts, as WebAssembly requires caller and callee signatures
93 // to match.
94 addModulePass(Pass: WebAssemblyFixFunctionBitcastsPass(), PMW);
95
96 // Optimize "returned" function attributes.
97 if (getOptLevel() != CodeGenOptLevel::None)
98 addFunctionPass(Pass: WebAssemblyOptimizeReturnedPass(), PMW);
99
100 // If exception handling is not enabled and setjmp/longjmp handling is
101 // enabled, we lower invokes into calls and delete unreachable landingpad
102 // blocks. Lowering invokes when there is no EH support is done in
103 // TargetPassConfig::addPassesToHandleExceptions, but that runs after these IR
104 // passes and Emscripten SjLj handling expects all invokes to be lowered
105 // before.
106 if (!WasmEnableEmEH && !WasmEnableEH) {
107 addFunctionPass(Pass: LowerInvokePass(), PMW);
108 // The lower invoke pass may create unreachable code. Remove it in order not
109 // to process dead blocks in setjmp/longjmp handling.
110 addFunctionPass(Pass: UnreachableBlockElimPass(), PMW);
111 }
112
113 // Handle exceptions and setjmp/longjmp if enabled. Unlike Wasm EH preparation
114 // done in WasmEHPrepare pass, Wasm SjLj preparation shares libraries and
115 // transformation algorithms with Emscripten SjLj, so we run
116 // LowerEmscriptenEHSjLj pass also when Wasm SjLj is enabled.
117 if (WasmEnableEmEH || WasmEnableEmSjLj || WasmEnableSjLj) {
118 flushFPMsToMPM(PMW);
119 addModulePass(Pass: WebAssemblyLowerEmscriptenEHSjLjPass(), PMW);
120 }
121
122 // Expand indirectbr instructions to switches.
123 addFunctionPass(Pass: IndirectBrExpandPass(TM), PMW);
124
125 // Try to expand `vecreduce_{and, or}` into `{any, all}_true`.
126 addFunctionPass(Pass: WebAssemblyReduceToAnyAllTruePass(TM), PMW);
127
128 Base::addIRPasses(PMW);
129}
130
131void WebAssemblyCodeGenPassBuilder::addISelPrepare(
132 PassManagerWrapper &PMW) const {
133 // We need to move reference type allocas to WASM_ADDRESS_SPACE_VAR so that
134 // loads and stores are promoted to local.gets/local.sets.
135 addFunctionPass(Pass: WebAssemblyRefTypeMem2LocalPass(), PMW);
136 // Lower atomics and TLS if necessary
137 flushFPMsToMPM(PMW);
138 addModulePass(Pass: WebAssemblyCoalesceFeaturesAndStripAtomicsPass(TM), PMW);
139
140 // This is a no-op if atomics are not used in the module
141 addFunctionPass(Pass: AtomicExpandPass(TM), PMW);
142
143 Base::addISelPrepare(PMW);
144}
145
146Error WebAssemblyCodeGenPassBuilder::addInstSelector(
147 PassManagerWrapper &PMW) const {
148 addMachineFunctionPass(Pass: WebAssemblyISelDAGToDAGPass(TM, getOptLevel()), PMW);
149
150 // Run the argument-move pass immediately after the ScheduleDAG scheduler
151 // so that we can fix up the ARGUMENT instructions before anything else
152 // sees them in the wrong place.
153 addMachineFunctionPass(Pass: WebAssemblyArgumentMovePass(), PMW);
154
155 // Set the p2align operands. This information is present during ISel, however
156 // it's inconvenient to collect. Collect it now, and update the immediate
157 // operands.
158 addMachineFunctionPass(Pass: WebAssemblySetP2AlignOperandsPass(), PMW);
159
160 // Eliminate range checks and add default targets to br_table instructions.
161 addMachineFunctionPass(Pass: WebAssemblyFixBrTableDefaultsPass(), PMW);
162
163 // unreachable is terminator, non-terminator instruction after it is not
164 // allowed.
165 addMachineFunctionPass(Pass: WebAssemblyCleanCodeAfterTrapPass(), PMW);
166
167 return Error::success();
168}
169
170void WebAssemblyCodeGenPassBuilder::addPreEmitPass(
171 PassManagerWrapper &PMW) const {
172 Base::addPreEmitPass(PMW);
173
174 // Nullify DBG_VALUE_LISTs that we cannot handle.
175 addMachineFunctionPass(Pass: WebAssemblyNullifyDebugValueListsPass(), PMW);
176
177 // Remove any unreachable blocks that may be left floating around.
178 // Rare, but possible. Needed for WebAssemblyFixIrreducibleControlFlow.
179 addMachineFunctionPass(Pass: UnreachableMachineBlockElimPass(), PMW);
180
181 // Eliminate multiple-entry loops.
182 addMachineFunctionPass(Pass: WebAssemblyFixIrreducibleControlFlowPass(), PMW);
183
184 // Do various transformations for exception handling.
185 // Every CFG-changing optimizations should come before this.
186 if (TM.Options.ExceptionModel == ExceptionHandling::Wasm)
187 addMachineFunctionPass(Pass: WebAssemblyLateEHPreparePass(), PMW);
188
189 // Now that we have a prologue and epilogue and all frame indices are
190 // rewritten, eliminate SP and FP. This allows them to be stackified,
191 // colored, and numbered with the rest of the registers.
192 addMachineFunctionPass(Pass: WebAssemblyReplacePhysRegsPass(), PMW);
193
194 // Preparations and optimizations related to register stackification.
195 if (getOptLevel() != CodeGenOptLevel::None) {
196 // Depend on LiveIntervals and perform some optimizations on it.
197 // TODO(boomanaiden154): WebAssemblyOptimizeLiveIntervals
198
199 // Prepare memory intrinsic calls for register stackifying.
200 // TODO(boomanaiden154): WebAssemblyMemIntrinsicResults
201 }
202
203 // Mark registers as representing wasm's value stack. This is a key
204 // code-compression technique in WebAssembly. We run this pass (and
205 // MemIntrinsicResults above) very late, so that it sees as much code as
206 // possible, including code emitted by PEI and expanded by late tail
207 // duplication.
208 // TODO(boomanaiden154): WebAssemblyRegStackify
209
210 if (getOptLevel() != CodeGenOptLevel::None) {
211 // Run the register coloring pass to reduce the total number of registers.
212 // This runs after stackification so that it doesn't consider registers
213 // that become stackified.
214 // TODO(boomanaiden154): WebAssemblyRegColoring
215 }
216
217 // Sort the blocks of the CFG into topological order, a prerequisite for
218 // BLOCK and LOOP markers.
219 // TODO(boomanaiden154): WebAssemblyCFGSort
220
221 // Insert BLOCK and LOOP markers.
222 // TODO(boomanaiden154): WebAssemblyCFGStackify
223
224 // Insert explicit local.get and local.set operators.
225 if (!WasmDisableExplicitLocals) {
226 // TODO(boomanaiden154): WebAssemblyExplicitLocals
227 }
228
229 // Lower br_unless into br_if.
230 // TODO(boomanaiden154): WebAssemblyLowerBrUnless
231
232 // Perform the very last peephole optimizations on the code.
233 if (getOptLevel() != CodeGenOptLevel::None) {
234 // TODO(boomanaiden154): WebAssemblyPeephole
235 }
236
237 // Create a mapping from LLVM CodeGen virtual registers to wasm registers.
238 // TODO(boomanaiden154): WebAssemblyRegNumbering
239
240 // Fix debug_values whose defs have been stackified.
241 if (!WasmDisableExplicitLocals) {
242 // TODO(boomanaiden154): WebAssemblyDebugFixup
243 }
244
245 // Collect information to prepare for MC lowering / asm printing.
246 // TODO(boomanaiden154): WebAssemblyMCLowerPrePass
247}
248
249} // namespace
250
251void WebAssemblyTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB){
252#define GET_PASS_REGISTRY "WebAssemblyPassRegistry.def"
253#include "llvm/Passes/TargetPassRegistry.inc"
254}
255
256Error WebAssemblyTargetMachine::buildCodeGenPipeline(
257 ModulePassManager &MPM, ModuleAnalysisManager &MAM, raw_pwrite_stream &Out,
258 raw_pwrite_stream *DwoOut, CodeGenFileType FileType,
259 const CGPassBuilderOption &Opt, MCContext &Ctx,
260 PassInstrumentationCallbacks *PIC) {
261 auto CGPB = WebAssemblyCodeGenPassBuilder(*this, Opt, PIC);
262 return CGPB.buildPipeline(MPM, MAM, Out, DwoOut, FileType, Ctx);
263}
264