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 { 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 unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(OldReg)); 68 MO.setReg(NewReg); 69 MO.setIsDead(); 70 MFI.stackifyVReg(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 unsigned FallthroughOpc, 81 unsigned CopyLocalOpc) { 82 if (DisableWebAssemblyFallthroughReturnOpt) 83 return false; 84 if (&MBB != &MF.back()) 85 return false; 86 if (MF.getSubtarget<WebAssemblySubtarget>() 87 .getTargetTriple().isOSBinFormatELF()) { 88 if (&MI != &MBB.back()) 89 return false; 90 } else { 91 MachineBasicBlock::iterator End = MBB.end(); 92 --End; 93 assert(End->getOpcode() == WebAssembly::END_FUNCTION); 94 --End; 95 if (&MI != &*End) 96 return false; 97 } 98 99 if (FallthroughOpc != WebAssembly::FALLTHROUGH_RETURN_VOID) { 100 // If the operand isn't stackified, insert a COPY to read the operand and 101 // stackify it. 102 MachineOperand &MO = MI.getOperand(0); 103 unsigned Reg = MO.getReg(); 104 if (!MFI.isVRegStackified(Reg)) { 105 unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(Reg)); 106 BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(CopyLocalOpc), NewReg) 107 .addReg(Reg); 108 MO.setReg(NewReg); 109 MFI.stackifyVReg(NewReg); 110 } 111 } 112 113 // Rewrite the return. 114 MI.setDesc(TII.get(FallthroughOpc)); 115 return true; 116 } 117 118 bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) { 119 DEBUG({ 120 dbgs() << "********** Peephole **********\n" 121 << "********** Function: " << MF.getName() << '\n'; 122 }); 123 124 MachineRegisterInfo &MRI = MF.getRegInfo(); 125 WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>(); 126 const auto &TII = *MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo(); 127 const WebAssemblyTargetLowering &TLI = 128 *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering(); 129 auto &LibInfo = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(); 130 bool Changed = false; 131 132 for (auto &MBB : MF) 133 for (auto &MI : MBB) 134 switch (MI.getOpcode()) { 135 default: 136 break; 137 case WebAssembly::CALL_I32: 138 case WebAssembly::CALL_I64: { 139 MachineOperand &Op1 = MI.getOperand(1); 140 if (Op1.isSymbol()) { 141 StringRef Name(Op1.getSymbolName()); 142 if (Name == TLI.getLibcallName(RTLIB::MEMCPY) || 143 Name == TLI.getLibcallName(RTLIB::MEMMOVE) || 144 Name == TLI.getLibcallName(RTLIB::MEMSET)) { 145 LibFunc Func; 146 if (LibInfo.getLibFunc(Name, Func)) { 147 const auto &Op2 = MI.getOperand(2); 148 if (!Op2.isReg()) 149 report_fatal_error("Peephole: call to builtin function with " 150 "wrong signature, not consuming reg"); 151 MachineOperand &MO = MI.getOperand(0); 152 unsigned OldReg = MO.getReg(); 153 unsigned NewReg = Op2.getReg(); 154 155 if (MRI.getRegClass(NewReg) != MRI.getRegClass(OldReg)) 156 report_fatal_error("Peephole: call to builtin function with " 157 "wrong signature, from/to mismatch"); 158 Changed |= MaybeRewriteToDrop(OldReg, NewReg, MO, MFI, MRI); 159 } 160 } 161 } 162 break; 163 } 164 // Optimize away an explicit void return at the end of the function. 165 case WebAssembly::RETURN_I32: 166 Changed |= MaybeRewriteToFallthrough( 167 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I32, 168 WebAssembly::COPY_I32); 169 break; 170 case WebAssembly::RETURN_I64: 171 Changed |= MaybeRewriteToFallthrough( 172 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_I64, 173 WebAssembly::COPY_I64); 174 break; 175 case WebAssembly::RETURN_F32: 176 Changed |= MaybeRewriteToFallthrough( 177 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F32, 178 WebAssembly::COPY_F32); 179 break; 180 case WebAssembly::RETURN_F64: 181 Changed |= MaybeRewriteToFallthrough( 182 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_F64, 183 WebAssembly::COPY_F64); 184 break; 185 case WebAssembly::RETURN_v16i8: 186 Changed |= MaybeRewriteToFallthrough( 187 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v16i8, 188 WebAssembly::COPY_V128); 189 break; 190 case WebAssembly::RETURN_v8i16: 191 Changed |= MaybeRewriteToFallthrough( 192 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v8i16, 193 WebAssembly::COPY_V128); 194 break; 195 case WebAssembly::RETURN_v4i32: 196 Changed |= MaybeRewriteToFallthrough( 197 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4i32, 198 WebAssembly::COPY_V128); 199 break; 200 case WebAssembly::RETURN_v4f32: 201 Changed |= MaybeRewriteToFallthrough( 202 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_v4f32, 203 WebAssembly::COPY_V128); 204 break; 205 case WebAssembly::RETURN_VOID: 206 Changed |= MaybeRewriteToFallthrough( 207 MI, MBB, MF, MFI, MRI, TII, WebAssembly::FALLTHROUGH_RETURN_VOID, 208 WebAssembly::INSTRUCTION_LIST_END); 209 break; 210 } 211 212 return Changed; 213 } 214