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(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(MRI.getRegClass(OldReg)); 68 MO.setReg(NewReg); 69 MO.setIsDead(); 70 MFI.stackifyVReg(MRI, 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(Reg)) { 97 unsigned CopyLocalOpc; 98 const TargetRegisterClass *RegClass = MRI.getRegClass(Reg); 99 CopyLocalOpc = WebAssembly::getCopyOpcodeForRegClass(RegClass); 100 Register NewReg = MRI.createVirtualRegister(RegClass); 101 BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(CopyLocalOpc), NewReg) 102 .addReg(Reg); 103 MO.setReg(NewReg); 104 MFI.stackifyVReg(MRI, NewReg); 105 } 106 } 107 108 MI.setDesc(TII.get(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(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(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 Register OldReg = MO.getReg(); 147 Register 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: 160 Changed |= maybeRewriteToFallthrough(MI, MBB, MF, MFI, MRI, TII); 161 break; 162 } 163 164 return Changed; 165 } 166