Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions llvm/docs/MIRLangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,23 @@ For an int eq predicate ``ICMP_EQ``, the syntax is:

%2:gpr(s32) = G_ICMP intpred(eq), %0, %1

Lanemask Operands
^^^^^^^^^^^^^^^^^^

A Lanemask operand is a 64-bit unsigned value that can store lane information
for a register operand in the instruction. It can be used as many times as needed
in an instruction, with one or more register operands associated with it. While
the active bits represent the live subregister (in virtual registers) or regUnits
(in physical registers), the remaining bits can represent the UNDEF part of it.


For example, the COPY_LANEMASK instruction uses this operand to copy only active
lanes (of the source register) in the mask. The syntax for it would look like:

.. code-block:: text

$vgpr1 = COPY_LANEMASK $vgpr0, lanemask(0x00000000000000C0)

.. TODO: Describe the parsers default behaviour when optional YAML attributes
are missing.
.. TODO: Describe the syntax for virtual register YAML definitions.
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/MachineInstr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,10 @@ class MachineInstr
return getOpcode() == TargetOpcode::COPY;
}

bool isCopyLanemask() const {
return getOpcode() == TargetOpcode::COPY_LANEMASK;
}

bool isFullCopy() const {
return isCopy() && !getOperand(0).getSubReg() && !getOperand(1).getSubReg();
}
Expand Down Expand Up @@ -1465,6 +1469,7 @@ class MachineInstr
case TargetOpcode::PHI:
case TargetOpcode::G_PHI:
case TargetOpcode::COPY:
case TargetOpcode::COPY_LANEMASK:
case TargetOpcode::INSERT_SUBREG:
case TargetOpcode::SUBREG_TO_REG:
case TargetOpcode::REG_SEQUENCE:
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/MachineInstrBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ class MachineInstrBuilder {
return *this;
}

const MachineInstrBuilder &addLaneMask(LaneBitmask LaneMask) const {
MI->addOperand(*MF, MachineOperand::CreateLaneMask(LaneMask));
return *this;
}

const MachineInstrBuilder &addSym(MCSymbol *Sym,
unsigned char TargetFlags = 0) const {
MI->addOperand(*MF, MachineOperand::CreateMCSymbol(Sym, TargetFlags));
Expand Down
17 changes: 16 additions & 1 deletion llvm/include/llvm/CodeGen/MachineOperand.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/MC/LaneBitmask.h"
#include "llvm/Support/Compiler.h"
#include <cassert>

Expand Down Expand Up @@ -69,7 +70,8 @@ class MachineOperand {
MO_Predicate, ///< Generic predicate for ISel
MO_ShuffleMask, ///< Other IR Constant for ISel (shuffle masks)
MO_DbgInstrRef, ///< Integer indices referring to an instruction+operand
MO_Last = MO_DbgInstrRef
MO_LaneMask, ///< Mask to represent active parts of registers
MO_Last = MO_LaneMask
};

private:
Expand Down Expand Up @@ -178,6 +180,7 @@ class MachineOperand {
Intrinsic::ID IntrinsicID; // For MO_IntrinsicID.
unsigned Pred; // For MO_Predicate
ArrayRef<int> ShuffleMask; // For MO_ShuffleMask
LaneBitmask LaneMask; // For MO_LaneMask

struct { // For MO_Register.
// Register number is in SmallContents.RegNo.
Expand Down Expand Up @@ -360,6 +363,7 @@ class MachineOperand {
bool isIntrinsicID() const { return OpKind == MO_IntrinsicID; }
bool isPredicate() const { return OpKind == MO_Predicate; }
bool isShuffleMask() const { return OpKind == MO_ShuffleMask; }
bool isLaneMask() const { return OpKind == MO_LaneMask; }
//===--------------------------------------------------------------------===//
// Accessors for Register Operands
//===--------------------------------------------------------------------===//
Expand Down Expand Up @@ -624,6 +628,11 @@ class MachineOperand {
return Contents.ShuffleMask;
}

LaneBitmask getLaneMask() const {
assert(isLaneMask() && "Wrong MachineOperand accessor");
return Contents.LaneMask;
}

/// Return the offset from the symbol in this operand. This always returns 0
/// for ExternalSymbol operands.
int64_t getOffset() const {
Expand Down Expand Up @@ -989,6 +998,12 @@ class MachineOperand {
return Op;
}

static MachineOperand CreateLaneMask(LaneBitmask LaneMask) {
MachineOperand Op(MachineOperand::MO_LaneMask);
Op.Contents.LaneMask = LaneMask;
return Op;
}

friend class MachineInstr;
friend class MachineRegisterInfo;

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/MC/LaneBitmask.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ namespace llvm {
constexpr bool operator== (LaneBitmask M) const { return Mask == M.Mask; }
constexpr bool operator!= (LaneBitmask M) const { return Mask != M.Mask; }
constexpr bool operator< (LaneBitmask M) const { return Mask < M.Mask; }
constexpr bool operator<=(LaneBitmask M) const { return Mask <= M.Mask; }
constexpr bool none() const { return Mask == 0; }
constexpr bool any() const { return Mask != 0; }
constexpr bool all() const { return ~Mask == 0; }
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/Support/TargetOpcodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ HANDLE_TARGET_OPCODE(REG_SEQUENCE)
/// used to copy between subregisters of virtual registers.
HANDLE_TARGET_OPCODE(COPY)

/// COPY_LANEMASK - Target-independent register copy for active mask in
/// register as represented by the lanemask. This instruction does not
/// support copy between subregisters of virtual registers.
HANDLE_TARGET_OPCODE(COPY_LANEMASK)

/// BUNDLE - This instruction represents an instruction bundle. Instructions
/// which immediately follow a BUNDLE instruction which are marked with
/// 'InsideBundle' flag are inside the bundle.
Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/Target/Target.td
Original file line number Diff line number Diff line change
Expand Up @@ -1330,6 +1330,13 @@ def COPY : StandardPseudoInstruction {
let isAsCheapAsAMove = true;
let hasNoSchedulingInfo = false;
}
def COPY_LANEMASK : StandardPseudoInstruction {
let OutOperandList = (outs unknown:$dst);
let InOperandList = (ins unknown:$src, unknown:$lanemask);
let AsmString = "";
let hasSideEffects = false;
let isAsCheapAsAMove = true;
}
def BUNDLE : StandardPseudoInstruction {
let OutOperandList = (outs);
let InOperandList = (ins variable_ops);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/MIRParser/MILexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) {
.Case("constant-pool", MIToken::kw_constant_pool)
.Case("call-entry", MIToken::kw_call_entry)
.Case("custom", MIToken::kw_custom)
.Case("lanemask", MIToken::kw_lanemask)
.Case("liveout", MIToken::kw_liveout)
.Case("landing-pad", MIToken::kw_landing_pad)
.Case("inlineasm-br-indirect-target",
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/MIRParser/MILexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ struct MIToken {
kw_constant_pool,
kw_call_entry,
kw_custom,
kw_lanemask,
kw_liveout,
kw_landing_pad,
kw_inlineasm_br_indirect_target,
Expand Down
29 changes: 29 additions & 0 deletions llvm/lib/CodeGen/MIRParser/MIParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ class MIParser {
bool parseTargetIndexOperand(MachineOperand &Dest);
bool parseDbgInstrRefOperand(MachineOperand &Dest);
bool parseCustomRegisterMaskOperand(MachineOperand &Dest);
bool parseLaneMaskOperand(MachineOperand &Dest);
bool parseLiveoutRegisterMaskOperand(MachineOperand &Dest);
bool parseMachineOperand(const unsigned OpCode, const unsigned OpIdx,
MachineOperand &Dest,
Expand Down Expand Up @@ -2870,6 +2871,32 @@ bool MIParser::parseCustomRegisterMaskOperand(MachineOperand &Dest) {
return false;
}

bool MIParser::parseLaneMaskOperand(MachineOperand &Dest) {
assert(Token.is(MIToken::kw_lanemask));

lex();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should get MIR print/parse tests and cover the error cases too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the same as you did for MO_ShuffleMask, but don't know with which instruction it should go with( not associated yet to any kind of MI), in order to write test?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could introduce the COPY_LANEMASK instruction here I suppose

Copy link
Contributor Author

@vg0204 vg0204 Aug 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be a normal (generic) COPY, with additional machineOperand as lanemask. Any other specs I need to know about it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be a new opcode, not COPY but yes. It should also forbid subregister operands

if (expectAndConsume(MIToken::lparen))
return error("lanemask should begin with '('.");

LaneBitmask LaneMask = LaneBitmask::getAll();
// Parse lanemask.
if (Token.isNot(MIToken::IntegerLiteral) && Token.isNot(MIToken::HexLiteral))
return error("expected a valid lane mask value.");
static_assert(sizeof(LaneBitmask::Type) == sizeof(uint64_t),
"Use correct get-function for lane mask.");
LaneBitmask::Type V;
if (getUint64(V))
return error("invalid lanemask value");
LaneMask = LaneBitmask(V);
lex();

if (expectAndConsume(MIToken::rparen))
return error("lanemask should be terminated by ')'.");

Dest = MachineOperand::CreateLaneMask(LaneMask);
return false;
}

bool MIParser::parseLiveoutRegisterMaskOperand(MachineOperand &Dest) {
assert(Token.is(MIToken::kw_liveout));
uint32_t *Mask = MF.allocateRegMask();
Expand Down Expand Up @@ -2970,6 +2997,8 @@ bool MIParser::parseMachineOperand(const unsigned OpCode, const unsigned OpIdx,
return parseIntrinsicOperand(Dest);
case MIToken::kw_target_index:
return parseTargetIndexOperand(Dest);
case MIToken::kw_lanemask:
return parseLaneMaskOperand(Dest);
case MIToken::kw_liveout:
return parseLiveoutRegisterMaskOperand(Dest);
case MIToken::kw_floatpred:
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/MIRPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,8 @@ static void printMIOperand(raw_ostream &OS, MFPrintState &State,
case MachineOperand::MO_Predicate:
case MachineOperand::MO_BlockAddress:
case MachineOperand::MO_DbgInstrRef:
case MachineOperand::MO_ShuffleMask: {
case MachineOperand::MO_ShuffleMask:
case MachineOperand::MO_LaneMask: {
unsigned TiedOperandIdx = 0;
if (ShouldPrintRegisterTies && Op.isReg() && Op.isTied() && !Op.isDef())
TiedOperandIdx = Op.getParent()->findTiedOperandIdx(OpIdx);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/MIRVRegNamerUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ std::string VRegRenamer::getInstructionOpcodeHash(MachineInstr &MI) {
case MachineOperand::MO_ExternalSymbol:
case MachineOperand::MO_GlobalAddress:
case MachineOperand::MO_BlockAddress:
case MachineOperand::MO_LaneMask:
case MachineOperand::MO_RegisterMask:
case MachineOperand::MO_RegisterLiveOut:
case MachineOperand::MO_Metadata:
Expand Down
19 changes: 16 additions & 3 deletions llvm/lib/CodeGen/MachineOperand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,8 @@ bool MachineOperand::isIdenticalTo(const MachineOperand &Other) const {
return getPredicate() == Other.getPredicate();
case MachineOperand::MO_ShuffleMask:
return getShuffleMask() == Other.getShuffleMask();
case MachineOperand::MO_LaneMask:
return getLaneMask() == Other.getLaneMask();
}
llvm_unreachable("Invalid machine operand type");
}
Expand Down Expand Up @@ -445,6 +447,9 @@ hash_code llvm::hash_value(const MachineOperand &MO) {
return hash_combine(MO.getType(), MO.getTargetFlags(), MO.getPredicate());
case MachineOperand::MO_ShuffleMask:
return hash_combine(MO.getType(), MO.getTargetFlags(), MO.getShuffleMask());
case MachineOperand::MO_LaneMask:
return hash_combine(MO.getType(), MO.getTargetFlags(),
MO.getLaneMask().getAsInteger());
}
llvm_unreachable("Invalid machine operand type");
}
Expand Down Expand Up @@ -1004,11 +1009,11 @@ void MachineOperand::print(raw_ostream &OS, ModuleSlotTracker &MST,
}
case MachineOperand::MO_Predicate: {
auto Pred = static_cast<CmpInst::Predicate>(getPredicate());
OS << (CmpInst::isIntPredicate(Pred) ? "int" : "float") << "pred("
<< Pred << ')';
OS << (CmpInst::isIntPredicate(Pred) ? "int" : "float") << "pred(" << Pred
<< ')';
break;
}
case MachineOperand::MO_ShuffleMask:
case MachineOperand::MO_ShuffleMask: {
OS << "shufflemask(";
ArrayRef<int> Mask = getShuffleMask();
StringRef Separator;
Expand All @@ -1023,6 +1028,14 @@ void MachineOperand::print(raw_ostream &OS, ModuleSlotTracker &MST,
OS << ')';
break;
}
case MachineOperand::MO_LaneMask: {
OS << "lanemask(";
LaneBitmask LaneMask = getLaneMask();
OS << "0x" << PrintLaneMask(LaneMask);
OS << ')';
break;
}
}
}

#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/CodeGen/MachineStableHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ stable_hash llvm::stableHashValue(const MachineOperand &MO) {
return stable_hash_combine(MO.getType(), MO.getTargetFlags(),
stable_hash_name(SymbolName));
}
case MachineOperand::MO_LaneMask: {
return stable_hash_combine(MO.getType(), MO.getTargetFlags(),
MO.getLaneMask().getAsInteger());
}
case MachineOperand::MO_CFIIndex:
return stable_hash_combine(MO.getType(), MO.getTargetFlags(),
MO.getCFIIndex());
Expand Down
32 changes: 32 additions & 0 deletions llvm/lib/CodeGen/MachineVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2414,6 +2414,38 @@ void MachineVerifier::visitMachineInstrBefore(const MachineInstr *MI) {
}
break;
}
case TargetOpcode::COPY_LANEMASK: {
const MachineOperand &DstOp = MI->getOperand(0);
const MachineOperand &SrcOp = MI->getOperand(1);
const MachineOperand &LaneMaskOp = MI->getOperand(2);
const Register SrcReg = SrcOp.getReg();
const LaneBitmask LaneMask = LaneMaskOp.getLaneMask();
LaneBitmask SrcMaxLanemask = LaneBitmask::getAll();

if (DstOp.getSubReg())
report("COPY_LANEMASK must not use a subregister index", &DstOp, 0);

if (SrcOp.getSubReg())
report("COPY_LANEMASK must not use a subregister index", &SrcOp, 1);

if (LaneMask.none())
report("COPY_LANEMASK must read at least one lane", MI);

if (SrcReg.isPhysical()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description in TargetOpcodes.def says this is a copy between physical registers, but this code suggests vregs are also allowed. Can you please clarify?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out, need to update that. As when started that time made assumption for only PhysReg which eventually got changed to support both virtReg & PhysReg by COPY_LANEMASK opcode

const TargetRegisterClass *SrcRC = TRI->getMinimalPhysRegClass(SrcReg);
if (SrcRC)
SrcMaxLanemask = SrcRC->getLaneMask();
} else {
SrcMaxLanemask = MRI->getMaxLaneMaskForVReg(SrcReg);
}

// If LaneMask is equal to OR greater than the SrcMaxLanemask, it
// impliess COPY_LANEMASK is trying to copy all lanes.
if (SrcMaxLanemask <= LaneMask)
report("COPY_LANEMASK cannot read all lanes", MI);

break;
}
case TargetOpcode::STATEPOINT: {
StatepointOpers SO(MI);
if (!MI->getOperand(SO.getIDPos()).isImm() ||
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,7 @@ static bool IsAnAddressOperand(const MachineOperand &MO) {
return true;
case MachineOperand::MO_RegisterMask:
case MachineOperand::MO_RegisterLiveOut:
case MachineOperand::MO_LaneMask:
return false;
case MachineOperand::MO_Metadata:
case MachineOperand::MO_MCSymbol:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
#
# DEBUG-NEXT: G_ABDS (opcode 65): 1 type index, 0 imm indices
# DEBUG-NEXT: G_ABDS (opcode 66): 1 type index, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
#
# DEBUG-NEXT: G_ABDU (opcode 66): 1 type index, 0 imm indices
# DEBUG-NEXT: G_ABDU (opcode 67): 1 type index, 0 imm indices
# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
Expand Down
13 changes: 13 additions & 0 deletions llvm/test/CodeGen/MIR/AMDGPU/parse-lanemask-operand-invalid-0.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# RUN: not llc -mtriple=amdgcn-amd-amdhsa -run-pass=none -filetype=null %s 2>&1 | FileCheck %s

---
name: test_missing_rparen
tracksRegLiveness: true
body: |
bb.0:
liveins: $vgpr0

; CHECK: [[@LINE+1]]:47: lanemask should be terminated by ')'.
$vgpr1 = COPY_LANEMASK $vgpr0, lanemask(16
S_ENDPGM 0
...
13 changes: 13 additions & 0 deletions llvm/test/CodeGen/MIR/AMDGPU/parse-lanemask-operand-invalid-1.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# RUN: not llc -mtriple=amdgcn-amd-amdhsa -run-pass=none -filetype=null %s 2>&1 | FileCheck %s

---
name: test_missing_lparen
tracksRegLiveness: true
body: |
bb.0:
liveins: $vgpr0

; CHECK: [[@LINE+1]]:45: lanemask should begin with '('.
$vgpr1 = COPY_LANEMASK $vgpr0, lanemask 14)
S_ENDPGM 0
...
13 changes: 13 additions & 0 deletions llvm/test/CodeGen/MIR/AMDGPU/parse-lanemask-operand-invalid-2.mir
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# RUN: not llc -mtriple=amdgcn-amd-amdhsa -run-pass=none -filetype=null %s 2>&1 | FileCheck %s

---
name: test_wrong_lanemask_type
tracksRegLiveness: true
body: |
bb.0:
liveins: $vgpr0

; CHECK: [[@LINE+1]]:45: expected a valid lane mask value.
$vgpr1 = COPY_LANEMASK $vgpr0, lanemask(undef)
S_ENDPGM 0
...
Loading