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