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 "WebAssembly.h"
16 #include "MCTargetDesc/WebAssemblyMCTargetDesc.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/MachineRegisterInfo.h"
22 using namespace llvm;
23 
24 #define DEBUG_TYPE "wasm-peephole"
25 
26 namespace {
27 class WebAssemblyPeephole final : public MachineFunctionPass {
28   const char *getPassName() const override {
29     return "WebAssembly late peephole optimizer";
30   }
31 
32   void getAnalysisUsage(AnalysisUsage &AU) const override {
33     AU.setPreservesCFG();
34     AU.addRequired<TargetLibraryInfoWrapperPass>();
35     MachineFunctionPass::getAnalysisUsage(AU);
36   }
37 
38   bool runOnMachineFunction(MachineFunction &MF) override;
39 
40 public:
41   static char ID;
42   WebAssemblyPeephole() : MachineFunctionPass(ID) {}
43 };
44 } // end anonymous namespace
45 
46 char WebAssemblyPeephole::ID = 0;
47 FunctionPass *llvm::createWebAssemblyPeephole() {
48   return new WebAssemblyPeephole();
49 }
50 
51 /// If desirable, rewrite NewReg to a discard register.
52 static bool MaybeRewriteToDiscard(unsigned OldReg, unsigned NewReg,
53                                   MachineOperand &MO,
54                                   WebAssemblyFunctionInfo &MFI,
55                                   MachineRegisterInfo &MRI) {
56   bool Changed = false;
57   // TODO: Handle SP/physregs
58   if (OldReg == NewReg && TargetRegisterInfo::isVirtualRegister(NewReg)) {
59     Changed = true;
60     unsigned NewReg = MRI.createVirtualRegister(MRI.getRegClass(OldReg));
61     MO.setReg(NewReg);
62     MO.setIsDead();
63     MFI.stackifyVReg(NewReg);
64     MFI.addWAReg(NewReg, WebAssemblyFunctionInfo::UnusedReg);
65   }
66   return Changed;
67 }
68 
69 bool WebAssemblyPeephole::runOnMachineFunction(MachineFunction &MF) {
70   DEBUG({
71     dbgs() << "********** Store Results **********\n"
72            << "********** Function: " << MF.getName() << '\n';
73   });
74 
75   MachineRegisterInfo &MRI = MF.getRegInfo();
76   WebAssemblyFunctionInfo &MFI = *MF.getInfo<WebAssemblyFunctionInfo>();
77   const WebAssemblyTargetLowering &TLI =
78       *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering();
79   auto &LibInfo = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
80   bool Changed = false;
81 
82   for (auto &MBB : MF)
83     for (auto &MI : MBB)
84       switch (MI.getOpcode()) {
85       default:
86         break;
87       case WebAssembly::STORE8_I32:
88       case WebAssembly::STORE16_I32:
89       case WebAssembly::STORE8_I64:
90       case WebAssembly::STORE16_I64:
91       case WebAssembly::STORE32_I64:
92       case WebAssembly::STORE_F32:
93       case WebAssembly::STORE_F64:
94       case WebAssembly::STORE_I32:
95       case WebAssembly::STORE_I64: {
96         // Store instructions return their value operand. If we ended up using
97         // the same register for both, replace it with a dead def so that it
98         // can use $discard instead.
99         MachineOperand &MO = MI.getOperand(0);
100         unsigned OldReg = MO.getReg();
101         unsigned NewReg =
102             MI.getOperand(WebAssembly::StoreValueOperandNo).getReg();
103         Changed |= MaybeRewriteToDiscard(OldReg, NewReg, MO, MFI, MRI);
104         break;
105       }
106       case WebAssembly::CALL_I32:
107       case WebAssembly::CALL_I64: {
108         MachineOperand &Op1 = MI.getOperand(1);
109         if (Op1.isSymbol()) {
110           StringRef Name(Op1.getSymbolName());
111           if (Name == TLI.getLibcallName(RTLIB::MEMCPY) ||
112               Name == TLI.getLibcallName(RTLIB::MEMMOVE) ||
113               Name == TLI.getLibcallName(RTLIB::MEMSET)) {
114             LibFunc::Func Func;
115             if (LibInfo.getLibFunc(Name, Func)) {
116               const auto &Op2 = MI.getOperand(2);
117               if (!Op2.isReg())
118                 report_fatal_error("Peephole: call to builtin function with "
119                                    "wrong signature, not consuming reg");
120               MachineOperand &MO = MI.getOperand(0);
121               unsigned OldReg = MO.getReg();
122               unsigned NewReg = Op2.getReg();
123 
124               // TODO: Handle SP/physregs in MaybeRewriteToDiscard
125               if (TargetRegisterInfo::isVirtualRegister(NewReg) &&
126                   (MRI.getRegClass(NewReg) != MRI.getRegClass(OldReg)))
127                 report_fatal_error("Peephole: call to builtin function with "
128                                    "wrong signature, from/to mismatch");
129               Changed |= MaybeRewriteToDiscard(OldReg, NewReg, MO, MFI, MRI);
130             }
131           }
132         }
133       }
134       }
135 
136   return Changed;
137 }
138