[llvm] [CodeGen] Introduce MIR-level target-independent rematerialization helper (PR #177080)
Quentin Colombet via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 12 17:54:44 PST 2026
================
@@ -0,0 +1,737 @@
+//=====-- Rematerializer.cpp - MIR rematerialization support ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//==-----------------------------------------------------------------------===//
+//
+/// \file
+/// Implements helpers for target-independent rematerialization at the MIR
+/// level.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/CodeGen/Rematerializer.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/CodeGen/LiveIntervals.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
+#include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/Register.h"
+#include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/CodeGen/TargetRegisterInfo.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "rematerializer"
+
+using namespace llvm;
+
+static bool isAvailableAtUse(const VNInfo *OVNI, LaneBitmask Mask,
+ SlotIndex UseIdx, const LiveInterval &LI) {
+ assert(OVNI);
+ if (OVNI != LI.getVNInfoAt(UseIdx))
+ return false;
+
+ // Check that subrange is live at user.
+ if (LI.hasSubRanges()) {
+ for (const LiveInterval::SubRange &SR : LI.subranges()) {
+ if ((SR.LaneMask & Mask).none())
+ continue;
+ if (!SR.liveAt(UseIdx))
+ return false;
+
+ // Early exit if all used lanes are checked. No need to continue.
+ Mask &= ~SR.LaneMask;
+ if (Mask.none())
+ break;
+ }
+ }
+ return true;
+}
+
+static Register isRegDependency(const MachineOperand &MO) {
+ if (!MO.isReg() || !MO.readsReg())
+ return Register();
+ Register Reg = MO.getReg();
+ if (Reg.isPhysical()) {
+ // By the requirements on trivially rematerializable instructions, a
+ // physical register use is either constant or ignorable.
+ return Register();
+ }
+ return Reg;
+}
+
+unsigned Rematerializer::rematerializeToRegion(unsigned RootIdx,
+ unsigned UseRegion,
+ DependencyReuseInfo &DRI) {
+
+ MachineInstr *FirstMI =
+ getReg(RootIdx).getRegionUseBounds(UseRegion, LIS).first;
+ unsigned NewRegIdx = rematerializeToPos(RootIdx, FirstMI, DRI);
+ transferRegionUsers(RootIdx, NewRegIdx, UseRegion);
+ return NewRegIdx;
+}
+
+unsigned
+Rematerializer::rematerializeToPos(unsigned RootIdx,
+ MachineBasicBlock::iterator InsertPos,
+ DependencyReuseInfo &DRI) {
+ LLVM_DEBUG({
+ rdbgs() << "Rematerializing " << printID(RootIdx) << " to "
+ << printUser(&*InsertPos) << '\n';
+ ++CallDepth;
+ });
+
+ // Create/identify dependencies for the new register. Copy the dependencies
+ // vector because underlying updates to the backing vector of registers may
+ // invalidate references.
+ SmallVector<Reg::Dependency, 2> NewDeps, Deps(Regs[RootIdx].Dependencies);
+ for (const Reg::Dependency &Dep : Deps) {
+ if (auto NewDep = DRI.DependencyMap.find(Dep.RegIdx);
+ NewDep != DRI.DependencyMap.end()) {
+ // We already have the version of the dependency we want to use.
+ NewDeps.emplace_back(Dep.MOIdx, NewDep->second);
+ } else {
+ // Dependencies must be rematerialized in def-use order.
+ unsigned NewDepIdx = rematerializeToPos(Dep.RegIdx, InsertPos, DRI);
+ DRI.DependencyMap.insert({Dep.RegIdx, NewDepIdx});
+ NewDeps.emplace_back(Dep.MOIdx, NewDepIdx);
+ }
+ }
+
+ LLVM_DEBUG(--CallDepth);
+ return rematerializeReg(RootIdx, InsertPos, std::move(NewDeps));
+}
+
+void Rematerializer::rollbackRematsOf(unsigned RootIdx) {
+ auto Remats = Rematerializations.find(RootIdx);
+ if (Remats == Rematerializations.end())
+ return;
+
+ LLVM_DEBUG({
+ rdbgs() << "Rolling back rematerializations of " << printID(RootIdx)
+ << '\n';
+ });
+
+ reviveRegIfDead(RootIdx);
+ // All of the rematerialization's users must use the revived register.
+ for (unsigned RematRegIdx : Remats->getSecond()) {
+ for (const auto &[UseRegion, RegionUsers] : Regs[RematRegIdx].Uses)
+ transferRegionUsers(RematRegIdx, RootIdx, UseRegion);
+ }
+ Rematerializations.erase(RootIdx);
+
+ LLVM_DEBUG({
+ rdbgs() << "** Rolled back rematerializations of " << printID(RootIdx)
+ << '\n';
+ });
+}
+
+void Rematerializer::rollback(unsigned RematIdx) {
+ assert(getReg(RematIdx).DefMI && !Rollbackable.contains(RematIdx) &&
+ "cannot rollback dead register");
+ const unsigned OriginRegIdx = getOriginOf(RematIdx);
+ reviveRegIfDead(OriginRegIdx);
+ for (const auto &[UseRegion, RegionUsers] : Regs[RematIdx].Uses)
+ transferRegionUsers(RematIdx, OriginRegIdx, UseRegion);
+}
+
+void Rematerializer::reviveRegIfDead(unsigned RootIdx) {
+ assert(!isRematerializedRegister(RootIdx) &&
+ "cannot revive rematerialization");
+
+ Reg &Root = Regs[RootIdx];
+ if (!Root.Uses.empty()) {
+ // The register still exists, nothing to do.
+ LLVM_DEBUG(rdbgs() << printID(RootIdx) << " still exists\n");
+ return;
+ }
+
+ assert(Rollbackable.contains(RootIdx) && "not marked rollbackable");
+ assert(Root.DefMI && Root.DefMI->getOpcode() == TargetOpcode::DBG_VALUE &&
+ "not the right opcode");
+ assert(Rematerializations.contains(RootIdx) && "no remats");
+
+ LLVM_DEBUG({
+ rdbgs() << "Reviving " << printID(RootIdx) << '\n';
+ ++CallDepth;
+ });
+
+ // Fully rematerialized dependencies need to be revived. All dependencies gain
+ // a new user.
+ for (const Reg::Dependency &Dep : Root.Dependencies) {
+ reviveRegIfDead(Dep.RegIdx);
+ Regs[Dep.RegIdx].addUser(Root.DefMI, Root.DefRegion);
+ LISUpdates.insert(Dep.RegIdx);
+ }
+
+ // Pick any rematerialization to retrieve the original opcode from.
+ unsigned RematIdx = *Rematerializations.at(RootIdx).begin();
+ Root.DefMI->setDesc(getReg(RematIdx).DefMI->getDesc());
+ for (const auto &[MOIdx, Reg] : Rollbackable.at(RootIdx))
+ Root.DefMI->getOperand(MOIdx).setReg(Reg);
+ Rollbackable.erase(RootIdx);
+ LISUpdates.insert(RootIdx);
+
+ LLVM_DEBUG({
+ rdbgs() << "** Revived " << printID(RootIdx) << " @ ";
+ LIS.getInstructionIndex(*Root.DefMI).print(dbgs());
+ dbgs() << '\n';
+ --CallDepth;
+ });
+}
+
+void Rematerializer::transferUser(unsigned FromRegIdx, unsigned ToRegIdx,
+ MachineInstr &UserMI) {
+ transferUserInternal(FromRegIdx, ToRegIdx, UserMI);
----------------
qcolombet wrote:
Nit: instead of `Internal` we usually go with `Impl`
https://github.com/llvm/llvm-project/pull/177080
More information about the llvm-commits
mailing list