Skip to content

[TableGen][GlobalISel] Add MIFlags matching & rewriting #71179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
40 changes: 40 additions & 0 deletions llvm/docs/GlobalISel/MIRPatterns.rst
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,46 @@ Semantics:
* The root cannot have any output operands.
* The root must be a CodeGenInstruction

Instruction Flags
-----------------

MIR Patterns support both matching & writing ``MIFlags``.
``MIFlags`` are never preserved; output instructions have never have
any flags unless explicitly set.

.. code-block:: text
:caption: Example

def Test : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src, (MIFlags FmNoNans, FmNoInfs))),
(apply (G_BAR $dst, $src, (MIFlags FmReassoc)))>;

In ``apply`` patterns, we also support referring to a matched instruction to
"take" its MIFlags.

.. code-block:: text
:caption: Example

; We match NoNans/NoInfs, but $zext may have more flags.
; Copy them all into the output instruction, and set Reassoc on the output inst.
def TestCpyFlags : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src, (MIFlags FmNoNans, FmNoInfs)):$zext),
(apply (G_BAR $dst, $src, (MIFlags $zext, FmReassoc)))>;

The ``not`` operator can be used to check that a flag is NOT present
on a matched instruction, and to remove a flag from a generated instruction.

.. code-block:: text
:caption: Example

; We match NoInfs but we don't want NoNans/Reassoc to be set. $zext may have more flags.
; Copy them all into the output instruction but remove NoInfs on the output inst.
def TestNot : GICombineRule<
(defs root:$dst),
(match (G_FOO $dst, $src, (MIFlags FmNoInfs, (not FmNoNans, FmReassoc))):$zext),
(apply (G_BAR $dst, $src, (MIFlags $zext, (not FmNoInfs))))>;

Limitations
-----------
Expand Down
21 changes: 21 additions & 0 deletions llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ enum {
/// - NewOpIdx
GIM_CheckCanReplaceReg,

/// Check that a matched instruction has, or doesn't have a MIFlag.
///
/// - InsnID - Instruction to check.
/// - Flag(s) - (can be one or more flags OR'd together)
GIM_MIFlags,
GIM_MIFlagsNot,

/// Predicates with 'let PredicateCodeUsesOperands = 1' need to examine some
/// named operands that will be recorded in RecordedOperands. Names of these
/// operands are referenced in predicate argument list. Emitter determines
Expand Down Expand Up @@ -344,6 +351,20 @@ enum {
/// OpIdx starts at 0 for the first implicit def.
GIR_SetImplicitDefDead,

/// Set or unset a MIFlag on an instruction.
///
/// - InsnID - Instruction to modify.
/// - Flag(s) - (can be one or more flags OR'd together)
GIR_SetMIFlags,
GIR_UnsetMIFlags,

/// Copy the MIFlags of a matched instruction into an
/// output instruction. The flags are OR'd together.
///
/// - InsnID - Instruction to modify.
/// - OldInsnID - Matched instruction to copy flags from.
GIR_CopyMIFlags,

/// Add a temporary register to the specified instruction
/// - InsnID - Instruction ID to modify
/// - TempRegID - The temporary register ID to add
Expand Down
61 changes: 59 additions & 2 deletions llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,6 @@ bool GIMatchTableExecutor::executeMatchTable(
if (Observer)
Observer->changedInstr(*MIB);
}

return true;
};

// If the index is >= 0, it's an index in the type objects generated by
Expand Down Expand Up @@ -919,6 +917,32 @@ bool GIMatchTableExecutor::executeMatchTable(
}
break;
}
case GIM_MIFlags: {
int64_t InsnID = MatchTable[CurrentIdx++];
uint32_t Flags = (uint32_t)MatchTable[CurrentIdx++];

DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": GIM_MIFlags(MIs[" << InsnID
<< "], " << Flags << ")\n");
if ((State.MIs[InsnID]->getFlags() & Flags) != Flags) {
if (handleReject() == RejectAndGiveUp)
return false;
}
break;
}
case GIM_MIFlagsNot: {
int64_t InsnID = MatchTable[CurrentIdx++];
uint32_t Flags = (uint32_t)MatchTable[CurrentIdx++];

DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": GIM_MIFlagsNot(MIs[" << InsnID
<< "], " << Flags << ")\n");
if ((State.MIs[InsnID]->getFlags() & Flags)) {
if (handleReject() == RejectAndGiveUp)
return false;
}
break;
}
case GIM_Reject:
DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": GIM_Reject\n");
Expand Down Expand Up @@ -1062,6 +1086,39 @@ bool GIMatchTableExecutor::executeMatchTable(
MI->getOperand(MI->getNumExplicitOperands() + OpIdx).setIsDead();
break;
}
case GIR_SetMIFlags: {
int64_t InsnID = MatchTable[CurrentIdx++];
uint32_t Flags = (uint32_t)MatchTable[CurrentIdx++];

DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": GIR_SetMIFlags(OutMIs["
<< InsnID << "], " << Flags << ")\n");
MachineInstr *MI = OutMIs[InsnID];
MI->setFlags(MI->getFlags() | Flags);
break;
}
case GIR_UnsetMIFlags: {
int64_t InsnID = MatchTable[CurrentIdx++];
uint32_t Flags = (uint32_t)MatchTable[CurrentIdx++];

DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": GIR_UnsetMIFlags(OutMIs["
<< InsnID << "], " << Flags << ")\n");
MachineInstr *MI = OutMIs[InsnID];
MI->setFlags(MI->getFlags() & ~Flags);
break;
}
case GIR_CopyMIFlags: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t OldInsnID = MatchTable[CurrentIdx++];

DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": GIR_CopyMIFlags(OutMIs["
<< InsnID << "], MIs[" << OldInsnID << "])\n");
MachineInstr *MI = OutMIs[InsnID];
MI->setFlags(MI->getFlags() | State.MIs[OldInsnID]->getFlags());
break;
}
case GIR_AddTempRegister:
case GIR_AddTempSubRegister: {
int64_t InsnID = MatchTable[CurrentIdx++];
Expand Down
19 changes: 19 additions & 0 deletions llvm/include/llvm/Target/GlobalISel/Combine.td
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,25 @@ def GIReplaceReg : GIBuiltinInst;
// TODO: Allow using this directly, like (apply GIEraseRoot)
def GIEraseRoot : GIBuiltinInst;

//===----------------------------------------------------------------------===//
// Pattern MIFlags
//===----------------------------------------------------------------------===//

class MIFlagEnum<string enumName> {
string EnumName = "MachineInstr::" # enumName;
}

def FmNoNans : MIFlagEnum<"FmNoNans">;
def FmNoInfs : MIFlagEnum<"FmNoInfs">;
def FmNsz : MIFlagEnum<"FmNsz">;
def FmArcp : MIFlagEnum<"FmArcp">;
def FmContract : MIFlagEnum<"FmContract">;
def FmAfn : MIFlagEnum<"FmAfn">;
def FmReassoc : MIFlagEnum<"FmReassoc">;

def MIFlags;
// def not; -> Already defined as a SDNode

//===----------------------------------------------------------------------===//

def extending_load_matchdata : GIDefMatchData<"PreferredTuple">;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: llvm-tblgen -I %p/../../../include -gen-global-isel-combiner \
// RUN: -combiners=MyCombiner %s | \
// RUN: FileCheck %s

include "llvm/Target/Target.td"
include "llvm/Target/GlobalISel/Combine.td"

def MyTargetISA : InstrInfo;
def MyTarget : Target { let InstructionSet = MyTargetISA; }

def MIFlagsTest : GICombineRule<
(defs root:$dst),
(match (G_SEXT $dst, $tmp), (G_ZEXT $tmp, $src, (MIFlags FmReassoc, FmNsz, (not FmNoNans, FmArcp))):$mi),
(apply (G_MUL $dst, $src, $src, (MIFlags $mi, FmReassoc, (not FmNsz, FmArcp))))>;

def MyCombiner: GICombiner<"GenMyCombiner", [MIFlagsTest]>;

// CHECK: const int64_t *GenMyCombiner::getMatchTable() const {
// CHECK-NEXT: constexpr static int64_t MatchTable0[] = {
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ 49, // Rule ID 0 //
// CHECK-NEXT: GIM_CheckSimplePredicate, GICXXPred_Simple_IsRule0Enabled,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SEXT,
// CHECK-NEXT: // MIs[0] dst
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: // MIs[0] tmp
// CHECK-NEXT: GIM_RecordInsnIgnoreCopies, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_ZEXT,
// CHECK-NEXT: GIM_MIFlags, /*MI*/1, MachineInstr::FmNsz | MachineInstr::FmReassoc,
// CHECK-NEXT: GIM_MIFlagsNot, /*MI*/1, MachineInstr::FmArcp | MachineInstr::FmNoNans,
// CHECK-NEXT: // MIs[1] src
// CHECK-NEXT: // No operand predicates
// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
// CHECK-NEXT: // Combiner Rule #0: MIFlagsTest
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::G_MUL,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src
// CHECK-NEXT: GIR_CopyMIFlags, /*InsnID*/0, /*OldInsnID*/1,
// CHECK-NEXT: GIR_SetMIFlags, /*InsnID*/0, MachineInstr::FmReassoc,
// CHECK-NEXT: GIR_UnsetMIFlags, /*InsnID*/0, MachineInstr::FmNsz | MachineInstr::FmArcp,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 0: @49
// CHECK-NEXT: GIM_Reject,
// CHECK-NEXT: };
// CHECK-NEXT: return MatchTable0;
// CHECK-NEXT: }
17 changes: 16 additions & 1 deletion llvm/test/TableGen/GlobalISelCombinerEmitter/patfrag-errors.td
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,19 @@ def root_def_has_multi_defs : GICombineRule<
(match (RootDefHasMultiDefs $root, (i32 10))),
(apply (COPY $root, (i32 0)))>;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: matching/writing MIFlags is only allowed on CodeGenInstruction patterns
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(DummyCXXPF ?:$x, (MIFlags FmArcp))'
def miflags_in_pf : GICombineRule<
(defs root:$x),
(match (COPY $x, $y), (DummyCXXPF $x, (MIFlags FmArcp))),
(apply (COPY $x, $y))>;

// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: '$pf' does not refer to a CodeGenInstruction in MIFlags of '__badtype_for_flagref_in_apply_apply_0'
def badtype_for_flagref_in_apply : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src), (DummyCXXPF $src):$pf),
(apply (G_MUL $dst, $src, $src, (MIFlags $pf)))>;

// CHECK: error: Failed to parse one or more rules

def MyCombiner: GICombiner<"GenMyCombiner", [
Expand All @@ -293,5 +306,7 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
patfrag_in_apply,
patfrag_cannot_be_root,
inconsistent_arg_type,
root_def_has_multi_defs
root_def_has_multi_defs,
miflags_in_pf,
badtype_for_flagref_in_apply
]>;
52 changes: 51 additions & 1 deletion llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-errors.td
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,50 @@ def def_named_imm_apply : GICombineRule<
(apply (COPY i32:$tmp, $y),
(COPY $x, (i32 0):$tmp):$foo)>;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: MIFlags can only be present once on an instruction
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(G_ZEXT ?:$dst, ?:$src, (MIFlags FmArcp), (MIFlags FmArcp))'
def multi_miflags : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src, (MIFlags FmArcp), (MIFlags FmArcp)):$mi),
(apply (G_MUL $dst, $src, $src))>;

def NotAMIFlagEnum;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: 'NotAMIFlagEnum' is not a subclass of 'MIFlagEnum'
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(G_ZEXT ?:$dst, ?:$src, (MIFlags NotAMIFlagEnum))'
def not_miflagenum_1 : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src, (MIFlags NotAMIFlagEnum)):$mi),
(apply (G_MUL $dst, $src, $src))>;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: 'NotAMIFlagEnum' is not a subclass of 'MIFlagEnum'
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(G_ZEXT ?:$dst, ?:$src, (MIFlags (not NotAMIFlagEnum)))'
def not_miflagenum_2 : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src, (MIFlags (not NotAMIFlagEnum))):$mi),

(apply (G_MUL $dst, $src, $src))>;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: matching/writing MIFlags is only allowed on CodeGenInstruction patterns
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceReg ?:$x, ?:$y, (MIFlags FmArcp))'
def miflags_in_builtin : GICombineRule<
(defs root:$x),
(match (COPY $x, $y)),
(apply (GIReplaceReg $x, $y, (MIFlags FmArcp)))>;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: 'match' patterns cannot refer to flags from other instructions
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: MIFlags in 'mi' refer to: impostor
def using_flagref_in_match : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src, (MIFlags $impostor)):$mi),
(apply (G_MUL $dst, $src, $src))>;

// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: unknown instruction '$impostor' referenced in MIFlags of '__badflagref_in_apply_apply_0'
def badflagref_in_apply : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src):$mi),
(apply (G_MUL $dst, $src, $src, (MIFlags $impostor)))>;

// CHECK: error: Failed to parse one or more rules

def MyCombiner: GICombiner<"GenMyCombiner", [
Expand Down Expand Up @@ -251,5 +295,11 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
bad_mo_type_not_a_valuetype,
untyped_new_reg_in_apply,
def_named_imm_match,
def_named_imm_apply
def_named_imm_apply,
multi_miflags,
not_miflagenum_1,
not_miflagenum_2,
miflags_in_builtin,
using_flagref_in_match,
badflagref_in_apply
]>;
25 changes: 24 additions & 1 deletion llvm/test/TableGen/GlobalISelCombinerEmitter/pattern-parsing.td
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,28 @@ def TypeOfTest : GICombineRule<
(G_ZEXT $tmp, $src)),
(apply (G_MUL $dst, (GITypeOf<"$src"> 0), (GITypeOf<"$dst"> -1)))>;


// CHECK: (CombineRule name:MIFlagsTest id:11 root:dst
// CHECK-NEXT: (MatchPats
// CHECK-NEXT: <match_root>mi:(CodeGenInstructionPattern G_ZEXT operands:[<def>$dst, $src] (MIFlags (set MachineInstr::FmReassoc) (unset MachineInstr::FmNoNans, MachineInstr::FmArcp)))
// CHECK-NEXT: )
// CHECK-NEXT: (ApplyPats
// CHECK-NEXT: <apply_root>__MIFlagsTest_apply_0:(CodeGenInstructionPattern G_MUL operands:[<def>$dst, $src, $src] (MIFlags (set MachineInstr::FmReassoc) (unset MachineInstr::FmNsz, MachineInstr::FmArcp) (copy mi)))
// CHECK-NEXT: )
// CHECK-NEXT: (OperandTable MatchPats
// CHECK-NEXT: dst -> mi
// CHECK-NEXT: src -> <live-in>
// CHECK-NEXT: )
// CHECK-NEXT: (OperandTable ApplyPats
// CHECK-NEXT: dst -> __MIFlagsTest_apply_0
// CHECK-NEXT: src -> <live-in>
// CHECK-NEXT: )
// CHECK-NEXT: )
def MIFlagsTest : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src, (MIFlags FmReassoc, (not FmNoNans, FmArcp))):$mi),
(apply (G_MUL $dst, $src, $src, (MIFlags $mi, FmReassoc, (not FmNsz, FmArcp))))>;

def MyCombiner: GICombiner<"GenMyCombiner", [
WipOpcodeTest0,
WipOpcodeTest1,
Expand All @@ -331,5 +353,6 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
PatFragTest1,
VariadicsInTest,
VariadicsOutTest,
TypeOfTest
TypeOfTest,
MIFlagsTest
]>;
Loading