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 "Utils/WebAssemblyUtilities.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 StringRef 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;
53 INITIALIZE_PASS(WebAssemblyPeephole, DEBUG_TYPE,
54 "WebAssembly peephole optimizations", false, false)
55
createWebAssemblyPeephole()56 FunctionPass *llvm::createWebAssemblyPeephole() {
57 return new WebAssemblyPeephole();
58 }
59
60 /// If desirable, rewrite NewReg to a drop register.
maybeRewriteToDrop(unsigned OldReg,unsigned NewReg,MachineOperand & MO,WebAssemblyFunctionInfo & MFI,MachineRegisterInfo & MRI)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
maybeRewriteToFallthrough(MachineInstr & MI,MachineBasicBlock & MBB,const MachineFunction & MF,WebAssemblyFunctionInfo & MFI,MachineRegisterInfo & MRI,const WebAssemblyInstrInfo & TII)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
runOnMachineFunction(MachineFunction & MF)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