Instruction pattern type inference problem

I have a back end which has both scalar and vector registers that alias each other. I'm having a problem generating the ISel from tablegen that appears only when a vector register class is declared to contain integer vectors. At that moment tablegen doesn't seem to be able to infer integer types in patterns that it was able to before, but I'm not clear on why that's the case.

This isn't just the results of instructions, but also immediate values as well. It seems to affect a smattering of node types. Any insights?

For instance:

where GPRegs contains types [i32, f32]

def BEQ : IF8<Opc.BEQ,
             (ops GPRegs:$Rsrc1, GPRegs:$Rsrc2, brtarget:$SImm16),
             "beq $Rsrc1, $Rsrc2, $SImm16",
             [(brcond (i32 (seteq GPRegs:$Rsrc1, GPRegs:$Rsrc2)), bb:$SImm16)], s_br>;

Tablegen reports:
BEQ: (brcond:void (setcc:i32 GPRegs:i32:$Rsrc1, GPRegs:i32:$Rsrc2, SETEQ:Other), (bb:Other):$SImm16)

as soon as I add a register class that supports either [v2i32] or [v4i32] I get the following:

BGE: (brcond:void (setcc:isInt GPRegs:i32:$Rsrc1, 0:i32, SETGE:Other), (bb:Other):$SImm16)
build/llvm/trunk/Debug/bin/tblgen: In BGE: Could not infer all types in pattern!

Thanks

Digging deeper...

1. Is there a good reason that v2f32 types are excluded from the isFloatingPoint filter? Looks like a bug to me.

     v2f32 = 22, // 2 x f32
     v4f32 = 23, // 4 x f32 <== start ??
     v2f64 = 24, // 2 x f64 <== end

   static inline bool isFloatingPoint(ValueType VT) {
     return (VT >= f32 && VT <= f128) || (VT >= v4f32 && VT <= v2f64);
   }

2. My problem seems to stem from what appears to be under-constrained typing of patterns. With vectors there is a challenge because currently there is no type constraint available to indicate that an operand can/cannot be a vector type, or a constraint that operands must have the same number of elements (either both scalers or vectors with the same number of elements, but not necessarily the same element type or element size).

Even with these constraints, it would be difficult to apply them to the standard nodes in a general way, though I could be wrong. Should standard nodes have vector aware type constraints?

I have a back end which has both scalar and vector registers that
alias each other. I'm having a problem generating the ISel from
tablegen that appears only when a vector register class is declared
to contain integer vectors. At that moment tablegen doesn't seem to
be able to infer integer types in patterns that it was able to
before, but I'm not clear on why that's the case.

ok

This isn't just the results of instructions, but also immediate
values as well. It seems to affect a smattering of node types. Any
insights?

For instance:

where GPRegs contains types [i32, f32]

def BEQ : IF8<Opc.BEQ,
            (ops GPRegs:$Rsrc1, GPRegs:$Rsrc2, brtarget:$SImm16),
            "beq $Rsrc1, $Rsrc2, $SImm16",
            [(brcond (i32 (seteq GPRegs:$Rsrc1, GPRegs:$Rsrc2)), bb:
$SImm16)], s_br>;

Tablegen reports:
BEQ: (brcond:void (setcc:i32 GPRegs:i32:$Rsrc1, GPRegs:i32:$Rsrc2,
SETEQ:Other), (bb:Other):$SImm16)

as soon as I add a register class that supports either [v2i32] or
[v4i32] I get the following:

BGE: (brcond:void (setcc:isInt GPRegs:i32:$Rsrc1, 0:i32,
SETGE:Other), (bb:Other):$SImm16)
build/llvm/trunk/Debug/bin/tblgen: In BGE: Could not infer all types
in pattern!

Comparison nodes don't support vector types.

-Chris

1. Is there a good reason that v2f32 types are excluded from the
isFloatingPoint filter? Looks like a bug to me.

    v2f32 = 22, // 2 x f32
    v4f32 = 23, // 4 x f32 <== start ??
    v2f64 = 24, // 2 x f64 <== end

  static inline bool isFloatingPoint(ValueType VT) {
    return (VT >= f32 && VT <= f128) || (VT >= v4f32 && VT <= v2f64);
  }

Definitely a bug. I don't think any existing targets support v2f32 which is probably why it wasn't noticed.

2. My problem seems to stem from what appears to be under-constrained
typing of patterns. With vectors there is a challenge because
currently there is no type constraint available to indicate that an
operand can/cannot be a vector type, or a constraint that operands
must have the same number of elements (either both scalers or vectors
with the same number of elements, but not necessarily the same
element type or element size).

Even with these constraints, it would be difficult to apply them to
the standard nodes in a general way, though I could be wrong. Should
standard nodes have vector aware type constraints?

It depends on which nodes you mean. "and" for example, can apply to any integer type, including vectors. The various compares can only apply to scalars, as it would be otherwise ambiguous how the comparison is done (i.e. is it true if all element satisfy the predicate, or if any of them?)

Do you have a specific example in mind?

-Chris

  1. Is there a good reason that v2f32 types are excluded from the
    isFloatingPoint filter? Looks like a bug to me.

v2f32 = 22, // 2 x f32
v4f32 = 23, // 4 x f32 <== start ??
v2f64 = 24, // 2 x f64 <== end

static inline bool isFloatingPoint(ValueType VT) {
return (VT >= f32 && VT <= f128) || (VT >= v4f32 && VT <= v2f64);
}

Definitely a bug. I don’t think any existing targets support v2f32 which
is probably why it wasn’t noticed.

Fix is in.

  1. My problem seems to stem from what appears to be under-constrained
    typing of patterns. With vectors there is a challenge because
    currently there is no type constraint available to indicate that an
    operand can/cannot be a vector type, or a constraint that operands
    must have the same number of elements (either both scalers or vectors
    with the same number of elements, but not necessarily the same
    element type or element size).

Even with these constraints, it would be difficult to apply them to
the standard nodes in a general way, though I could be wrong. Should
standard nodes have vector aware type constraints?

It depends on which nodes you mean. “and” for example, can apply to any
integer type, including vectors. The various compares can only apply to
scalars, as it would be otherwise ambiguous how the comparison is done
(i.e. is it true if all element satisfy the predicate, or if any of
them?)

Do you have a specific example in mind?

A strategy that we have had luck with in the past is the concept of a “vector boolean” type, which is the result of a comparison between two vectors. It’s then necessary to perform a reduction (and, or) on the elements in order to produce a scalar boolean value that’s suitable for a branch. This also allows stuff like letting ‘select’ accept a vector boolean predicate and vector values to perform a vector select.

In these cases you’d want to enforce the kinds of operand type constraints as mentioned above to ensure that the types are all acceptable.

Then this node would really benefit from a type constraint that indicates this. Here the type constraint ‘isInt’ includes all the integer vector types, causing tablegen to not be able to infer all the types in the pattern.

Christopher Lamb wrote:

A strategy that we have had luck with in the past is the concept of a "vector boolean" type, which is the result of a comparison between two vectors. It's then necessary to perform a reduction (and, or) on the elements in order to produce a scalar boolean value that's suitable for a branch. This also allows stuff like letting 'select' accept a vector boolean predicate and vector values to perform a vector select.

this is the semantics that GLSL uses for vector compares, for example the signature of one of the overloads for the built-in function 'lessThan' is:

bvec4 lessThan(vec4 x, vec4 y)

what I wanted to mention are the built-in functions 'any' and 'all' which have signatures (for 4-vectors)

bool any(bvec4 x)
bool all(bvec4 x)

and the obvious semantics any(x)= x.x || x.y || x.z || x.w and all(x)= x.x && x.y && x.z && x.w -- I think if vector comparisions and vector boolean types are added to LLVM, it would also be very nice to have the 'any' and 'all' added as intrinsics...

m.

This is precisely the approach that we took. Programmers can pretty easily wrap their head around the idea of vector bools.

Definitely a bug. I don't think any existing targets support v2f32 which
is probably why it wasn't noticed.

Fix is in.

Thanks!

Do you have a specific example in mind?

A strategy that we have had luck with in the past is the concept of a "vector boolean" type, which is the result of a comparison between two vectors. It's then necessary to perform a reduction (and, or) on the elements in order to produce a scalar boolean value that's suitable for a branch. This also allows stuff like letting 'select' accept a vector boolean predicate and vector values to perform a vector select.

In these cases you'd want to enforce the kinds of operand type constraints as mentioned above to ensure that the types are all acceptable.

Sure, this makes sense. If you wanted to add comparisons and select operations to the LLVM level, I'd suggest exactly what you suggest:

* vector compares which return a vector of bools
* vector selects which take a vector of bools and two other vectors
* an any/all predicate that performs an and/or reduction.

I believe that the SSE and Altivec instruction set could implement the important cases of these (e.g. compare+any), but right now we just use LLVM intrinsics for all of the SSE/Altivec compare-related instructions. When we get a vectorizor, we will probably want the more generic mechanism though.

-Chris

Yep, that makes sense to me.

-Chris