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"
23using namespace llvm;
24
25#define DEBUG_TYPE "wasm-peephole"
26
27static cl::opt<bool> DisableWebAssemblyFallthroughReturnOpt(
28 "disable-wasm-fallthrough-return-opt", cl::Hidden,
29 cl::desc("WebAssembly: Disable fallthrough-return optimizations."),
30 cl::init(Val: false));
31
32namespace {
33class 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
46public:
47 static char ID;
48 WebAssemblyPeephole() : MachineFunctionPass(ID) {}
49};
50} // end anonymous namespace
51
52char WebAssemblyPeephole::ID = 0;
53INITIALIZE_PASS(WebAssemblyPeephole, DEBUG_TYPE,
54 "WebAssembly peephole optimizations", false, false)
55
56FunctionPass *llvm::createWebAssemblyPeephole() {
57 return new WebAssemblyPeephole();
58}
59
60/// If desirable, rewrite NewReg to a drop register.
61static 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(RegClass: MRI.getRegClass(Reg: OldReg));
68 MO.setReg(NewReg);
69 MO.setIsDead();
70 MFI.stackifyVReg(MRI, VReg: NewReg);
71 }
72 return Changed;
73}
74
75static 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(VReg: Reg)) {
97 unsigned CopyLocalOpc;
98 const TargetRegisterClass *RegClass = MRI.getRegClass(Reg);
99 CopyLocalOpc = WebAssembly::getCopyOpcodeForRegClass(RC: RegClass);
100 Register NewReg = MRI.createVirtualRegister(RegClass);
101 BuildMI(BB&: MBB, I&: MI, MIMD: MI.getDebugLoc(), MCID: TII.get(Opcode: CopyLocalOpc), DestReg: NewReg)
102 .addReg(RegNo: Reg);
103 MO.setReg(NewReg);
104 MFI.stackifyVReg(MRI, VReg: NewReg);
105 }
106 }
107
108 MI.setDesc(TII.get(Opcode: WebAssembly::FALLTHROUGH_RETURN));
109 return true;
110}
111
112bool 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(F: 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(i: 1);
134 if (Op1.isSymbol()) {
135 StringRef Name(Op1.getSymbolName());
136 if (Name == TLI.getLibcallName(Call: RTLIB::MEMCPY) ||
137 Name == TLI.getLibcallName(Call: RTLIB::MEMMOVE) ||
138 Name == TLI.getLibcallName(Call: RTLIB::MEMSET)) {
139 LibFunc Func;
140 if (LibInfo.getLibFunc(funcName: Name, F&: Func)) {
141 const auto &Op2 = MI.getOperand(i: 2);
142 if (!Op2.isReg())
143 report_fatal_error(reason: "Peephole: call to builtin function with "
144 "wrong signature, not consuming reg");
145 MachineOperand &MO = MI.getOperand(i: 0);
146 Register OldReg = MO.getReg();
147 Register NewReg = Op2.getReg();
148
149 if (MRI.getRegClass(Reg: NewReg) != MRI.getRegClass(Reg: OldReg))
150 report_fatal_error(reason: "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