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