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