xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //== WebAssemblyMemIntrinsicResults.cpp - Optimize memory intrinsic results ==//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric ///
9*0b57cec5SDimitry Andric /// \file
10*0b57cec5SDimitry Andric /// This file implements an optimization pass using memory intrinsic results.
11*0b57cec5SDimitry Andric ///
12*0b57cec5SDimitry Andric /// Calls to memory intrinsics (memcpy, memmove, memset) return the destination
13*0b57cec5SDimitry Andric /// address. They are in the form of
14*0b57cec5SDimitry Andric ///   %dst_new = call @memcpy %dst, %src, %len
15*0b57cec5SDimitry Andric /// where %dst and %dst_new registers contain the same value.
16*0b57cec5SDimitry Andric ///
17*0b57cec5SDimitry Andric /// This is to enable an optimization wherein uses of the %dst register used in
18*0b57cec5SDimitry Andric /// the parameter can be replaced by uses of the %dst_new register used in the
19*0b57cec5SDimitry Andric /// result, making the %dst register more likely to be single-use, thus more
20*0b57cec5SDimitry Andric /// likely to be useful to register stackifying, and potentially also exposing
21*0b57cec5SDimitry Andric /// the call instruction itself to register stackifying. These both can reduce
22*0b57cec5SDimitry Andric /// local.get/local.set traffic.
23*0b57cec5SDimitry Andric ///
24*0b57cec5SDimitry Andric /// The LLVM intrinsics for these return void so they can't use the returned
25*0b57cec5SDimitry Andric /// attribute and consequently aren't handled by the OptimizeReturned pass.
26*0b57cec5SDimitry Andric ///
27*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
28*0b57cec5SDimitry Andric 
29*0b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
30*0b57cec5SDimitry Andric #include "WebAssembly.h"
31*0b57cec5SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h"
32*0b57cec5SDimitry Andric #include "WebAssemblySubtarget.h"
33*0b57cec5SDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h"
34*0b57cec5SDimitry Andric #include "llvm/CodeGen/LiveIntervals.h"
35*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineBlockFrequencyInfo.h"
36*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineDominators.h"
37*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
38*0b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h"
39*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
40*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
41*0b57cec5SDimitry Andric using namespace llvm;
42*0b57cec5SDimitry Andric 
43*0b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-mem-intrinsic-results"
44*0b57cec5SDimitry Andric 
45*0b57cec5SDimitry Andric namespace {
46*0b57cec5SDimitry Andric class WebAssemblyMemIntrinsicResults final : public MachineFunctionPass {
47*0b57cec5SDimitry Andric public:
48*0b57cec5SDimitry Andric   static char ID; // Pass identification, replacement for typeid
49*0b57cec5SDimitry Andric   WebAssemblyMemIntrinsicResults() : MachineFunctionPass(ID) {}
50*0b57cec5SDimitry Andric 
51*0b57cec5SDimitry Andric   StringRef getPassName() const override {
52*0b57cec5SDimitry Andric     return "WebAssembly Memory Intrinsic Results";
53*0b57cec5SDimitry Andric   }
54*0b57cec5SDimitry Andric 
55*0b57cec5SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
56*0b57cec5SDimitry Andric     AU.setPreservesCFG();
57*0b57cec5SDimitry Andric     AU.addRequired<MachineBlockFrequencyInfo>();
58*0b57cec5SDimitry Andric     AU.addPreserved<MachineBlockFrequencyInfo>();
59*0b57cec5SDimitry Andric     AU.addRequired<MachineDominatorTree>();
60*0b57cec5SDimitry Andric     AU.addPreserved<MachineDominatorTree>();
61*0b57cec5SDimitry Andric     AU.addRequired<LiveIntervals>();
62*0b57cec5SDimitry Andric     AU.addPreserved<SlotIndexes>();
63*0b57cec5SDimitry Andric     AU.addPreserved<LiveIntervals>();
64*0b57cec5SDimitry Andric     AU.addRequired<TargetLibraryInfoWrapperPass>();
65*0b57cec5SDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
66*0b57cec5SDimitry Andric   }
67*0b57cec5SDimitry Andric 
68*0b57cec5SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override;
69*0b57cec5SDimitry Andric 
70*0b57cec5SDimitry Andric private:
71*0b57cec5SDimitry Andric };
72*0b57cec5SDimitry Andric } // end anonymous namespace
73*0b57cec5SDimitry Andric 
74*0b57cec5SDimitry Andric char WebAssemblyMemIntrinsicResults::ID = 0;
75*0b57cec5SDimitry Andric INITIALIZE_PASS(WebAssemblyMemIntrinsicResults, DEBUG_TYPE,
76*0b57cec5SDimitry Andric                 "Optimize memory intrinsic result values for WebAssembly",
77*0b57cec5SDimitry Andric                 false, false)
78*0b57cec5SDimitry Andric 
79*0b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyMemIntrinsicResults() {
80*0b57cec5SDimitry Andric   return new WebAssemblyMemIntrinsicResults();
81*0b57cec5SDimitry Andric }
82*0b57cec5SDimitry Andric 
83*0b57cec5SDimitry Andric // Replace uses of FromReg with ToReg if they are dominated by MI.
84*0b57cec5SDimitry Andric static bool replaceDominatedUses(MachineBasicBlock &MBB, MachineInstr &MI,
85*0b57cec5SDimitry Andric                                  unsigned FromReg, unsigned ToReg,
86*0b57cec5SDimitry Andric                                  const MachineRegisterInfo &MRI,
87*0b57cec5SDimitry Andric                                  MachineDominatorTree &MDT,
88*0b57cec5SDimitry Andric                                  LiveIntervals &LIS) {
89*0b57cec5SDimitry Andric   bool Changed = false;
90*0b57cec5SDimitry Andric 
91*0b57cec5SDimitry Andric   LiveInterval *FromLI = &LIS.getInterval(FromReg);
92*0b57cec5SDimitry Andric   LiveInterval *ToLI = &LIS.getInterval(ToReg);
93*0b57cec5SDimitry Andric 
94*0b57cec5SDimitry Andric   SlotIndex FromIdx = LIS.getInstructionIndex(MI).getRegSlot();
95*0b57cec5SDimitry Andric   VNInfo *FromVNI = FromLI->getVNInfoAt(FromIdx);
96*0b57cec5SDimitry Andric 
97*0b57cec5SDimitry Andric   SmallVector<SlotIndex, 4> Indices;
98*0b57cec5SDimitry Andric 
99*0b57cec5SDimitry Andric   for (auto I = MRI.use_nodbg_begin(FromReg), E = MRI.use_nodbg_end();
100*0b57cec5SDimitry Andric        I != E;) {
101*0b57cec5SDimitry Andric     MachineOperand &O = *I++;
102*0b57cec5SDimitry Andric     MachineInstr *Where = O.getParent();
103*0b57cec5SDimitry Andric 
104*0b57cec5SDimitry Andric     // Check that MI dominates the instruction in the normal way.
105*0b57cec5SDimitry Andric     if (&MI == Where || !MDT.dominates(&MI, Where))
106*0b57cec5SDimitry Andric       continue;
107*0b57cec5SDimitry Andric 
108*0b57cec5SDimitry Andric     // If this use gets a different value, skip it.
109*0b57cec5SDimitry Andric     SlotIndex WhereIdx = LIS.getInstructionIndex(*Where);
110*0b57cec5SDimitry Andric     VNInfo *WhereVNI = FromLI->getVNInfoAt(WhereIdx);
111*0b57cec5SDimitry Andric     if (WhereVNI && WhereVNI != FromVNI)
112*0b57cec5SDimitry Andric       continue;
113*0b57cec5SDimitry Andric 
114*0b57cec5SDimitry Andric     // Make sure ToReg isn't clobbered before it gets there.
115*0b57cec5SDimitry Andric     VNInfo *ToVNI = ToLI->getVNInfoAt(WhereIdx);
116*0b57cec5SDimitry Andric     if (ToVNI && ToVNI != FromVNI)
117*0b57cec5SDimitry Andric       continue;
118*0b57cec5SDimitry Andric 
119*0b57cec5SDimitry Andric     Changed = true;
120*0b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "Setting operand " << O << " in " << *Where << " from "
121*0b57cec5SDimitry Andric                       << MI << "\n");
122*0b57cec5SDimitry Andric     O.setReg(ToReg);
123*0b57cec5SDimitry Andric 
124*0b57cec5SDimitry Andric     // If the store's def was previously dead, it is no longer.
125*0b57cec5SDimitry Andric     if (!O.isUndef()) {
126*0b57cec5SDimitry Andric       MI.getOperand(0).setIsDead(false);
127*0b57cec5SDimitry Andric 
128*0b57cec5SDimitry Andric       Indices.push_back(WhereIdx.getRegSlot());
129*0b57cec5SDimitry Andric     }
130*0b57cec5SDimitry Andric   }
131*0b57cec5SDimitry Andric 
132*0b57cec5SDimitry Andric   if (Changed) {
133*0b57cec5SDimitry Andric     // Extend ToReg's liveness.
134*0b57cec5SDimitry Andric     LIS.extendToIndices(*ToLI, Indices);
135*0b57cec5SDimitry Andric 
136*0b57cec5SDimitry Andric     // Shrink FromReg's liveness.
137*0b57cec5SDimitry Andric     LIS.shrinkToUses(FromLI);
138*0b57cec5SDimitry Andric 
139*0b57cec5SDimitry Andric     // If we replaced all dominated uses, FromReg is now killed at MI.
140*0b57cec5SDimitry Andric     if (!FromLI->liveAt(FromIdx.getDeadSlot()))
141*0b57cec5SDimitry Andric       MI.addRegisterKilled(FromReg, MBB.getParent()
142*0b57cec5SDimitry Andric                                         ->getSubtarget<WebAssemblySubtarget>()
143*0b57cec5SDimitry Andric                                         .getRegisterInfo());
144*0b57cec5SDimitry Andric   }
145*0b57cec5SDimitry Andric 
146*0b57cec5SDimitry Andric   return Changed;
147*0b57cec5SDimitry Andric }
148*0b57cec5SDimitry Andric 
149*0b57cec5SDimitry Andric static bool optimizeCall(MachineBasicBlock &MBB, MachineInstr &MI,
150*0b57cec5SDimitry Andric                          const MachineRegisterInfo &MRI,
151*0b57cec5SDimitry Andric                          MachineDominatorTree &MDT, LiveIntervals &LIS,
152*0b57cec5SDimitry Andric                          const WebAssemblyTargetLowering &TLI,
153*0b57cec5SDimitry Andric                          const TargetLibraryInfo &LibInfo) {
154*0b57cec5SDimitry Andric   MachineOperand &Op1 = MI.getOperand(1);
155*0b57cec5SDimitry Andric   if (!Op1.isSymbol())
156*0b57cec5SDimitry Andric     return false;
157*0b57cec5SDimitry Andric 
158*0b57cec5SDimitry Andric   StringRef Name(Op1.getSymbolName());
159*0b57cec5SDimitry Andric   bool CallReturnsInput = Name == TLI.getLibcallName(RTLIB::MEMCPY) ||
160*0b57cec5SDimitry Andric                           Name == TLI.getLibcallName(RTLIB::MEMMOVE) ||
161*0b57cec5SDimitry Andric                           Name == TLI.getLibcallName(RTLIB::MEMSET);
162*0b57cec5SDimitry Andric   if (!CallReturnsInput)
163*0b57cec5SDimitry Andric     return false;
164*0b57cec5SDimitry Andric 
165*0b57cec5SDimitry Andric   LibFunc Func;
166*0b57cec5SDimitry Andric   if (!LibInfo.getLibFunc(Name, Func))
167*0b57cec5SDimitry Andric     return false;
168*0b57cec5SDimitry Andric 
169*0b57cec5SDimitry Andric   unsigned FromReg = MI.getOperand(2).getReg();
170*0b57cec5SDimitry Andric   unsigned ToReg = MI.getOperand(0).getReg();
171*0b57cec5SDimitry Andric   if (MRI.getRegClass(FromReg) != MRI.getRegClass(ToReg))
172*0b57cec5SDimitry Andric     report_fatal_error("Memory Intrinsic results: call to builtin function "
173*0b57cec5SDimitry Andric                        "with wrong signature, from/to mismatch");
174*0b57cec5SDimitry Andric   return replaceDominatedUses(MBB, MI, FromReg, ToReg, MRI, MDT, LIS);
175*0b57cec5SDimitry Andric }
176*0b57cec5SDimitry Andric 
177*0b57cec5SDimitry Andric bool WebAssemblyMemIntrinsicResults::runOnMachineFunction(MachineFunction &MF) {
178*0b57cec5SDimitry Andric   LLVM_DEBUG({
179*0b57cec5SDimitry Andric     dbgs() << "********** Memory Intrinsic Results **********\n"
180*0b57cec5SDimitry Andric            << "********** Function: " << MF.getName() << '\n';
181*0b57cec5SDimitry Andric   });
182*0b57cec5SDimitry Andric 
183*0b57cec5SDimitry Andric   MachineRegisterInfo &MRI = MF.getRegInfo();
184*0b57cec5SDimitry Andric   auto &MDT = getAnalysis<MachineDominatorTree>();
185*0b57cec5SDimitry Andric   const WebAssemblyTargetLowering &TLI =
186*0b57cec5SDimitry Andric       *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering();
187*0b57cec5SDimitry Andric   const auto &LibInfo = getAnalysis<TargetLibraryInfoWrapperPass>().getTLI();
188*0b57cec5SDimitry Andric   auto &LIS = getAnalysis<LiveIntervals>();
189*0b57cec5SDimitry Andric   bool Changed = false;
190*0b57cec5SDimitry Andric 
191*0b57cec5SDimitry Andric   // We don't preserve SSA form.
192*0b57cec5SDimitry Andric   MRI.leaveSSA();
193*0b57cec5SDimitry Andric 
194*0b57cec5SDimitry Andric   assert(MRI.tracksLiveness() &&
195*0b57cec5SDimitry Andric          "MemIntrinsicResults expects liveness tracking");
196*0b57cec5SDimitry Andric 
197*0b57cec5SDimitry Andric   for (auto &MBB : MF) {
198*0b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "Basic Block: " << MBB.getName() << '\n');
199*0b57cec5SDimitry Andric     for (auto &MI : MBB)
200*0b57cec5SDimitry Andric       switch (MI.getOpcode()) {
201*0b57cec5SDimitry Andric       default:
202*0b57cec5SDimitry Andric         break;
203*0b57cec5SDimitry Andric       case WebAssembly::CALL_i32:
204*0b57cec5SDimitry Andric       case WebAssembly::CALL_i64:
205*0b57cec5SDimitry Andric         Changed |= optimizeCall(MBB, MI, MRI, MDT, LIS, TLI, LibInfo);
206*0b57cec5SDimitry Andric         break;
207*0b57cec5SDimitry Andric       }
208*0b57cec5SDimitry Andric   }
209*0b57cec5SDimitry Andric 
210*0b57cec5SDimitry Andric   return Changed;
211*0b57cec5SDimitry Andric }
212