1 | //===-- WebAssemblyPeephole.cpp - WebAssembly Peephole Optimiztions -------===// |
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 | /// Late peephole optimizations for WebAssembly. |
11 | /// |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" |
15 | #include "WebAssembly.h" |
16 | #include "WebAssemblyMachineFunctionInfo.h" |
17 | #include "WebAssemblySubtarget.h" |
18 | #include "WebAssemblyUtilities.h" |
19 | #include "llvm/Analysis/TargetLibraryInfo.h" |
20 | #include "llvm/CodeGen/MachineFunctionPass.h" |
21 | #include "llvm/CodeGen/MachineInstrBuilder.h" |
22 | #include "llvm/CodeGen/MachineRegisterInfo.h" |
23 | using namespace llvm; |
24 | |
25 | #define DEBUG_TYPE "wasm-peephole" |
26 | |
27 | static cl::opt<bool> DisableWebAssemblyFallthroughReturnOpt( |
28 | "disable-wasm-fallthrough-return-opt" , cl::Hidden, |
29 | cl::desc("WebAssembly: Disable fallthrough-return optimizations." ), |
30 | cl::init(Val: false)); |
31 | |
32 | namespace { |
33 | class WebAssemblyPeephole final : public MachineFunctionPass { |
34 | StringRef getPassName() const override { |
35 | return "WebAssembly late peephole optimizer" ; |
36 | } |
37 | |
38 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
39 | AU.setPreservesCFG(); |
40 | AU.addRequired<TargetLibraryInfoWrapperPass>(); |
41 | MachineFunctionPass::getAnalysisUsage(AU); |
42 | } |
43 | |
44 | bool runOnMachineFunction(MachineFunction &MF) override; |
45 | |
46 | public: |
47 | static char ID; |
48 | WebAssemblyPeephole() : MachineFunctionPass(ID) {} |
49 | }; |
50 | } // end anonymous namespace |
51 | |
52 | char WebAssemblyPeephole::ID = 0; |
53 | INITIALIZE_PASS(WebAssemblyPeephole, DEBUG_TYPE, |
54 | "WebAssembly peephole optimizations" , false, false) |
55 | |
56 | FunctionPass *llvm::createWebAssemblyPeephole() { |
57 | return new WebAssemblyPeephole(); |
58 | } |
59 | |
60 | /// If desirable, rewrite NewReg to a drop register. |
61 | static bool maybeRewriteToDrop(unsigned OldReg, unsigned NewReg, |
62 | MachineOperand &MO, WebAssemblyFunctionInfo &MFI, |
63 | MachineRegisterInfo &MRI) { |
64 | bool Changed = false; |
65 | if (OldReg == NewReg) { |
66 | Changed = true; |
67 | Register NewReg = MRI.createVirtualRegister(RegClass: MRI.getRegClass(Reg: OldReg)); |
68 | MO.setReg(NewReg); |
69 | MO.setIsDead(); |
70 | MFI.stackifyVReg(MRI, VReg: NewReg); |
71 | } |
72 | return Changed; |
73 | } |
74 | |
75 | static bool maybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB, |
76 | const MachineFunction &MF, |
77 | WebAssemblyFunctionInfo &MFI, |
78 | MachineRegisterInfo &MRI, |
79 | const WebAssemblyInstrInfo &TII) { |
80 | if (DisableWebAssemblyFallthroughReturnOpt) |
81 | return false; |
82 | if (&MBB != &MF.back()) |
83 | return false; |
84 | |
85 | MachineBasicBlock::iterator End = MBB.end(); |
86 | --End; |
87 | assert(End->getOpcode() == WebAssembly::END_FUNCTION); |
88 | --End; |
89 | if (&MI != &*End) |
90 | return false; |
91 | |
92 | for (auto &MO : MI.explicit_operands()) { |
93 | // If the operand isn't stackified, insert a COPY to read the operands and |
94 | // stackify them. |
95 | Register Reg = MO.getReg(); |
96 | if (!MFI.isVRegStackified(VReg: Reg)) { |
97 | unsigned CopyLocalOpc; |
98 | const TargetRegisterClass *RegClass = MRI.getRegClass(Reg); |
99 | CopyLocalOpc = WebAssembly::getCopyOpcodeForRegClass(RC: RegClass); |
100 | Register NewReg = MRI.createVirtualRegister(RegClass); |
101 | BuildMI(BB&: MBB, I&: MI, MIMD: MI.getDebugLoc(), MCID: TII.get(Opcode: CopyLocalOpc), DestReg: NewReg) |
102 | .addReg(RegNo: Reg); |
103 | MO.setReg(NewReg); |
104 | MFI.stackifyVReg(MRI, VReg: NewReg); |
105 | } |
106 | } |
107 | |
108 | MI.setDesc(TII.get(Opcode: WebAssembly::FALLTHROUGH_RETURN)); |
109 | return true; |
110 | } |
111 | |
112 | bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) { |
113 | LLVM_DEBUG({ |
114 | dbgs() << "********** Peephole **********\n" |
115 | << "********** Function: " << MF.getName() << '\n'; |
116 | }); |
117 | |
118 | MachineRegisterInfo &MRI = MF.getRegInfo(); |
119 | WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); |
120 | const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); |
121 | const WebAssemblyTargetLowering &TLI = |
122 | *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering(); |
123 | auto &LibInfo = |
124 | getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F: MF.getFunction()); |
125 | bool Changed = false; |
126 | |
127 | for (auto &MBB : MF) |
128 | for (auto &MI : MBB) |
129 | switch (MI.getOpcode()) { |
130 | default: |
131 | break; |
132 | case WebAssembly::CALL: { |
133 | MachineOperand &Op1 = MI.getOperand(i: 1); |
134 | if (Op1.isSymbol()) { |
135 | StringRef Name(Op1.getSymbolName()); |
136 | if (Name == TLI.getLibcallName(Call: RTLIB::MEMCPY) || |
137 | Name == TLI.getLibcallName(Call: RTLIB::MEMMOVE) || |
138 | Name == TLI.getLibcallName(Call: RTLIB::MEMSET)) { |
139 | LibFunc Func; |
140 | if (LibInfo.getLibFunc(funcName: Name, F&: Func)) { |
141 | const auto &Op2 = MI.getOperand(i: 2); |
142 | if (!Op2.isReg()) |
143 | report_fatal_error(reason: "Peephole: call to builtin function with " |
144 | "wrong signature, not consuming reg" ); |
145 | MachineOperand &MO = MI.getOperand(i: 0); |
146 | Register OldReg = MO.getReg(); |
147 | Register NewReg = Op2.getReg(); |
148 | |
149 | if (MRI.getRegClass(Reg: NewReg) != MRI.getRegClass(Reg: OldReg)) |
150 | report_fatal_error(reason: "Peephole: call to builtin function with " |
151 | "wrong signature, from/to mismatch" ); |
152 | Changed |= maybeRewriteToDrop(OldReg, NewReg, MO, MFI, MRI); |
153 | } |
154 | } |
155 | } |
156 | break; |
157 | } |
158 | // Optimize away an explicit void return at the end of the function. |
159 | case WebAssembly::RETURN: |
160 | Changed |= maybeRewriteToFallthrough(MI, MBB, MF, MFI, MRI, TII); |
161 | break; |
162 | } |
163 | |
164 | return Changed; |
165 | } |
166 | |