[llvm-branch-commits] [llvm] CodeGen: Represent block arguments on MachineBasicBlock (PR #218894)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 26 06:50:17 PDT 2026
https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/218894
>From 2738797e9918458e03e0ecc6df4c6606809e66fd Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 14 Aug 2026 09:45:11 +0200
Subject: [PATCH] CodeGen: Represent block arguments on MachineBasicBlock
The receiver half of the block-argument representation: a block declares a list
of virtual registers that receive values forwarded by each predecessor's
SUCC_ARGS. Unlike a PHI, a block argument is defined by the block itself, not by
an instruction.
MachineBasicBlock holds the argument list, printed and parsed as an "arguments:"
block header line. MachineRegisterInfo maps each argument to its defining block,
so getDefBlock works for these registers. The verifier treats an argument as
defined at block entry and requires each predecessor to supply one matching
SUCC_ARGS.
This is only building infrastructure, and is not yet used.
Co-Authored-By: Claude <noreply at anthropic.com> (Claude Opus 4.8)
---
llvm/include/llvm/CodeGen/MachineBasicBlock.h | 26 ++++
.../llvm/CodeGen/MachineRegisterInfo.h | 30 +++-
llvm/lib/CodeGen/MIRParser/MILexer.cpp | 1 +
llvm/lib/CodeGen/MIRParser/MILexer.h | 1 +
llvm/lib/CodeGen/MIRParser/MIParser.cpp | 37 +++++
llvm/lib/CodeGen/MIRPrinter.cpp | 11 ++
llvm/lib/CodeGen/MachineBasicBlock.cpp | 34 +++++
llvm/lib/CodeGen/MachineFunction.cpp | 1 +
llvm/lib/CodeGen/MachineVerifier.cpp | 50 ++++++-
.../CodeGen/MIR/X86/block-args-invalid.mir | 53 +++++++
.../MIR/X86/block-args-named-blocks.mir | 32 +++++
llvm/test/CodeGen/MIR/X86/block-args.mir | 131 ++++++++++++++++++
.../CodeGen/X86/block-args-delete-block.mir | 29 ++++
.../X86/block-args-count-mismatch.mir | 20 +++
.../X86/block-args-duplicate-succ-args.mir | 20 +++
.../X86/block-args-loop-missing-backedge.mir | 26 ++++
.../X86/block-args-missing-succ-args.mir | 17 +++
.../X86/block-args-physreg.mir | 13 ++
.../X86/block-args-undef-use.mir | 14 ++
19 files changed, 540 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/CodeGen/MIR/X86/block-args-invalid.mir
create mode 100644 llvm/test/CodeGen/MIR/X86/block-args-named-blocks.mir
create mode 100644 llvm/test/CodeGen/MIR/X86/block-args.mir
create mode 100644 llvm/test/CodeGen/X86/block-args-delete-block.mir
create mode 100644 llvm/test/MachineVerifier/X86/block-args-count-mismatch.mir
create mode 100644 llvm/test/MachineVerifier/X86/block-args-duplicate-succ-args.mir
create mode 100644 llvm/test/MachineVerifier/X86/block-args-loop-missing-backedge.mir
create mode 100644 llvm/test/MachineVerifier/X86/block-args-missing-succ-args.mir
create mode 100644 llvm/test/MachineVerifier/X86/block-args-physreg.mir
create mode 100644 llvm/test/MachineVerifier/X86/block-args-undef-use.mir
diff --git a/llvm/include/llvm/CodeGen/MachineBasicBlock.h b/llvm/include/llvm/CodeGen/MachineBasicBlock.h
index fd5fc8fe8c1e5..a02da825975d2 100644
--- a/llvm/include/llvm/CodeGen/MachineBasicBlock.h
+++ b/llvm/include/llvm/CodeGen/MachineBasicBlock.h
@@ -169,6 +169,10 @@ class MachineBasicBlock
using LiveInVector = std::vector<RegisterMaskPair>;
LiveInVector LiveIns;
+ /// The virtual registers defined as arguments of this block, fed positionally
+ /// by a SUCC_ARGS operand in each predecessor.
+ SmallVector<Register, 0> BlockArgs;
+
/// Alignment of the basic block. One if the basic block does not need to be
/// aligned.
Align Alignment;
@@ -550,6 +554,28 @@ class MachineBasicBlock
const std::vector<RegisterMaskPair> &getLiveIns() const { return LiveIns; }
+ //===--------------------------------------------------------------------===//
+ // Block argument accessors.
+ //===--------------------------------------------------------------------===//
+
+ ArrayRef<Register> getBlockArgs() const { return BlockArgs; }
+
+ unsigned getNumBlockArgs() const { return BlockArgs.size(); }
+ Register getBlockArg(unsigned I) const { return BlockArgs[I]; }
+ bool hasBlockArgs() const { return !BlockArgs.empty(); }
+
+ /// Append a register to this block's argument list and record it as defined
+ /// by this block.
+ LLVM_ABI void addBlockArg(Register Reg);
+
+ LLVM_ABI void clearBlockArgs();
+
+ /// Remove block argument \p I and the matching forwarded operand from every
+ /// predecessor's SUCC_ARGS, preserving the positional correspondence between
+ /// block arguments and SUCC_ARGS operands. A SUCC_ARGS left with no forwarded
+ /// values is erased.
+ LLVM_ABI void removeBlockArgAndUpdateSuccArgs(unsigned I);
+
class liveout_iterator {
public:
using iterator_category = std::input_iterator_tag;
diff --git a/llvm/include/llvm/CodeGen/MachineRegisterInfo.h b/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
index a26462641de52..69b1265568d4a 100644
--- a/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineRegisterInfo.h
@@ -15,6 +15,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallPtrSet.h"
@@ -163,6 +164,10 @@ class MachineRegisterInfo {
/// second element.
std::vector<std::pair<MCRegister, Register>> LiveIns;
+ /// Map from a block-argument virtual register to the block that defines it.
+ /// Such registers have no defining MachineInstr.
+ DenseMap<Register, MachineBasicBlock *> BlockArgDefs;
+
public:
LLVM_ABI explicit MachineRegisterInfo(MachineFunction *MF);
MachineRegisterInfo(const MachineRegisterInfo &) = delete;
@@ -635,7 +640,9 @@ class MachineRegisterInfo {
/// getVRegDef - Return the machine instr that defines the specified virtual
/// register or null if none is found. This assumes that the code is in SSA
- /// form, so there should only be one definition.
+ /// form, so there should only be one definition. If you are only interested
+ /// in the defining block, use getDefBlock instead of checking the returned
+ /// instruction's parent.
LLVM_ABI LLVM_READONLY MachineInstr *getVRegDef(Register Reg) const;
/// getUniqueVRegDef - Return the unique machine instr that defines the
@@ -645,11 +652,26 @@ class MachineRegisterInfo {
/// Return the machine basic block in which the specified virtual register is
/// defined, or null if it has no definition. This assumes SSA form.
- MachineBasicBlock *getDefBlock(Register Reg) const {
- MachineInstr *DefMI = getVRegDef(Reg);
- return DefMI ? DefMI->getParent() : nullptr;
+ LLVM_READONLY MachineBasicBlock *getDefBlock(Register Reg) const {
+ if (MachineInstr *DefMI = getVRegDef(Reg))
+ return DefMI->getParent();
+ return getBlockArgDef(Reg);
+ }
+
+ /// If \p Reg is a block-argument register, return its defining block;
+ /// otherwise return null.
+ LLVM_READONLY MachineBasicBlock *getBlockArgDef(Register Reg) const {
+ return BlockArgDefs.lookup(Reg);
}
+ bool isBlockArgDef(Register Reg) const { return BlockArgDefs.contains(Reg); }
+
+ void setBlockArgDef(Register Reg, MachineBasicBlock *MBB) {
+ BlockArgDefs[Reg] = MBB;
+ }
+
+ void clearBlockArgDef(Register Reg) { BlockArgDefs.erase(Reg); }
+
/// clearKillFlags - Iterate over all the uses of the given register and
/// clear the kill flag from the MachineOperand. This function is used by
/// optimization passes which extend register lifetimes and need only
diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp
index 67fbc00edd9db..afc023a204583 100644
--- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp
@@ -281,6 +281,7 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) {
.Case("ehfunclet-entry", MIToken::kw_ehfunclet_entry)
.Case("liveins", MIToken::kw_liveins)
.Case("successors", MIToken::kw_successors)
+ .Case("arguments", MIToken::kw_arguments)
.Case("floatpred", MIToken::kw_floatpred)
.Case("intpred", MIToken::kw_intpred)
.Case("shufflemask", MIToken::kw_shufflemask)
diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.h b/llvm/lib/CodeGen/MIRParser/MILexer.h
index f5947bfe59b9d..6d266ef8d23f2 100644
--- a/llvm/lib/CodeGen/MIRParser/MILexer.h
+++ b/llvm/lib/CodeGen/MIRParser/MILexer.h
@@ -135,6 +135,7 @@ struct MIToken {
kw_ehfunclet_entry,
kw_liveins,
kw_successors,
+ kw_arguments,
kw_floatpred,
kw_intpred,
kw_shufflemask,
diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
index bb0b87cc042d0..6b5c9b1f9d74a 100644
--- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp
@@ -448,6 +448,7 @@ class MIParser {
MachineBasicBlock *&AddFalthroughFrom);
bool parseBasicBlockLiveins(MachineBasicBlock &MBB);
bool parseBasicBlockSuccessors(MachineBasicBlock &MBB);
+ bool parseBasicBlockArguments(MachineBasicBlock &MBB);
bool parseNamedRegister(Register &Reg);
bool parseVirtualRegister(VRegInfo *&Info);
@@ -957,6 +958,39 @@ bool MIParser::parseBasicBlockSuccessors(MachineBasicBlock &MBB) {
return false;
}
+bool MIParser::parseBasicBlockArguments(MachineBasicBlock &MBB) {
+ assert(Token.is(MIToken::kw_arguments));
+ lex();
+ if (expectAndConsume(MIToken::colon))
+ return true;
+ if (Token.isNewlineOrEOF()) // Allow an empty list of arguments.
+ return false;
+ do {
+ if (!Token.isRegister())
+ return error("expected a register");
+ Register Reg;
+ VRegInfo *Info;
+ if (parseRegister(Reg, Info))
+ return true;
+ if (!Reg)
+ return error("block argument cannot be $noreg");
+ lex();
+ // A block argument is a plain register definition; subregister indices are
+ // not meaningful here.
+ if (Token.is(MIToken::dot))
+ return error("unexpected subregister index on a block argument");
+ // Parse an optional inline register class, e.g. "%0:gr32".
+ if (consumeIfPresent(MIToken::colon)) {
+ if (!Info)
+ return error("register class specified for a non-virtual register");
+ if (parseRegisterClassOrBank(*Info))
+ return true;
+ }
+ MBB.addBlockArg(Reg);
+ } while (consumeIfPresent(MIToken::comma));
+ return false;
+}
+
bool MIParser::parseBasicBlock(MachineBasicBlock &MBB,
MachineBasicBlock *&AddFalthroughFrom) {
// Skip the definition.
@@ -987,6 +1021,9 @@ bool MIParser::parseBasicBlock(MachineBasicBlock &MBB,
} else if (Token.is(MIToken::kw_liveins)) {
if (parseBasicBlockLiveins(MBB))
return true;
+ } else if (Token.is(MIToken::kw_arguments)) {
+ if (parseBasicBlockArguments(MBB))
+ return true;
} else if (consumeIfPresent(MIToken::Newline)) {
continue;
} else {
diff --git a/llvm/lib/CodeGen/MIRPrinter.cpp b/llvm/lib/CodeGen/MIRPrinter.cpp
index f66300c17509c..7a287f6b5eabd 100644
--- a/llvm/lib/CodeGen/MIRPrinter.cpp
+++ b/llvm/lib/CodeGen/MIRPrinter.cpp
@@ -804,6 +804,17 @@ void printMBB(raw_ostream &OS, MFPrintState &State,
HasLineAttributes = true;
}
+ // Print the block arguments.
+ if (!MBB.getBlockArgs().empty()) {
+ const TargetRegisterInfo &TRI = *MRI.getTargetRegisterInfo();
+ OS.indent(2) << "arguments: ";
+ ListSeparator LS;
+ for (Register Arg : MBB.getBlockArgs())
+ OS << LS << printReg(Arg, &TRI);
+ OS << '\n';
+ HasLineAttributes = true;
+ }
+
if (HasLineAttributes && !MBB.empty())
OS << "\n";
bool IsInBundle = false;
diff --git a/llvm/lib/CodeGen/MachineBasicBlock.cpp b/llvm/lib/CodeGen/MachineBasicBlock.cpp
index 977bec06a547a..eb1fb8feae886 100644
--- a/llvm/lib/CodeGen/MachineBasicBlock.cpp
+++ b/llvm/lib/CodeGen/MachineBasicBlock.cpp
@@ -1821,6 +1821,40 @@ MachineBasicBlock::getEndClobberMask(const TargetRegisterInfo *TRI) const {
return isReturnBlock() && !succ_empty() ? TRI->getNoPreservedMask() : nullptr;
}
+void MachineBasicBlock::addBlockArg(Register Reg) {
+ BlockArgs.push_back(Reg);
+ getParent()->getRegInfo().setBlockArgDef(Reg, this);
+}
+
+void MachineBasicBlock::clearBlockArgs() {
+ MachineRegisterInfo &MRI = getParent()->getRegInfo();
+ for (Register Reg : BlockArgs)
+ MRI.clearBlockArgDef(Reg);
+ BlockArgs.clear();
+}
+
+void MachineBasicBlock::removeBlockArgAndUpdateSuccArgs(unsigned I) {
+ assert(I < BlockArgs.size() && "Block argument index out of range");
+ MachineRegisterInfo &MRI = getParent()->getRegInfo();
+ MRI.clearBlockArgDef(BlockArgs[I]);
+ BlockArgs.erase(BlockArgs.begin() + I);
+
+ // Drop the matching forwarded operand (index I + 1, past the successor MBB
+ // operand) from each predecessor's SUCC_ARGS that targets this block. If that
+ // leaves a SUCC_ARGS with no forwarded values (only the successor operand),
+ // erase it.
+ for (MachineBasicBlock *Pred : predecessors()) {
+ for (MachineInstr &MI : make_early_inc_range(Pred->succ_args())) {
+ if (MI.getOperand(0).getMBB() != this)
+ continue;
+ assert(I + 1 < MI.getNumOperands() && "SUCC_ARGS operand count mismatch");
+ MI.removeOperand(I + 1);
+ if (MI.getNumOperands() == 1)
+ MI.eraseFromParent();
+ }
+ }
+}
+
void MachineBasicBlock::clearLiveIns() {
LiveIns.clear();
}
diff --git a/llvm/lib/CodeGen/MachineFunction.cpp b/llvm/lib/CodeGen/MachineFunction.cpp
index 213d4ae1ce434..4f7bb44556bd3 100644
--- a/llvm/lib/CodeGen/MachineFunction.cpp
+++ b/llvm/lib/CodeGen/MachineFunction.cpp
@@ -558,6 +558,7 @@ MachineFunction::CreateMachineBasicBlock(const BasicBlock *BB,
/// Delete the given MachineBasicBlock.
void MachineFunction::deleteMachineBasicBlock(MachineBasicBlock *MBB) {
assert(MBB->getParent() == this && "MBB parent mismatch!");
+ MBB->clearBlockArgs();
// Clean up any references to MBB in jump tables before deleting it.
if (JumpTableInfo)
JumpTableInfo->RemoveMBBFromJumpTables(MBB);
diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index dd034d7e5cb73..9a0f38da8f2f7 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -354,6 +354,7 @@ struct MachineVerifier {
void markReachable(const MachineBasicBlock *MBB);
void calcRegsPassed();
void checkPHIOps(const MachineBasicBlock &MBB);
+ void checkBlockArgOps(const MachineBasicBlock &MBB);
void calcRegsRequired();
void verifyLiveVariables();
@@ -923,6 +924,13 @@ MachineVerifier::visitMachineBasicBlockBefore(const MachineBasicBlock *MBB) {
}
}
+ // Block arguments are defined at block entry.
+ for (Register Arg : MBB->getBlockArgs()) {
+ if (!Arg.isVirtual())
+ report("Block argument must be a virtual register", MBB);
+ regsLive.insert(Arg);
+ }
+
const MachineFrameInfo &MFI = MF->getFrameInfo();
BitVector PR = MFI.getPristineRegs(*MF);
for (unsigned I : PR.set_bits())
@@ -3185,7 +3193,7 @@ void MachineVerifier::checkLiveness(const MachineOperand *MO, unsigned MONum) {
}
if (Bad)
report("Using an undefined physical register", MO, MONum);
- } else if (MRI->def_empty(Reg)) {
+ } else if (MRI->def_empty(Reg) && !MRI->isBlockArgDef(Reg)) {
report("Reading virtual register without a def", MO, MONum);
} else {
BBInfo &MInfo = MBBInfoMap[MI->getParent()];
@@ -3523,6 +3531,42 @@ void MachineVerifier::checkPHIOps(const MachineBasicBlock &MBB) {
}
}
+// Check the SUCC_ARGS feeders of a block's arguments. Each predecessor must
+// supply exactly one SUCC_ARGS for this block, whose forwarded value count
+// matches the block's argument count.
+void MachineVerifier::checkBlockArgOps(const MachineBasicBlock &MBB) {
+ unsigned NumArgs = MBB.getNumBlockArgs();
+ if (NumArgs == 0)
+ return;
+
+ for (const MachineBasicBlock *Pred : MBB.predecessors()) {
+ const MachineInstr *Found = nullptr;
+ for (const MachineInstr &MI : Pred->succ_args()) {
+ if (MI.getOperand(0).getMBB() != &MBB)
+ continue;
+ if (Found) {
+ report("Multiple SUCC_ARGS for the same successor", &MI);
+ continue;
+ }
+ Found = &MI;
+ unsigned NumForwarded = MI.getNumOperands() - 1;
+ if (NumForwarded != NumArgs) {
+ report("SUCC_ARGS operand count does not match successor block "
+ "argument count",
+ &MI);
+ OS << "SUCC_ARGS forwards " << NumForwarded << " values but "
+ << printMBBReference(MBB) << " has " << NumArgs << " arguments.\n";
+ }
+ }
+ if (!Found) {
+ report("Missing SUCC_ARGS for a block with arguments", &MBB);
+ OS << printMBBReference(*Pred)
+ << " is a predecessor but has no SUCC_ARGS for "
+ << printMBBReference(MBB) << ".\n";
+ }
+ }
+}
+
static void
verifyConvergenceControl(const MachineFunction &MF, MachineDominatorTree &DT,
std::function<void(const Twine &Message)> FailureCB,
@@ -3550,8 +3594,10 @@ void MachineVerifier::visitMachineFunctionAfter() {
calcRegsPassed();
- for (const MachineBasicBlock &MBB : *MF)
+ for (const MachineBasicBlock &MBB : *MF) {
checkPHIOps(MBB);
+ checkBlockArgOps(MBB);
+ }
// Now check liveness info if available
calcRegsRequired();
diff --git a/llvm/test/CodeGen/MIR/X86/block-args-invalid.mir b/llvm/test/CodeGen/MIR/X86/block-args-invalid.mir
new file mode 100644
index 0000000000000..374e73677e6c0
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/X86/block-args-invalid.mir
@@ -0,0 +1,53 @@
+# RUN: split-file %s %t
+
+# The "arguments:" block header accepts only plain virtual register
+# definitions. Reject subregister indices, $noreg, immediates, and other
+# non-register value expressions.
+
+;--- subreg.mir
+# RUN: not llc %t/subreg.mir -mtriple=x86_64-- -run-pass=none -filetype=null 2>&1 | FileCheck %t/subreg.mir
+# CHECK: error: {{.*}} unexpected subregister index on a block argument
+---
+name: subreg
+usesBlockArgs: true
+body: |
+ bb.0:
+ arguments: %0.sub_32bit:gr32
+ RET64
+...
+
+;--- noreg.mir
+# RUN: not llc %t/noreg.mir -mtriple=x86_64-- -run-pass=none -filetype=null 2>&1 | FileCheck %t/noreg.mir
+# CHECK: error: {{.*}} block argument cannot be $noreg
+---
+name: noreg
+usesBlockArgs: true
+body: |
+ bb.0:
+ arguments: $noreg
+ RET64
+...
+
+;--- immediate.mir
+# RUN: not llc %t/immediate.mir -mtriple=x86_64-- -run-pass=none -filetype=null 2>&1 | FileCheck %t/immediate.mir
+# CHECK: error: {{.*}} expected a register
+---
+name: immediate
+usesBlockArgs: true
+body: |
+ bb.0:
+ arguments: 42
+ RET64
+...
+
+;--- mbb.mir
+# RUN: not llc %t/mbb.mir -mtriple=x86_64-- -run-pass=none -filetype=null 2>&1 | FileCheck %t/mbb.mir
+# CHECK: error: {{.*}} expected a register
+---
+name: mbb
+usesBlockArgs: true
+body: |
+ bb.0:
+ arguments: %bb.0
+ RET64
+...
diff --git a/llvm/test/CodeGen/MIR/X86/block-args-named-blocks.mir b/llvm/test/CodeGen/MIR/X86/block-args-named-blocks.mir
new file mode 100644
index 0000000000000..65daff43aac22
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/X86/block-args-named-blocks.mir
@@ -0,0 +1,32 @@
+# RUN: llc -mtriple=x86_64-- -run-pass=none -o - %s | FileCheck %s
+# With an IR section providing block names, the string name is preserved after
+# the %bb.N prefix on the block label, alongside block arguments.
+
+--- |
+ define void @named_blocks() {
+ entry:
+ br label %exit
+ exit:
+ ret void
+ }
+...
+---
+name: named_blocks
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ ; CHECK-LABEL: name: named_blocks
+ ; CHECK: bb.0.entry:
+ bb.0.entry:
+ successors: %bb.1
+ %0:gr32 = MOV32ri 7
+ ; CHECK: SUCC_ARGS %bb.1, %0
+ SUCC_ARGS %bb.1, %0
+ JMP_1 %bb.1
+
+ ; CHECK: bb.1.exit:
+ ; CHECK: arguments: %1
+ bb.1.exit:
+ arguments: %1:gr32
+ RET64 %1
+...
diff --git a/llvm/test/CodeGen/MIR/X86/block-args.mir b/llvm/test/CodeGen/MIR/X86/block-args.mir
new file mode 100644
index 0000000000000..7b58b2b55ac92
--- /dev/null
+++ b/llvm/test/CodeGen/MIR/X86/block-args.mir
@@ -0,0 +1,131 @@
+# RUN: llc -mtriple=x86_64-- -run-pass=none -o - %s | FileCheck %s
+# Round-trip test for block arguments and their SUCC_ARGS feeders.
+
+# A single block argument fed by one predecessor.
+---
+name: single_arg
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ ; CHECK-LABEL: name: single_arg
+ bb.0:
+ successors: %bb.1
+ ; CHECK: SUCC_ARGS %bb.1, %0
+ %0:gr32 = MOV32ri 7
+ SUCC_ARGS %bb.1, %0
+ JMP_1 %bb.1
+
+ ; CHECK: bb.1:
+ ; CHECK: arguments: %1
+ bb.1:
+ arguments: %1:gr32
+ RET64 %1
+...
+
+# Multiple block arguments fed positionally from two predecessors.
+---
+name: multiple_args
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ ; CHECK-LABEL: name: multiple_args
+ bb.0:
+ successors: %bb.1, %bb.2
+ %0:gr32 = MOV32ri 1
+ %1:gr32 = MOV32ri 2
+ ; CHECK: SUCC_ARGS %bb.2, %0, %1
+ SUCC_ARGS %bb.2, %0, %1
+ JCC_1 %bb.2, 4, implicit undef $eflags
+ JMP_1 %bb.1
+
+ bb.1:
+ successors: %bb.2
+ %2:gr32 = MOV32ri 3
+ %3:gr32 = MOV32ri 4
+ ; CHECK: SUCC_ARGS %bb.2, %2, %3
+ SUCC_ARGS %bb.2, %2, %3
+ JMP_1 %bb.2
+
+ ; CHECK: bb.2:
+ ; CHECK: arguments: %4, %5
+ bb.2:
+ arguments: %4:gr32, %5:gr32
+ %6:gr32 = ADD32rr %4, %5, implicit-def dead $eflags
+ RET64 %6
+...
+
+# An empty argument list must not print an "arguments:" line.
+---
+name: empty_args
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ ; CHECK-LABEL: name: empty_args
+ ; CHECK-NOT: arguments:
+ bb.0:
+ RET64
+...
+
+# A loop header argument fed from both the entry edge and the latch back-edge.
+---
+name: loop_header_arg
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ ; CHECK-LABEL: name: loop_header_arg
+ bb.0:
+ successors: %bb.1
+ %0:gr32 = MOV32ri 0
+ ; CHECK: SUCC_ARGS %bb.1, %0
+ SUCC_ARGS %bb.1, %0
+ JMP_1 %bb.1
+
+ ; CHECK: bb.1:
+ ; CHECK: arguments: %1
+ bb.1:
+ successors: %bb.1, %bb.2
+ arguments: %1:gr32
+ %2:gr32 = ADD32ri %1, 1, implicit-def dead $eflags
+ ; CHECK: SUCC_ARGS %bb.1, %2
+ SUCC_ARGS %bb.1, %2
+ JCC_1 %bb.1, 4, implicit undef $eflags
+ JMP_1 %bb.2
+
+ bb.2:
+ RET64 %1
+...
+
+# The "successors:" and "arguments:" block header lines may appear in either
+# order. bb.1 lists arguments first, bb.2 lists successors first.
+---
+name: header_order
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ ; CHECK-LABEL: name: header_order
+ bb.0:
+ successors: %bb.1
+ %0:gr32 = MOV32ri 7
+ SUCC_ARGS %bb.1, %0
+ JMP_1 %bb.1
+
+ ; CHECK: bb.1:
+ ; CHECK: arguments: %1
+ bb.1:
+ arguments: %1:gr32
+ successors: %bb.2
+ SUCC_ARGS %bb.2, %1
+ JMP_1 %bb.2
+
+ ; CHECK: bb.2:
+ ; CHECK: arguments: %2
+ bb.2:
+ successors: %bb.3
+ arguments: %2:gr32
+ SUCC_ARGS %bb.3, %2
+ JMP_1 %bb.3
+
+ bb.3:
+ arguments: %3:gr32
+ RET64 %3
+...
diff --git a/llvm/test/CodeGen/X86/block-args-delete-block.mir b/llvm/test/CodeGen/X86/block-args-delete-block.mir
new file mode 100644
index 0000000000000..6b56a4d792ce3
--- /dev/null
+++ b/llvm/test/CodeGen/X86/block-args-delete-block.mir
@@ -0,0 +1,29 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=x86_64-- -run-pass=unreachable-mbb-elimination %s -o - | FileCheck %s
+
+# Deleting a block that owns block arguments must drop those arguments
+# from the MachineRegisterInfo reverse map, so the map does not retain
+# a dangling pointer to the freed block.
+---
+name: delete_unreachable_blockarg
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ ; CHECK-LABEL: name: delete_unreachable_blockarg
+ ; CHECK: bb.0:
+ ; CHECK-NEXT: RET64
+
+ ; CHECK-NOT: bb.1:
+ bb.0:
+ RET64
+
+ bb.1:
+ successors: %bb.2
+ %0:gr32 = MOV32ri 7
+ SUCC_ARGS %bb.2, %0
+ JMP_1 %bb.2
+
+ bb.2:
+ arguments: %1:gr32
+ RET64 %1
+...
diff --git a/llvm/test/MachineVerifier/X86/block-args-count-mismatch.mir b/llvm/test/MachineVerifier/X86/block-args-count-mismatch.mir
new file mode 100644
index 0000000000000..ac7ae02f64e9b
--- /dev/null
+++ b/llvm/test/MachineVerifier/X86/block-args-count-mismatch.mir
@@ -0,0 +1,20 @@
+# RUN: not --crash llc -mtriple=x86_64-- -run-pass=none -filetype=null %s 2>&1 | FileCheck %s
+
+# A SUCC_ARGS whose forwarded value count differs from the successor's block
+# argument count is malformed.
+# CHECK: *** Bad machine code: SUCC_ARGS operand count does not match successor block argument count ***
+---
+name: count_mismatch
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ bb.0:
+ successors: %bb.1
+ %0:gr32 = MOV32ri 1
+ SUCC_ARGS %bb.1, %0
+ JMP_1 %bb.1
+
+ bb.1:
+ arguments: %1:gr32, %2:gr32
+ RET64 %1
+...
diff --git a/llvm/test/MachineVerifier/X86/block-args-duplicate-succ-args.mir b/llvm/test/MachineVerifier/X86/block-args-duplicate-succ-args.mir
new file mode 100644
index 0000000000000..485ec9441c91c
--- /dev/null
+++ b/llvm/test/MachineVerifier/X86/block-args-duplicate-succ-args.mir
@@ -0,0 +1,20 @@
+# RUN: not --crash llc -mtriple=x86_64-- -run-pass=none -filetype=null %s 2>&1 | FileCheck %s
+
+# A predecessor must supply at most one SUCC_ARGS per successor.
+# CHECK: *** Bad machine code: Multiple SUCC_ARGS for the same successor ***
+---
+name: duplicate_succ_args
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ bb.0:
+ successors: %bb.1
+ %0:gr32 = MOV32ri 1
+ SUCC_ARGS %bb.1, %0
+ SUCC_ARGS %bb.1, %0
+ JMP_1 %bb.1
+
+ bb.1:
+ arguments: %1:gr32
+ RET64 %1
+...
diff --git a/llvm/test/MachineVerifier/X86/block-args-loop-missing-backedge.mir b/llvm/test/MachineVerifier/X86/block-args-loop-missing-backedge.mir
new file mode 100644
index 0000000000000..945ca5480d117
--- /dev/null
+++ b/llvm/test/MachineVerifier/X86/block-args-loop-missing-backedge.mir
@@ -0,0 +1,26 @@
+# RUN: not --crash llc -mtriple=x86_64-- -run-pass=none -filetype=null %s 2>&1 | FileCheck %s
+
+# A loop header with arguments must be fed by every predecessor, including the
+# latch on the back-edge. Here the latch (bb.1) omits its SUCC_ARGS.
+# CHECK: *** Bad machine code: Missing SUCC_ARGS for a block with arguments ***
+---
+name: loop_missing_backedge
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ bb.0:
+ successors: %bb.1
+ %0:gr32 = MOV32ri 0
+ SUCC_ARGS %bb.1, %0
+ JMP_1 %bb.1
+
+ bb.1:
+ successors: %bb.1, %bb.2
+ arguments: %1:gr32
+ ; Back-edge to bb.1 provides no SUCC_ARGS for the argument.
+ JCC_1 %bb.1, 4, implicit undef $eflags
+ JMP_1 %bb.2
+
+ bb.2:
+ RET64 %1
+...
diff --git a/llvm/test/MachineVerifier/X86/block-args-missing-succ-args.mir b/llvm/test/MachineVerifier/X86/block-args-missing-succ-args.mir
new file mode 100644
index 0000000000000..a3d0b639b6b53
--- /dev/null
+++ b/llvm/test/MachineVerifier/X86/block-args-missing-succ-args.mir
@@ -0,0 +1,17 @@
+# RUN: not --crash llc -mtriple=x86_64-- -run-pass=none -filetype=null %s 2>&1 | FileCheck %s
+
+# A predecessor of a block with arguments must supply a SUCC_ARGS for it.
+# CHECK: *** Bad machine code: Missing SUCC_ARGS for a block with arguments ***
+---
+name: missing_succ_args
+tracksRegLiveness: true
+usesBlockArgs: true
+body: |
+ bb.0:
+ successors: %bb.1
+ JMP_1 %bb.1
+
+ bb.1:
+ arguments: %0:gr32
+ RET64 %0
+...
diff --git a/llvm/test/MachineVerifier/X86/block-args-physreg.mir b/llvm/test/MachineVerifier/X86/block-args-physreg.mir
new file mode 100644
index 0000000000000..1e2f1d1c563e9
--- /dev/null
+++ b/llvm/test/MachineVerifier/X86/block-args-physreg.mir
@@ -0,0 +1,13 @@
+# RUN: not --crash llc -mtriple=x86_64-- -run-pass=none -filetype=null %s 2>&1 | FileCheck %s
+
+# A block argument must be a virtual register, not a physical one.
+# CHECK: *** Bad machine code: Block argument must be a virtual register ***
+# CHECK-NEXT: - function: physreg_block_arg
+---
+name: physreg_block_arg
+tracksRegLiveness: true
+body: |
+ bb.0:
+ arguments: $eax
+ RET64 $eax
+...
diff --git a/llvm/test/MachineVerifier/X86/block-args-undef-use.mir b/llvm/test/MachineVerifier/X86/block-args-undef-use.mir
new file mode 100644
index 0000000000000..b668d8b23d9db
--- /dev/null
+++ b/llvm/test/MachineVerifier/X86/block-args-undef-use.mir
@@ -0,0 +1,14 @@
+# RUN: not --crash llc -mtriple=x86_64-- -run-pass=none -filetype=null %s 2>&1 | FileCheck %s
+
+# A virtual register that is used but never defined, and is not a block
+# argument, must still be reported. This guards that the block-argument def
+# exemption is narrow.
+# CHECK: *** Bad machine code: Reading virtual register without a def ***
+# CHECK-NEXT: - function: no_def_errors
+---
+name: no_def_errors
+tracksRegLiveness: true
+body: |
+ bb.0:
+ RET64 %0:gr32
+...
More information about the llvm-branch-commits
mailing list