1 //===-- WebAssemblyPeephole.cpp - WebAssembly Peephole Optimiztions -------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief Late peephole optimizations for WebAssembly.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16 #include "WebAssembly.h"
17 #include "WebAssemblyMachineFunctionInfo.h"
18 #include "WebAssemblySubtarget.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(false));
31
32 namespace {
33 class WebAssemblyPeephole final : public MachineFunctionPass {
getPassName() const34 const char *getPassName() const override {
35 return "WebAssembly late peephole optimizer";
36 }
37
getAnalysisUsage(AnalysisUsage & AU) const38 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;
WebAssemblyPeephole()48 WebAssemblyPeephole() : MachineFunctionPass(ID) {}
49 };
50 } // end anonymous namespace
51
52 char WebAssemblyPeephole::ID = 0;
createWebAssemblyPeephole()53 FunctionPass *llvm::createWebAssemblyPeephole() {
54 return new WebAssemblyPeephole();
55 }
56
57 /// If desirable, rewrite NewReg to a drop register.
MaybeRewriteToDrop(unsigned OldReg,unsigned NewReg,MachineOperand & MO,WebAssemblyFunctionInfo & MFI,MachineRegisterInfo & MRI)58 static bool MaybeRewriteToDrop(unsigned OldReg, unsigned NewReg,
59 MachineOperand &MO, WebAssemblyFunctionInfo &MFI,
60 MachineRegisterInfo &MRI) {
61 bool Changed = false;
62 if (OldReg == NewReg) {
63 Changed = true;
64 unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(OldReg));
65 MO.setReg(NewReg);
66 MO.setIsDead();
67 MFI.stackifyVReg(NewReg);
68 }
69 return Changed;
70 }
71
MaybeRewriteToFallthrough(MachineInstr & MI,MachineBasicBlock & MBB,const MachineFunction & MF,WebAssemblyFunctionInfo & MFI,MachineRegisterInfo & MRI,const WebAssemblyInstrInfo & TII,unsigned FallthroughOpc,unsigned CopyLocalOpc)72 static bool MaybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB,
73 const MachineFunction &MF,
74 WebAssemblyFunctionInfo &MFI,
75 MachineRegisterInfo &MRI,
76 const WebAssemblyInstrInfo &TII,
77 unsigned FallthroughOpc,
78 unsigned CopyLocalOpc) {
79 if (DisableWebAssemblyFallthroughReturnOpt)
80 return false;
81 if (&MBB != &MF.back())
82 return false;
83 if (&MI != &MBB.back())
84 return false;
85
86 // If the operand isn't stackified, insert a COPY_LOCAL to read the operand
87 // and stackify it.
88 MachineOperand &MO = MI.getOperand(0);
89 unsigned Reg = MO.getReg();
90 if (!MFI.isVRegStackified(Reg)) {
91 unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg));
92 BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(CopyLocalOpc), NewReg)
93 .addReg(Reg);
94 MO.setReg(NewReg);
95 MFI.stackifyVReg(NewReg);
96 }
97
98 // Rewrite the return.
99 MI.setDesc(TII.get(FallthroughOpc));
100 return true;
101 }
102
runOnMachineFunction(MachineFunction & MF)103 bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
104 DEBUG({
105 dbgs() << "********** Peephole **********\n"
106 << "********** Function: " << MF.getName() << '\n';
107 });
108
109 MachineRegisterInfo &MRI = MF.getRegInfo();
110 WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
111 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
112 const WebAssemblyTargetLowering &TLI =
113 *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering();
114 auto &LibInfo = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
115 bool Changed = false;
116
117 for (auto &MBB : MF)
118 for (auto &MI : MBB)
119 switch (MI.getOpcode()) {
120 default:
121 break;
122 case WebAssembly::STORE8_I32:
123 case WebAssembly::STORE16_I32:
124 case WebAssembly::STORE8_I64:
125 case WebAssembly::STORE16_I64:
126 case WebAssembly::STORE32_I64:
127 case WebAssembly::STORE_F32:
128 case WebAssembly::STORE_F64:
129 case WebAssembly::STORE_I32:
130 case WebAssembly::STORE_I64: {
131 // Store instructions return their value operand. If we ended up using
132 // the same register for both, replace it with a dead def so that it
133 // can use $drop instead.
134 MachineOperand &MO = MI.getOperand(0);
135 unsigned OldReg = MO.getReg();
136 unsigned NewReg =
137 MI.getOperand(WebAssembly::StoreValueOperandNo).getReg();
138 Changed |= MaybeRewriteToDrop(OldReg, NewReg, MO, MFI, MRI);
139 break;
140 }
141 case WebAssembly::CALL_I32:
142 case WebAssembly::CALL_I64: {
143 MachineOperand &Op1 = MI.getOperand(1);
144 if (Op1.isSymbol()) {
145 StringRef Name(Op1.getSymbolName());
146 if (Name == TLI.getLibcallName(RTLIB::MEMCPY) ||
147 Name == TLI.getLibcallName(RTLIB::MEMMOVE) ||
148 Name == TLI.getLibcallName(RTLIB::MEMSET)) {
149 LibFunc::Func Func;
150 if (LibInfo.getLibFunc(Name, Func)) {
151 const auto &Op2 = MI.getOperand(2);
152 if (!Op2.isReg())
153 report_fatal_error("Peephole: call to builtin function with "
154 "wrong signature, not consuming reg");
155 MachineOperand &MO = MI.getOperand(0);
156 unsigned OldReg = MO.getReg();
157 unsigned NewReg = Op2.getReg();
158
159 if (MRI.getRegClass(NewReg) != MRI.getRegClass(OldReg))
160 report_fatal_error("Peephole: call to builtin function with "
161 "wrong signature, from/to mismatch");
162 Changed |= MaybeRewriteToDrop(OldReg, NewReg, MO, MFI, MRI);
163 }
164 }
165 }
166 break;
167 }
168 // Optimize away an explicit void return at the end of the function.
169 case WebAssembly::RETURN_I32:
170 Changed |= MaybeRewriteToFallthrough(
171 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I32,
172 WebAssembly::COPY_LOCAL_I32);
173 break;
174 case WebAssembly::RETURN_I64:
175 Changed |= MaybeRewriteToFallthrough(
176 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I64,
177 WebAssembly::COPY_LOCAL_I64);
178 break;
179 case WebAssembly::RETURN_F32:
180 Changed |= MaybeRewriteToFallthrough(
181 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F32,
182 WebAssembly::COPY_LOCAL_F32);
183 break;
184 case WebAssembly::RETURN_F64:
185 Changed |= MaybeRewriteToFallthrough(
186 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F64,
187 WebAssembly::COPY_LOCAL_F64);
188 break;
189 case WebAssembly::RETURN_VOID:
190 if (!DisableWebAssemblyFallthroughReturnOpt &&
191 &MBB == &MF.back() && &MI == &MBB.back())
192 MI.setDesc(TII.get(WebAssembly::FALLTHROUGH_RETURN_VOID));
193 break;
194 }
195
196 return Changed;
197 }
198