Skip to content

[GlobalISel] Add GITypeOf special type #66079

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
Oct 31, 2023
Merged
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
42 changes: 42 additions & 0 deletions llvm/docs/GlobalISel/MIRPatterns.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,48 @@ pattern, you can try naming your patterns to see exactly where the issue is.
// using $x again here copies operand 1 from G_AND into the new inst.
(apply (COPY $root, $x))

Types
-----

ValueType
~~~~~~~~~

Subclasses of ``ValueType`` are valid types, e.g. ``i32``.

GITypeOf
~~~~~~~~

``GITypeOf<"$x">`` is a ``GISpecialType`` that allows for the creation of a
register or immediate with the same type as another (register) operand.

Operand:

* An operand name as a string, prefixed by ``$``.

Semantics:

* Can only appear in an 'apply' pattern.
* The operand name used must appear in the 'match' pattern of the
same ``GICombineRule``.

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

def mul_by_neg_one: GICombineRule <
(defs root:$root),
(match (G_MUL $dst, $x, -1)),
(apply (G_SUB $dst, (GITypeOf<"$x"> 0), $x))
>;

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

def Test0 : GICombineRule<
(defs root:$dst),
(match (G_FMUL $dst, $src, -1)),
(apply (G_FSUB $dst, $src, $tmp),
(G_FNEG GITypeOf<"$dst">:$tmp, $src))>;

Builtin Operations
------------------

Expand Down
3 changes: 0 additions & 3 deletions llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,9 +403,6 @@ class CombinerHelper {
void applyCombineTruncOfShift(MachineInstr &MI,
std::pair<MachineInstr *, LLT> &MatchInfo);

/// Transform G_MUL(x, -1) to G_SUB(0, x)
void applyCombineMulByNegativeOne(MachineInstr &MI);

/// Return true if any explicit use operand on \p MI is defined by a
/// G_IMPLICIT_DEF.
bool matchAnyExplicitUseIsUndef(MachineInstr &MI);
Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ enum {
/// - StoreIdx - Store location in RecordedOperands.
GIM_RecordNamedOperand,

/// Records an operand's register type into the set of temporary types.
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - TempTypeIdx - Temp Type Index, always negative.
GIM_RecordRegType,

/// Fail the current try-block, or completely fail to match if there is no
/// current try-block.
GIM_Reject,
Expand Down Expand Up @@ -522,6 +528,10 @@ class GIMatchTableExecutor {
/// list. Currently such predicates don't have more then 3 arguments.
std::array<const MachineOperand *, 3> RecordedOperands;

/// Types extracted from an instruction's operand.
/// Whenever a type index is negative, we look here instead.
SmallVector<LLT, 4> RecordedTypes;

MatcherState(unsigned MaxRenderers);
};

Expand Down
32 changes: 29 additions & 3 deletions llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ bool GIMatchTableExecutor::executeMatchTable(
return true;
};

// If the index is >= 0, it's an index in the type objects generated by
// TableGen. If the index is <0, it's an index in the recorded types object.
auto getTypeFromIdx = [&](int64_t Idx) -> LLT {
if (Idx >= 0)
return ExecInfo.TypeObjects[Idx];
return State.RecordedTypes[1 - Idx];
};

while (true) {
assert(CurrentIdx != ~0u && "Invalid MatchTable index");
int64_t MatcherOpcode = MatchTable[CurrentIdx++];
Expand Down Expand Up @@ -627,8 +635,7 @@ bool GIMatchTableExecutor::executeMatchTable(
<< "), TypeID=" << TypeID << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx);
if (!MO.isReg() ||
MRI.getType(MO.getReg()) != ExecInfo.TypeObjects[TypeID]) {
if (!MO.isReg() || MRI.getType(MO.getReg()) != getTypeFromIdx(TypeID)) {
if (handleReject() == RejectAndGiveUp)
return false;
}
Expand Down Expand Up @@ -679,6 +686,25 @@ bool GIMatchTableExecutor::executeMatchTable(
State.RecordedOperands[StoreIdx] = &State.MIs[InsnID]->getOperand(OpIdx);
break;
}
case GIM_RecordRegType: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t OpIdx = MatchTable[CurrentIdx++];
int64_t TypeIdx = MatchTable[CurrentIdx++];

DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": GIM_RecordRegType(MIs["
<< InsnID << "]->getOperand(" << OpIdx
<< "), TypeIdx=" << TypeIdx << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
assert(TypeIdx <= 0 && "Temp types always have negative indexes!");
// Indexes start at -1.
TypeIdx = 1 - TypeIdx;
const auto &Op = State.MIs[InsnID]->getOperand(OpIdx);
if (State.RecordedTypes.size() <= (uint64_t)TypeIdx)
State.RecordedTypes.resize(TypeIdx + 1, LLT());
State.RecordedTypes[TypeIdx] = MRI.getType(Op.getReg());
break;
}
case GIM_CheckRegBankForClass: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t OpIdx = MatchTable[CurrentIdx++];
Expand Down Expand Up @@ -1275,7 +1301,7 @@ bool GIMatchTableExecutor::executeMatchTable(
int64_t TypeID = MatchTable[CurrentIdx++];

State.TempRegisters[TempRegID] =
MRI.createGenericVirtualRegister(ExecInfo.TypeObjects[TypeID]);
MRI.createGenericVirtualRegister(getTypeFromIdx(TypeID));
DEBUG_WITH_TYPE(TgtExecutor::getName(),
dbgs() << CurrentIdx << ": TempRegs[" << TempRegID
<< "] = GIR_MakeTempReg(" << TypeID << ")\n");
Expand Down
25 changes: 21 additions & 4 deletions llvm/include/llvm/Target/GlobalISel/Combine.td
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,24 @@ class GICombinePatFrag<dag outs, dag ins, list<dag> alts> {
list<dag> Alternatives = alts;
}

//===----------------------------------------------------------------------===//
// Pattern Special Types
//===----------------------------------------------------------------------===//

class GISpecialType;

// In an apply pattern, GITypeOf can be used to set the type of a new temporary
// register to match the type of a matched register.
//
// This can only be used on temporary registers defined by the apply pattern.
//
// TODO: Make this work in matchers as well?
//
// FIXME: Syntax is very ugly.
class GITypeOf<string opName> : GISpecialType {
string OpName = opName;
}

//===----------------------------------------------------------------------===//
// Pattern Builtins
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -776,10 +794,9 @@ def trunc_shift: GICombineRule <

// Transform (mul x, -1) -> (sub 0, x)
def mul_by_neg_one: GICombineRule <
(defs root:$root),
(match (wip_match_opcode G_MUL):$root,
[{ return Helper.matchConstantOp(${root}->getOperand(2), -1); }]),
(apply [{ Helper.applyCombineMulByNegativeOne(*${root}); }])
(defs root:$dst),
(match (G_MUL $dst, $x, -1)),
(apply (G_SUB $dst, (GITypeOf<"$x"> 0), $x))
>;

// Fold (xor (and x, y), y) -> (and (not x), y)
Expand Down
12 changes: 0 additions & 12 deletions llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2224,18 +2224,6 @@ void CombinerHelper::applyCombineExtOfExt(
}
}

void CombinerHelper::applyCombineMulByNegativeOne(MachineInstr &MI) {
assert(MI.getOpcode() == TargetOpcode::G_MUL && "Expected a G_MUL");
Register DstReg = MI.getOperand(0).getReg();
Register SrcReg = MI.getOperand(1).getReg();
LLT DstTy = MRI.getType(DstReg);

Builder.setInstrAndDebugLoc(MI);
Builder.buildSub(DstReg, Builder.buildConstant(DstTy, 0), SrcReg,
MI.getFlags());
MI.eraseFromParent();
}

bool CombinerHelper::matchCombineTruncOfExt(
MachineInstr &MI, std::pair<Register, unsigned> &MatchInfo) {
assert(MI.getOpcode() == TargetOpcode::G_TRUNC && "Expected a G_TRUNC");
Expand Down
49 changes: 49 additions & 0 deletions llvm/test/TableGen/GlobalISelCombinerEmitter/match-table-typeof.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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 Test0 : GICombineRule<
(defs root:$dst),
(match (G_MUL $dst, $src, -1)),
(apply (G_SUB $dst, (GITypeOf<"$src"> 0), $tmp),
(G_CONSTANT GITypeOf<"$dst">:$tmp, (GITypeOf<"$src"> 42)))>;

// CHECK: const int64_t *GenMyCombiner::getMatchTable() const {
// CHECK-NEXT: constexpr static int64_t MatchTable0[] = {
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 0*/ 57, // Rule ID 0 //
// CHECK-NEXT: GIM_CheckSimplePredicate, GICXXPred_Simple_IsRule0Enabled,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_MUL,
// CHECK-NEXT: // MIs[0] dst
// CHECK-NEXT: GIM_RecordRegType, /*MI*/0, /*Op*/0, /*TempTypeIdx*/-1,
// CHECK-NEXT: // MIs[0] src
// CHECK-NEXT: GIM_RecordRegType, /*MI*/0, /*Op*/1, /*TempTypeIdx*/-2,
// CHECK-NEXT: // MIs[0] Operand 2
// CHECK-NEXT: GIM_CheckConstantInt, /*MI*/0, /*Op*/2, -1,
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/-2,
// CHECK-NEXT: GIR_BuildConstant, /*TempRegID*/1, /*Val*/0,
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/-1,
// CHECK-NEXT: // Combiner Rule #0: Test0
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/TargetOpcode::G_CONSTANT,
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0,
// CHECK-NEXT: GIR_AddCImm, /*InsnID*/0, /*Type*/-2, /*Imm*/42,
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::G_SUB,
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/1, /*TempRegFlags*/0,
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 0: @57
// CHECK-NEXT: GIM_Reject,
// CHECK-NEXT: };
// CHECK-NEXT: return MatchTable0;
// CHECK-NEXT: }

def MyCombiner: GICombiner<"GenMyCombiner", [
Test0
]>;
28 changes: 27 additions & 1 deletion llvm/test/TableGen/GlobalISelCombinerEmitter/operand-types.td
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,33 @@ def PatFragTest0 : GICombineRule<
(match (FooPF $dst)),
(apply (COPY $dst, (i32 0)))>;


// CHECK: (CombineRule name:TypeOfProp id:2 root:x
// CHECK-NEXT: (MatchPats
// CHECK-NEXT: <match_root>__TypeOfProp_match_0:(CodeGenInstructionPattern G_ZEXT operands:[<def>$x, $y])
// CHECK-NEXT: )
// CHECK-NEXT: (ApplyPats
// CHECK-NEXT: <apply_root>__TypeOfProp_apply_0:(CodeGenInstructionPattern G_ANYEXT operands:[<def>$x, GITypeOf<$y>:$tmp])
// CHECK-NEXT: __TypeOfProp_apply_1:(CodeGenInstructionPattern G_ANYEXT operands:[<def>GITypeOf<$y>:$tmp, $y])
// CHECK-NEXT: )
// CHECK-NEXT: (OperandTable MatchPats
// CHECK-NEXT: x -> __TypeOfProp_match_0
// CHECK-NEXT: y -> <live-in>
// CHECK-NEXT: )
// CHECK-NEXT: (OperandTable ApplyPats
// CHECK-NEXT: tmp -> __TypeOfProp_apply_1
// CHECK-NEXT: x -> __TypeOfProp_apply_0
// CHECK-NEXT: y -> <live-in>
// CHECK-NEXT: )
// CHECK-NEXT: )
def TypeOfProp : GICombineRule<
(defs root:$x),
(match (G_ZEXT $x, $y)),
(apply (G_ANYEXT $x, GITypeOf<"$y">:$tmp),
(G_ANYEXT $tmp, $y))>;

def MyCombiner: GICombiner<"GenMyCombiner", [
InstTest0,
PatFragTest0
PatFragTest0,
TypeOfProp
]>;
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 @@ -297,6 +297,28 @@ def VariadicsOutTest : GICombineRule<
(apply (COPY $a, (i32 0)),
(COPY $b, (i32 0)))>;

// CHECK: (CombineRule name:TypeOfTest id:10 root:dst
// CHECK-NEXT: (MatchPats
// CHECK-NEXT: <match_root>__TypeOfTest_match_0:(CodeGenInstructionPattern COPY operands:[<def>$dst, $tmp])
// CHECK-NEXT: __TypeOfTest_match_1:(CodeGenInstructionPattern G_ZEXT operands:[<def>$tmp, $src])
// CHECK-NEXT: )
// CHECK-NEXT: (ApplyPats
// CHECK-NEXT: <apply_root>__TypeOfTest_apply_0:(CodeGenInstructionPattern G_MUL operands:[<def>$dst, (GITypeOf<$src> 0), (GITypeOf<$dst> -1)])
// CHECK-NEXT: )
// CHECK-NEXT: (OperandTable MatchPats
// CHECK-NEXT: dst -> __TypeOfTest_match_0
// CHECK-NEXT: src -> <live-in>
// CHECK-NEXT: tmp -> __TypeOfTest_match_1
// CHECK-NEXT: )
// CHECK-NEXT: (OperandTable ApplyPats
// CHECK-NEXT: dst -> __TypeOfTest_apply_0
// CHECK-NEXT: )
// CHECK-NEXT: )
def TypeOfTest : GICombineRule<
(defs root:$dst),
(match (COPY $dst, $tmp),
(G_ZEXT $tmp, $src)),
(apply (G_MUL $dst, (GITypeOf<"$src"> 0), (GITypeOf<"$dst"> -1)))>;

def MyCombiner: GICombiner<"GenMyCombiner", [
WipOpcodeTest0,
Expand All @@ -308,5 +330,6 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
PatFragTest0,
PatFragTest1,
VariadicsInTest,
VariadicsOutTest
VariadicsOutTest,
TypeOfTest
]>;
72 changes: 72 additions & 0 deletions llvm/test/TableGen/GlobalISelCombinerEmitter/typeof-errors.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// RUN: not llvm-tblgen -I %p/../../../include -gen-global-isel-combiner \
// RUN: -combiners=MyCombiner %s 2>&1| \
// RUN: FileCheck %s -implicit-check-not=error:

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

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

// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: invalid operand name format 'unknown' in GITypeOf: expected '$' followed by an operand name
def NoDollarSign : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src)),
(apply (G_ANYEXT $dst, (GITypeOf<"unknown"> 0)))>;

// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'unknown' ('GITypeOf<$unknown>') does not refer to a matched operand!
def UnknownOperand : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src)),
(apply (G_ANYEXT $dst, (GITypeOf<"$unknown"> 0)))>;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: GISpecialType is not supported in 'match' patterns
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: operand 1 of '__UseInMatch_match_0' has type 'GITypeOf<$dst>'
def UseInMatch : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, (GITypeOf<"$dst"> 0))),
(apply (G_ANYEXT $dst, (i32 0)))>;

// CHECK: :[[@LINE+3]]:{{[0-9]+}}: error: GISpecialType is not supported in GICombinePatFrag
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: note: operand 1 of '__PFWithTypeOF_alt0_pattern_0' has type 'GITypeOf<$dst>
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Could not parse GICombinePatFrag 'PFWithTypeOF'
def PFWithTypeOF: GICombinePatFrag<
(outs $dst), (ins),
[(pattern (G_ANYEXT $dst, (GITypeOf<"$dst"> 0)))]>;

// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(PFWithTypeOF ?:$dst)'
def UseInPF: GICombineRule<
(defs root:$dst),
(match (PFWithTypeOF $dst)),
(apply (G_ANYEXT $dst, (i32 0)))>;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: GISpecialType is not supported in 'match' patterns
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: operand 1 of '__InferredUseInMatch_match_0' has type 'GITypeOf<$dst>'
def InferredUseInMatch : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src)),
(apply (G_ANYEXT $dst, GITypeOf<"$dst">:$src))>;

// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: conflicting types for operand 'src': first seen with 'i32' in '__InferenceConflict_match_0, now seen with 'GITypeOf<$dst>' in '__InferenceConflict_apply_0'
def InferenceConflict : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, i32:$src)),
(apply (G_ANYEXT $dst, GITypeOf<"$dst">:$src))>;

// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'tmp' ('GITypeOf<$tmp>') does not refer to a matched operand!
def TypeOfApplyTmp : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src)),
(apply (G_ANYEXT $dst, i32:$tmp),
(G_ANYEXT $tmp, (GITypeOf<"$tmp"> 0)))>;

// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse one or more rules
def MyCombiner: GICombiner<"GenMyCombiner", [
NoDollarSign,
UnknownOperand,
UseInMatch,
UseInPF,
InferredUseInMatch,
InferenceConflict,
TypeOfApplyTmp
]>;
Loading