xref: /freebsd/contrib/llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyMemIntrinsicResults.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
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();
570b57cec5SDimitry Andric     AU.addRequired<MachineBlockFrequencyInfo>();
580b57cec5SDimitry Andric     AU.addPreserved<MachineBlockFrequencyInfo>();
590b57cec5SDimitry Andric     AU.addRequired<MachineDominatorTree>();
600b57cec5SDimitry Andric     AU.addPreserved<MachineDominatorTree>();
610b57cec5SDimitry Andric     AU.addRequired<LiveIntervals>();
620b57cec5SDimitry Andric     AU.addPreserved<SlotIndexes>();
630b57cec5SDimitry Andric     AU.addPreserved<LiveIntervals>();
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 
990b57cec5SDimitry Andric   for (auto I = MRI.use_nodbg_begin(FromReg), E = MRI.use_nodbg_end();
1000b57cec5SDimitry Andric        I != E;) {
1010b57cec5SDimitry Andric     MachineOperand &O = *I++;
1020b57cec5SDimitry Andric     MachineInstr *Where = O.getParent();
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric     // Check that MI dominates the instruction in the normal way.
1050b57cec5SDimitry Andric     if (&MI == Where || !MDT.dominates(&MI, Where))
1060b57cec5SDimitry Andric       continue;
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric     // If this use gets a different value, skip it.
1090b57cec5SDimitry Andric     SlotIndex WhereIdx = LIS.getInstructionIndex(*Where);
1100b57cec5SDimitry Andric     VNInfo *WhereVNI = FromLI->getVNInfoAt(WhereIdx);
1110b57cec5SDimitry Andric     if (WhereVNI && WhereVNI != FromVNI)
1120b57cec5SDimitry Andric       continue;
1130b57cec5SDimitry Andric 
1140b57cec5SDimitry Andric     // Make sure ToReg isn't clobbered before it gets there.
1150b57cec5SDimitry Andric     VNInfo *ToVNI = ToLI->getVNInfoAt(WhereIdx);
1160b57cec5SDimitry Andric     if (ToVNI && ToVNI != FromVNI)
1170b57cec5SDimitry Andric       continue;
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric     Changed = true;
1200b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "Setting operand " << O << " in " << *Where << " from "
1210b57cec5SDimitry Andric                       << MI << "\n");
1220b57cec5SDimitry Andric     O.setReg(ToReg);
1230b57cec5SDimitry Andric 
1240b57cec5SDimitry Andric     // If the store's def was previously dead, it is no longer.
1250b57cec5SDimitry Andric     if (!O.isUndef()) {
1260b57cec5SDimitry Andric       MI.getOperand(0).setIsDead(false);
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric       Indices.push_back(WhereIdx.getRegSlot());
1290b57cec5SDimitry Andric     }
1300b57cec5SDimitry Andric   }
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   if (Changed) {
1330b57cec5SDimitry Andric     // Extend ToReg's liveness.
1340b57cec5SDimitry Andric     LIS.extendToIndices(*ToLI, Indices);
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric     // Shrink FromReg's liveness.
1370b57cec5SDimitry Andric     LIS.shrinkToUses(FromLI);
1380b57cec5SDimitry Andric 
1390b57cec5SDimitry Andric     // If we replaced all dominated uses, FromReg is now killed at MI.
1400b57cec5SDimitry Andric     if (!FromLI->liveAt(FromIdx.getDeadSlot()))
1410b57cec5SDimitry Andric       MI.addRegisterKilled(FromReg, MBB.getParent()
1420b57cec5SDimitry Andric                                         ->getSubtarget<WebAssemblySubtarget>()
1430b57cec5SDimitry Andric                                         .getRegisterInfo());
1440b57cec5SDimitry Andric   }
1450b57cec5SDimitry Andric 
1460b57cec5SDimitry Andric   return Changed;
1470b57cec5SDimitry Andric }
1480b57cec5SDimitry Andric 
1490b57cec5SDimitry Andric static bool optimizeCall(MachineBasicBlock &MBB, MachineInstr &MI,
1500b57cec5SDimitry Andric                          const MachineRegisterInfo &MRI,
1510b57cec5SDimitry Andric                          MachineDominatorTree &MDT, LiveIntervals &LIS,
1520b57cec5SDimitry Andric                          const WebAssemblyTargetLowering &TLI,
1530b57cec5SDimitry Andric                          const TargetLibraryInfo &LibInfo) {
1540b57cec5SDimitry Andric   MachineOperand &Op1 = MI.getOperand(1);
1550b57cec5SDimitry Andric   if (!Op1.isSymbol())
1560b57cec5SDimitry Andric     return false;
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric   StringRef Name(Op1.getSymbolName());
1590b57cec5SDimitry Andric   bool CallReturnsInput = Name == TLI.getLibcallName(RTLIB::MEMCPY) ||
1600b57cec5SDimitry Andric                           Name == TLI.getLibcallName(RTLIB::MEMMOVE) ||
1610b57cec5SDimitry Andric                           Name == TLI.getLibcallName(RTLIB::MEMSET);
1620b57cec5SDimitry Andric   if (!CallReturnsInput)
1630b57cec5SDimitry Andric     return false;
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric   LibFunc Func;
1660b57cec5SDimitry Andric   if (!LibInfo.getLibFunc(Name, Func))
1670b57cec5SDimitry Andric     return false;
1680b57cec5SDimitry Andric 
1698bcb0991SDimitry Andric   Register FromReg = MI.getOperand(2).getReg();
1708bcb0991SDimitry Andric   Register ToReg = MI.getOperand(0).getReg();
1710b57cec5SDimitry Andric   if (MRI.getRegClass(FromReg) != MRI.getRegClass(ToReg))
1720b57cec5SDimitry Andric     report_fatal_error("Memory Intrinsic results: call to builtin function "
1730b57cec5SDimitry Andric                        "with wrong signature, from/to mismatch");
1740b57cec5SDimitry Andric   return replaceDominatedUses(MBB, MI, FromReg, ToReg, MRI, MDT, LIS);
1750b57cec5SDimitry Andric }
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric bool WebAssemblyMemIntrinsicResults::runOnMachineFunction(MachineFunction &MF) {
1780b57cec5SDimitry Andric   LLVM_DEBUG({
1790b57cec5SDimitry Andric     dbgs() << "********** Memory Intrinsic Results **********\n"
1800b57cec5SDimitry Andric            << "********** Function: " << MF.getName() << '\n';
1810b57cec5SDimitry Andric   });
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric   MachineRegisterInfo &MRI = MF.getRegInfo();
1840b57cec5SDimitry Andric   auto &MDT = getAnalysis<MachineDominatorTree>();
1850b57cec5SDimitry Andric   const WebAssemblyTargetLowering &TLI =
1860b57cec5SDimitry Andric       *MF.getSubtarget<WebAssemblySubtarget>().getTargetLowering();
1878bcb0991SDimitry Andric   const auto &LibInfo =
1888bcb0991SDimitry Andric       getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(MF.getFunction());
1890b57cec5SDimitry Andric   auto &LIS = getAnalysis<LiveIntervals>();
1900b57cec5SDimitry Andric   bool Changed = false;
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric   // We don't preserve SSA form.
1930b57cec5SDimitry Andric   MRI.leaveSSA();
1940b57cec5SDimitry Andric 
1950b57cec5SDimitry Andric   assert(MRI.tracksLiveness() &&
1960b57cec5SDimitry Andric          "MemIntrinsicResults expects liveness tracking");
1970b57cec5SDimitry Andric 
1980b57cec5SDimitry Andric   for (auto &MBB : MF) {
1990b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "Basic Block: " << MBB.getName() << '\n');
2000b57cec5SDimitry Andric     for (auto &MI : MBB)
2010b57cec5SDimitry Andric       switch (MI.getOpcode()) {
2020b57cec5SDimitry Andric       default:
2030b57cec5SDimitry Andric         break;
204*5ffd83dbSDimitry Andric       case WebAssembly::CALL:
2050b57cec5SDimitry Andric         Changed |= optimizeCall(MBB, MI, MRI, MDT, LIS, TLI, LibInfo);
2060b57cec5SDimitry Andric         break;
2070b57cec5SDimitry Andric       }
2080b57cec5SDimitry Andric   }
2090b57cec5SDimitry Andric 
2100b57cec5SDimitry Andric   return Changed;
2110b57cec5SDimitry Andric }
212