Skip to content

Commit

Permalink
[TableGen][GlobalISel] Add rule-wide type inference (#66377)
Browse files Browse the repository at this point in the history
The inference is trivial and leverages the MCOI OperandTypes encoded in
CodeGenInstructions to infer types across patterns in a CombineRule.
It's thus very limited and only supports CodeGenInstructions (but that's the
main use case so it's fine).

We only try to infer untyped operands in apply patterns when they're
temp reg defs, or immediates. Inference always outputs a `GITypeOf<$x>` where
$x is a named operand from a match pattern.

This allows us to drop the `GITypeOf` in most cases without any errors.
  • Loading branch information
Pierre-vh committed Nov 8, 2023
1 parent 1146d96 commit 573fa77
Show file tree
Hide file tree
Showing 6 changed files with 662 additions and 144 deletions.
7 changes: 7 additions & 0 deletions llvm/include/llvm/Target/GenericOpcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

class GenericInstruction : StandardPseudoInstruction {
let isPreISelOpcode = true;

// When all variadic ops share a type with another operand,
// this is the type they share. Used by MIR patterns type inference.
TypedOperand variadicOpsType = ?;
}

// Provide a variant of an instruction with the same operands, but
Expand Down Expand Up @@ -1228,6 +1232,7 @@ def G_UNMERGE_VALUES : GenericInstruction {
let OutOperandList = (outs type0:$dst0, variable_ops);
let InOperandList = (ins type1:$src);
let hasSideEffects = false;
let variadicOpsType = type0;
}

// Insert a smaller register into a larger one at the specified bit-index.
Expand All @@ -1245,6 +1250,7 @@ def G_MERGE_VALUES : GenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins type1:$src0, variable_ops);
let hasSideEffects = false;
let variadicOpsType = type1;
}

/// Create a vector from multiple scalar registers. No implicit
Expand All @@ -1254,6 +1260,7 @@ def G_BUILD_VECTOR : GenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins type1:$src0, variable_ops);
let hasSideEffects = false;
let variadicOpsType = type1;
}

/// Like G_BUILD_VECTOR, but truncates the larger operand types to fit the
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/Target/GlobalISel/Combine.td
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ def trunc_shift: GICombineRule <
def mul_by_neg_one: GICombineRule <
(defs root:$dst),
(match (G_MUL $dst, $x, -1)),
(apply (G_SUB $dst, (GITypeOf<"$x"> 0), $x))
(apply (G_SUB $dst, 0, $x))
>;

// Fold (xor (and x, y), y) -> (and (not x), y)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def bad_imm_too_many_args : GICombineRule<
(match (COPY $x, (i32 0, 0)):$d),
(apply (COPY $x, $b):$d)>;

// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: cannot parse immediate '(COPY 0)', 'COPY' is not a ValueType
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: cannot parse immediate '(COPY 0)': unknown type 'COPY'
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(COPY ?:$x, (COPY 0))
def bad_imm_not_a_valuetype : GICombineRule<
(defs root:$a),
Expand Down Expand Up @@ -186,7 +186,7 @@ def expected_op_name : GICombineRule<
(match (G_FNEG $x, i32)),
(apply (COPY $x, (i32 0)))>;

// CHECK: :[[@LINE+3]]:{{[0-9]+}}: error: invalid operand type: 'not_a_type' is not a ValueType
// CHECK: :[[@LINE+3]]:{{[0-9]+}}: error: cannot parse operand type: unknown type 'not_a_type'
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: Failed to parse pattern: '(G_FNEG ?:$x, not_a_type:$y)'
def not_a_type;
def bad_mo_type_not_a_valuetype : GICombineRule<
Expand Down
68 changes: 68 additions & 0 deletions llvm/test/TableGen/GlobalISelCombinerEmitter/type-inference.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// RUN: llvm-tblgen -I %p/../../../include -gen-global-isel-combiner \
// RUN: -gicombiner-debug-typeinfer -combiners=MyCombiner %s 2>&1 | \
// RUN: FileCheck %s

// Checks reasoning of the inference rules.

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

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

// CHECK: Rule Operand Type Equivalence Classes for inference_mul_by_neg_one:
// CHECK-NEXT: Groups for __inference_mul_by_neg_one_match_0: [dst, x]
// CHECK-NEXT: Groups for __inference_mul_by_neg_one_apply_0: [dst, x]
// CHECK-NEXT: Final Type Equivalence Classes: [dst, x]
// CHECK-NEXT: INFER: imm 0 -> GITypeOf<$x>
// CHECK-NEXT: Apply patterns for rule inference_mul_by_neg_one after inference:
// CHECK-NEXT: (CodeGenInstructionPattern name:__inference_mul_by_neg_one_apply_0 G_SUB operands:[<def>$dst, (GITypeOf<$x> 0), $x])
def inference_mul_by_neg_one: GICombineRule <
(defs root:$dst),
(match (G_MUL $dst, $x, -1)),
(apply (G_SUB $dst, 0, $x))
>;

// CHECK: Rule Operand Type Equivalence Classes for infer_complex_tempreg:
// CHECK-NEXT: Groups for __infer_complex_tempreg_match_0: [dst] [x, y, z]
// CHECK-NEXT: Groups for __infer_complex_tempreg_apply_0: [tmp2] [x, y]
// CHECK-NEXT: Groups for __infer_complex_tempreg_apply_1: [tmp, tmp2]
// CHECK-NEXT: Groups for __infer_complex_tempreg_apply_2: [dst, tmp]
// CHECK-NEXT: Final Type Equivalence Classes: [dst, tmp, tmp2] [x, y, z]
// CHECK-NEXT: INFER: MachineOperand $tmp2 -> GITypeOf<$dst>
// CHECK-NEXT: INFER: MachineOperand $tmp -> GITypeOf<$dst>
// CHECK-NEXT: Apply patterns for rule infer_complex_tempreg after inference:
// CHECK-NEXT: (CodeGenInstructionPattern name:__infer_complex_tempreg_apply_0 G_BUILD_VECTOR operands:[<def>GITypeOf<$dst>:$tmp2, $x, $y])
// CHECK-NEXT: (CodeGenInstructionPattern name:__infer_complex_tempreg_apply_1 G_FNEG operands:[<def>GITypeOf<$dst>:$tmp, GITypeOf<$dst>:$tmp2])
// CHECK-NEXT: (CodeGenInstructionPattern name:__infer_complex_tempreg_apply_2 G_FNEG operands:[<def>$dst, GITypeOf<$dst>:$tmp])
def infer_complex_tempreg: GICombineRule <
(defs root:$dst),
(match (G_MERGE_VALUES $dst, $x, $y, $z)),
(apply (G_BUILD_VECTOR $tmp2, $x, $y),
(G_FNEG $tmp, $tmp2),
(G_FNEG $dst, $tmp))
>;

// CHECK: Rule Operand Type Equivalence Classes for infer_variadic_outs:
// CHECK-NEXT: Groups for __infer_variadic_outs_match_0: [x, y] [vec]
// CHECK-NEXT: Groups for __infer_variadic_outs_match_1: [dst, x]
// CHECK-NEXT: Groups for __infer_variadic_outs_apply_0: [tmp, y]
// CHECK-NEXT: Groups for __infer_variadic_outs_apply_1:
// CHECK-NEXT: Final Type Equivalence Classes: [tmp, dst, x, y] [vec]
// CHECK-NEXT: INFER: MachineOperand $tmp -> GITypeOf<$dst>
// CHECK-NEXT: Apply patterns for rule infer_variadic_outs after inference:
// CHECK-NEXT: (CodeGenInstructionPattern name:__infer_variadic_outs_apply_0 G_FNEG operands:[<def>GITypeOf<$dst>:$tmp, $y])
// CHECK-NEXT: (CodeGenInstructionPattern name:__infer_variadic_outs_apply_1 COPY operands:[<def>$dst, GITypeOf<$dst>:$tmp])
def infer_variadic_outs: GICombineRule <
(defs root:$dst),
(match (G_UNMERGE_VALUES $x, $y, $vec),
(G_FNEG $dst, $x)),
(apply (G_FNEG $tmp, $y),
(COPY $dst, $tmp))
>;

def MyCombiner: GICombiner<"GenMyCombiner", [
inference_mul_by_neg_one,
infer_complex_tempreg,
infer_variadic_outs
]>;
7 changes: 5 additions & 2 deletions llvm/test/TableGen/GlobalISelCombinerEmitter/typeof-errors.td
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ 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
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: cannot parse immediate '(anonymous_7029 0)': invalid operand name format 'unknown' in GITypeOf: expected '$' followed by an operand name
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(G_ANYEXT ?:$dst, (anonymous_
def NoDollarSign : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, $src)),
Expand Down Expand Up @@ -47,7 +48,9 @@ def InferredUseInMatch : GICombineRule<
(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'
// CHECK: :[[@LINE+3]]:{{[0-9]+}}: error: conflicting types for operand 'src': 'i32' vs 'GITypeOf<$dst>'
// CHECK: :[[@LINE+2]]:{{[0-9]+}}: note: 'src' seen with type 'GITypeOf<$dst>' in '__InferenceConflict_apply_0'
// CHECK: :[[@LINE+1]]:{{[0-9]+}}: note: 'src' seen with type 'i32' in '__InferenceConflict_match_0'
def InferenceConflict : GICombineRule<
(defs root:$dst),
(match (G_ZEXT $dst, i32:$src)),
Expand Down

0 comments on commit 573fa77

Please sign in to comment.