10b57cec5SDimitry Andric //== WebAssemblyMemIntrinsicResults.cpp - Optimize memory intrinsic results ==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric /// 90b57cec5SDimitry Andric /// \file 100b57cec5SDimitry Andric /// This file implements an optimization pass using memory intrinsic results. 110b57cec5SDimitry Andric /// 120b57cec5SDimitry Andric /// Calls to memory intrinsics (memcpy, memmove, memset) return the destination 130b57cec5SDimitry Andric /// address. They are in the form of 140b57cec5SDimitry Andric /// %dst_new = call @memcpy %dst, %src, %len 150b57cec5SDimitry Andric /// where %dst and %dst_new registers contain the same value. 160b57cec5SDimitry Andric /// 170b57cec5SDimitry Andric /// This is to enable an optimization wherein uses of the %dst register used in 180b57cec5SDimitry Andric /// the parameter can be replaced by uses of the %dst_new register used in the 190b57cec5SDimitry Andric /// result, making the %dst register more likely to be single-use, thus more 200b57cec5SDimitry Andric /// likely to be useful to register stackifying, and potentially also exposing 210b57cec5SDimitry Andric /// the call instruction itself to register stackifying. These both can reduce 220b57cec5SDimitry Andric /// local.get/local.set traffic. 230b57cec5SDimitry Andric /// 240b57cec5SDimitry Andric /// The LLVM intrinsics for these return void so they can't use the returned 250b57cec5SDimitry Andric /// attribute and consequently aren't handled by the OptimizeReturned pass. 260b57cec5SDimitry Andric /// 270b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 280b57cec5SDimitry Andric 290b57cec5SDimitry Andric #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 300b57cec5SDimitry Andric #include "WebAssembly.h" 310b57cec5SDimitry Andric #include "WebAssemblyMachineFunctionInfo.h" 320b57cec5SDimitry Andric #include "WebAssemblySubtarget.h" 330b57cec5SDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h" 340b57cec5SDimitry Andric #include "llvm/CodeGen/LiveIntervals.h" 350b57cec5SDimitry Andric #include "llvm/CodeGen/MachineBlockFrequencyInfo.h" 360b57cec5SDimitry Andric #include "llvm/CodeGen/MachineDominators.h" 370b57cec5SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h" 380b57cec5SDimitry Andric #include "llvm/CodeGen/Passes.h" 390b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 400b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 410b57cec5SDimitry Andric using namespace llvm; 420b57cec5SDimitry Andric 430b57cec5SDimitry Andric #define DEBUG_TYPE "wasm-mem-intrinsic-results" 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric namespace { 460b57cec5SDimitry Andric class WebAssemblyMemIntrinsicResults final : public MachineFunctionPass { 470b57cec5SDimitry Andric public: 480b57cec5SDimitry Andric static char ID; // Pass identification, replacement for typeid 490b57cec5SDimitry Andric WebAssemblyMemIntrinsicResults() : MachineFunctionPass(ID) {} 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric StringRef getPassName() const override { 520b57cec5SDimitry Andric return "WebAssembly Memory Intrinsic Results"; 530b57cec5SDimitry Andric } 540b57cec5SDimitry Andric 550b57cec5SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override { 560b57cec5SDimitry Andric AU.setPreservesCFG(); 57*0fca6ea1SDimitry Andric AU.addRequired<MachineBlockFrequencyInfoWrapperPass>(); 58*0fca6ea1SDimitry Andric AU.addPreserved<MachineBlockFrequencyInfoWrapperPass>(); 59*0fca6ea1SDimitry Andric AU.addRequired<MachineDominatorTreeWrapperPass>(); 60*0fca6ea1SDimitry Andric AU.addPreserved<MachineDominatorTreeWrapperPass>(); 61*0fca6ea1SDimitry Andric AU.addRequired<LiveIntervalsWrapperPass>(); 62*0fca6ea1SDimitry Andric AU.addPreserved<SlotIndexesWrapperPass>(); 63*0fca6ea1SDimitry Andric AU.addPreserved<LiveIntervalsWrapperPass>(); 640b57cec5SDimitry Andric AU.addRequired<TargetLibraryInfoWrapperPass>(); 650b57cec5SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU); 660b57cec5SDimitry Andric } 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override; 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric private: 710b57cec5SDimitry Andric }; 720b57cec5SDimitry Andric } // end anonymous namespace 730b57cec5SDimitry Andric 740b57cec5SDimitry Andric char WebAssemblyMemIntrinsicResults::ID = 0; 750b57cec5SDimitry Andric INITIALIZE_PASS(WebAssemblyMemIntrinsicResults, DEBUG_TYPE, 760b57cec5SDimitry Andric "Optimize memory intrinsic result values for WebAssembly", 770b57cec5SDimitry Andric false, false) 780b57cec5SDimitry Andric 790b57cec5SDimitry Andric FunctionPass *llvm::createWebAssemblyMemIntrinsicResults() { 800b57cec5SDimitry Andric return new WebAssemblyMemIntrinsicResults(); 810b57cec5SDimitry Andric } 820b57cec5SDimitry Andric 830b57cec5SDimitry Andric // Replace uses of FromReg with ToReg if they are dominated by MI. 840b57cec5SDimitry Andric static bool replaceDominatedUses(MachineBasicBlock &MBB, MachineInstr &MI, 850b57cec5SDimitry Andric unsigned FromReg, unsigned ToReg, 860b57cec5SDimitry Andric const MachineRegisterInfo &MRI, 870b57cec5SDimitry Andric MachineDominatorTree &MDT, 880b57cec5SDimitry Andric LiveIntervals &LIS) { 890b57cec5SDimitry Andric bool Changed = false; 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric LiveInterval *FromLI = &LIS.getInterval(FromReg); 920b57cec5SDimitry Andric LiveInterval *ToLI = &LIS.getInterval(ToReg); 930b57cec5SDimitry Andric 940b57cec5SDimitry Andric SlotIndex FromIdx = LIS.getInstructionIndex(MI).getRegSlot(); 950b57cec5SDimitry Andric VNInfo *FromVNI = FromLI->getVNInfoAt(FromIdx); 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric SmallVector<SlotIndex, 4> Indices; 980b57cec5SDimitry Andric 99349cc55cSDimitry Andric for (MachineOperand &O : 100349cc55cSDimitry Andric llvm::make_early_inc_range(MRI.use_nodbg_operands(FromReg))) { 1010b57cec5SDimitry Andric MachineInstr *Where = O.getParent(); 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric // Check that MI dominates the instruction in the normal way. 1040b57cec5SDimitry Andric if (&MI == Where || !MDT.dominates(&MI, Where)) 1050b57cec5SDimitry Andric continue; 1060b57cec5SDimitry Andric 1070b57cec5SDimitry Andric // If this use gets a different value, skip it. 1080b57cec5SDimitry Andric SlotIndex WhereIdx = LIS.getInstructionIndex(*Where); 1090b57cec5SDimitry Andric VNInfo *WhereVNI = FromLI->getVNInfoAt(WhereIdx); 1100b57cec5SDimitry Andric if (WhereVNI && WhereVNI != FromVNI) 1110b57cec5SDimitry Andric continue; 1120b57cec5SDimitry Andric 1130b57cec5SDimitry Andric // Make sure ToReg isn't clobbered before it gets there. 1140b57cec5SDimitry Andric VNInfo *ToVNI = ToLI->getVNInfoAt(WhereIdx); 1150b57cec5SDimitry Andric if (ToVNI && ToVNI != FromVNI) 1160b57cec5SDimitry Andric continue; 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric Changed = true; 1190b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Setting operand " << O << " in " << *Where << " from " 1200b57cec5SDimitry Andric << MI << "\n"); 1210b57cec5SDimitry Andric O.setReg(ToReg); 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric // If the store's def was previously dead, it is no longer. 1240b57cec5SDimitry Andric if (!O.isUndef()) { 1250b57cec5SDimitry Andric MI.getOperand(0).setIsDead(false); 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric Indices.push_back(WhereIdx.getRegSlot()); 1280b57cec5SDimitry Andric } 1290b57cec5SDimitry Andric } 1300b57cec5SDimitry Andric 1310b57cec5SDimitry Andric if (Changed) { 1320b57cec5SDimitry Andric // Extend ToReg's liveness. 1330b57cec5SDimitry Andric LIS.extendToIndices(*ToLI, Indices); 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric // Shrink FromReg's liveness. 1360b57cec5SDimitry Andric LIS.shrinkToUses(FromLI); 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric // If we replaced all dominated uses, FromReg is now killed at MI. 1390b57cec5SDimitry Andric if (!FromLI->liveAt(FromIdx.getDeadSlot())) 1400b57cec5SDimitry Andric MI.addRegisterKilled(FromReg, MBB.getParent() 1410b57cec5SDimitry Andric ->getSubtarget<WebAssemblySubtarget>() 1420b57cec5SDimitry Andric .getRegisterInfo()); 1430b57cec5SDimitry Andric } 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric return Changed; 1460b57cec5SDimitry Andric } 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric static bool optimizeCall(MachineBasicBlock &MBB, MachineInstr &MI, 1490b57cec5SDimitry Andric const MachineRegisterInfo &MRI, 1500b57cec5SDimitry Andric MachineDominatorTree &MDT, LiveIntervals &LIS, 1510b57cec5SDimitry Andric const WebAssemblyTargetLowering &TLI, 1520b57cec5SDimitry Andric const TargetLibraryInfo &LibInfo) { 1530b57cec5SDimitry Andric MachineOperand &Op1 = MI.getOperand(1); 1540b57cec5SDimitry Andric if (!Op1.isSymbol()) 1550b57cec5SDimitry Andric return false; 1560b57cec5SDimitry Andric 1570b57cec5SDimitry Andric StringRef Name(Op1.getSymbolName()); 1580b57cec5SDimitry Andric bool CallReturnsInput = Name == TLI.getLibcallName(RTLIB::MEMCPY) || 1590b57cec5SDimitry Andric Name == TLI.getLibcallName(RTLIB::MEMMOVE) || 1600b57cec5SDimitry Andric Name == TLI.getLibcallName(RTLIB::MEMSET); 1610b57cec5SDimitry Andric if (!CallReturnsInput) 1620b57cec5SDimitry Andric return false; 1630b57cec5SDimitry Andric 1640b57cec5SDimitry Andric LibFunc Func; 1650b57cec5SDimitry Andric if (!LibInfo.getLibFunc(Name, Func)) 1660b57cec5SDimitry Andric return false; 1670b57cec5SDimitry Andric 1688bcb0991SDimitry Andric Register FromReg = MI.getOperand(2).getReg(); 1698bcb0991SDimitry Andric Register ToReg = MI.getOperand(0).getReg(); 1700b57cec5SDimitry Andric if (MRI.getRegClass(FromReg) != MRI.getRegClass(ToReg)) 1710b57cec5SDimitry Andric report_fatal_error("Memory Intrinsic results: call to builtin function " 1720b57cec5SDimitry Andric "with wrong signature, from/to mismatch"); 1730b57cec5SDimitry Andric return replaceDominatedUses(MBB, MI, FromReg, ToReg, MRI, MDT, LIS); 1740b57cec5SDimitry Andric } 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric bool WebAssemblyMemIntrinsicResults::runOnMachineFunction(MachineFunction &MF) { 1770b57cec5SDimitry Andric LLVM_DEBUG({ 1780b57cec5SDimitry Andric dbgs() << "********** Memory Intrinsic Results **********\n" 1790b57cec5SDimitry Andric << "********** Function: " << MF.getName() << '\n'; 1800b57cec5SDimitry Andric }); 1810b57cec5SDimitry Andric 1820b57cec5SDimitry Andric MachineRegisterInfo &MRI = MF.getRegInfo(); 183*0fca6ea1SDimitry Andric auto &MDT = getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree(); 1840b57cec5SDimitry Andric const WebAssemblyTargetLowering &TLI = 1850b57cec5SDimitry Andric *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering(); 1868bcb0991SDimitry Andric const auto &LibInfo = 1878bcb0991SDimitry Andric getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(MF.getFunction()); 188*0fca6ea1SDimitry Andric auto &LIS = getAnalysis<LiveIntervalsWrapperPass>().getLIS(); 1890b57cec5SDimitry Andric bool Changed = false; 1900b57cec5SDimitry Andric 1910b57cec5SDimitry Andric // We don't preserve SSA form. 1920b57cec5SDimitry Andric MRI.leaveSSA(); 1930b57cec5SDimitry Andric 1940b57cec5SDimitry Andric assert(MRI.tracksLiveness() && 1950b57cec5SDimitry Andric "MemIntrinsicResults expects liveness tracking"); 1960b57cec5SDimitry Andric 1970b57cec5SDimitry Andric for (auto &MBB : MF) { 1980b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Basic Block: " << MBB.getName() << '\n'); 1990b57cec5SDimitry Andric for (auto &MI : MBB) 2000b57cec5SDimitry Andric switch (MI.getOpcode()) { 2010b57cec5SDimitry Andric default: 2020b57cec5SDimitry Andric break; 2035ffd83dbSDimitry Andric case WebAssembly::CALL: 2040b57cec5SDimitry Andric Changed |= optimizeCall(MBB, MI, MRI, MDT, LIS, TLI, LibInfo); 2050b57cec5SDimitry Andric break; 2060b57cec5SDimitry Andric } 2070b57cec5SDimitry Andric } 2080b57cec5SDimitry Andric 2090b57cec5SDimitry Andric return Changed; 2100b57cec5SDimitry Andric } 211