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