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 "llvm/Analysis/TargetLibraryInfo.h" 19 #include "llvm/CodeGen/MachineFunctionPass.h" 20 #include "llvm/CodeGen/MachineInstrBuilder.h" 21 #include "llvm/CodeGen/MachineRegisterInfo.h" 22 using namespace llvm; 23 24 #define DEBUG_TYPE "wasm-peephole" 25 26 static cl::opt<bool> DisableWebAssemblyFallthroughReturnOpt( 27 "disable-wasm-fallthrough-return-opt", cl::Hidden, 28 cl::desc("WebAssembly: Disable fallthrough-return optimizations."), 29 cl::init(false)); 30 31 namespace { 32 class WebAssemblyPeephole final : public MachineFunctionPass { 33 StringRef getPassName() const override { 34 return "WebAssembly late peephole optimizer"; 35 } 36 37 void getAnalysisUsage(AnalysisUsage &AU) const override { 38 AU.setPreservesCFG(); 39 AU.addRequired<TargetLibraryInfoWrapperPass>(); 40 MachineFunctionPass::getAnalysisUsage(AU); 41 } 42 43 bool runOnMachineFunction(MachineFunction &MF) override; 44 45 public: 46 static char ID; 47 WebAssemblyPeephole() : MachineFunctionPass(ID) {} 48 }; 49 } // end anonymous namespace 50 51 char WebAssemblyPeephole::ID = 0; 52 INITIALIZE_PASS(WebAssemblyPeephole, DEBUG_TYPE, 53 "WebAssembly peephole optimizations", false, false) 54 55 FunctionPass *llvm::createWebAssemblyPeephole() { 56 return new WebAssemblyPeephole(); 57 } 58 59 /// If desirable, rewrite NewReg to a drop register. 60 static bool maybeRewriteToDrop(unsigned OldReg, unsigned NewReg, 61 MachineOperand &MO, WebAssemblyFunctionInfo &MFI, 62 MachineRegisterInfo &MRI) { 63 bool Changed = false; 64 if (OldReg == NewReg) { 65 Changed = true; 66 unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(OldReg)); 67 MO.setReg(NewReg); 68 MO.setIsDead(); 69 MFI.stackifyVReg(NewReg); 70 } 71 return Changed; 72 } 73 74 static bool maybeRewriteToFallthrough(MachineInstr &MI, MachineBasicBlock &MBB, 75 const MachineFunction &MF, 76 WebAssemblyFunctionInfo &MFI, 77 MachineRegisterInfo &MRI, 78 const WebAssemblyInstrInfo &TII, 79 unsigned FallthroughOpc, 80 unsigned CopyLocalOpc) { 81 if (DisableWebAssemblyFallthroughReturnOpt) 82 return false; 83 if (&MBB != &MF.back()) 84 return false; 85 86 MachineBasicBlock::iterator End = MBB.end(); 87 --End; 88 assert(End->getOpcode() == WebAssembly::END_FUNCTION); 89 --End; 90 if (&MI != &*End) 91 return false; 92 93 if (FallthroughOpc != WebAssembly::FALLTHROUGH_RETURN_VOID) { 94 // If the operand isn't stackified, insert a COPY to read the operand and 95 // stackify it. 96 MachineOperand &MO = MI.getOperand(0); 97 unsigned Reg = MO.getReg(); 98 if (!MFI.isVRegStackified(Reg)) { 99 unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); 100 BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(CopyLocalOpc), NewReg) 101 .addReg(Reg); 102 MO.setReg(NewReg); 103 MFI.stackifyVReg(NewReg); 104 } 105 } 106 107 // Rewrite the return. 108 MI.setDesc(TII.get(FallthroughOpc)); 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 = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(); 124 bool Changed = false; 125 126 for (auto &MBB : MF) 127 for (auto &MI : MBB) 128 switch (MI.getOpcode()) { 129 default: 130 break; 131 case WebAssembly::CALL_i32: 132 case WebAssembly::CALL_i64: { 133 MachineOperand &Op1 = MI.getOperand(1); 134 if (Op1.isSymbol()) { 135 StringRef Name(Op1.getSymbolName()); 136 if (Name == TLI.getLibcallName(RTLIB::MEMCPY) || 137 Name == TLI.getLibcallName(RTLIB::MEMMOVE) || 138 Name == TLI.getLibcallName(RTLIB::MEMSET)) { 139 LibFunc Func; 140 if (LibInfo.getLibFunc(Name, Func)) { 141 const auto &Op2 = MI.getOperand(2); 142 if (!Op2.isReg()) 143 report_fatal_error("Peephole: call to builtin function with " 144 "wrong signature, not consuming reg"); 145 MachineOperand &MO = MI.getOperand(0); 146 unsigned OldReg = MO.getReg(); 147 unsigned NewReg = Op2.getReg(); 148 149 if (MRI.getRegClass(NewReg) != MRI.getRegClass(OldReg)) 150 report_fatal_error("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_I32: 160 Changed |= maybeRewriteToFallthrough( 161 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I32, 162 WebAssembly::COPY_I32); 163 break; 164 case WebAssembly::RETURN_I64: 165 Changed |= maybeRewriteToFallthrough( 166 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I64, 167 WebAssembly::COPY_I64); 168 break; 169 case WebAssembly::RETURN_F32: 170 Changed |= maybeRewriteToFallthrough( 171 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F32, 172 WebAssembly::COPY_F32); 173 break; 174 case WebAssembly::RETURN_F64: 175 Changed |= maybeRewriteToFallthrough( 176 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F64, 177 WebAssembly::COPY_F64); 178 break; 179 case WebAssembly::RETURN_v16i8: 180 Changed |= maybeRewriteToFallthrough( 181 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v16i8, 182 WebAssembly::COPY_V128); 183 break; 184 case WebAssembly::RETURN_v8i16: 185 Changed |= maybeRewriteToFallthrough( 186 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v8i16, 187 WebAssembly::COPY_V128); 188 break; 189 case WebAssembly::RETURN_v4i32: 190 Changed |= maybeRewriteToFallthrough( 191 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4i32, 192 WebAssembly::COPY_V128); 193 break; 194 case WebAssembly::RETURN_v2i64: 195 Changed |= maybeRewriteToFallthrough( 196 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v2i64, 197 WebAssembly::COPY_V128); 198 break; 199 case WebAssembly::RETURN_v4f32: 200 Changed |= maybeRewriteToFallthrough( 201 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4f32, 202 WebAssembly::COPY_V128); 203 break; 204 case WebAssembly::RETURN_v2f64: 205 Changed |= maybeRewriteToFallthrough( 206 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v2f64, 207 WebAssembly::COPY_V128); 208 break; 209 case WebAssembly::RETURN_VOID: 210 Changed |= maybeRewriteToFallthrough( 211 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_VOID, 212 WebAssembly::INSTRUCTION_LIST_END); 213 break; 214 } 215 216 return Changed; 217 } 218