xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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