[llvm-dev] [RFC] Tablegen-erated GlobalISel Combine Rules

Nicolai Hähnle via llvm-dev llvm-dev at lists.llvm.org
Thu Nov 29 01:57:30 PST 2018


Hi Daniel,


On 27.11.18 18:59, Daniel Sanders wrote:
> I've more or less finished updating the examples to the DAG style we 
> were talking about. Hopefully I haven't forgotten anything, there was a 
> lot to keep track of :-). Overall, I think there's a couple places where 
> things get a a little awkward (mainly debug info) but things generally 
> look good to me.

That's awesome :)


> _A Simple Example_
> 
> def : GICombineRule<(defs reg:$D, reg:$S),
>                      (match (G_ZEXT s32:$t1, s8:$S),
>                             (G_TRUNC s16:$D, s32:$t1)),
>                      (apply (G_ZEXT s16:$D, s8:$S))>;
> 
> This has been converted to the style that fits within tblgen's DAG type 
> but isn't (directly) a DAG. The DAG structure is defined by the matching 
> names instead of tblgen's DAG type. This avoids the restrictions of the 
> DAG type and is something that's needed anyway to allow one node to be 
> referenced in multiple places. I've kept the 'match' node at the top of 
> the match to allow instructions and predicates to be freely combined 
> within this section (see later examples). Without it, the stricter type 
> checking on list<X> would require Instruction, GIMatchPredicate, and 
> any future matching extensions to have a common base class.

Makes sense.

I was wondering whether it also makes sense to allow eliding the (match) 
if it only has one child. It seems convenient, although having it always 
be required may reduce the learning curve.

The other point is that I have been wondering in the past whether it 
wouldn't make sense to add an 'any' type to TableGen, so that 
heterogenous lists of type list<any> are possible. TableGen already 
internally has any-of-list-of-classes types -- there's no syntax to 
write these types down, but they were needed to consistently describe 
the type of a def that is derived from multiple classes (these types are 
printed as {Class1,Class2,...} in debug output).

I haven't *really* needed an 'any' type yet, but there were times when I 
thought it might be convenient, so I'd be open to that.

Then again, explicitly writing (match) also has the advantage that it'd 
be obvious which part of the rule one is looking at.


[snip]
> _Passing arbitrary data from match to apply_
> 
> The main change in this section that hasn't already been discussed is 
> that the result of extending_load_predicate has been moved to the new 
> 'outs' section of GIMatchPredicate and the code expansion refers to a 
> particular output of the predicate using 'matchinfo.B' similar to a 
> struct member or multi-operand ComplexPatterns.
> 
>      def extending_load_matchdata : GIDefMatchData<"PreferredTuple">;
>      def extending_load_predicate : GIMatchPredicate<
>           bool, (ins reg:$A), (outs extending_load_matchdata:$B), [{
>        return Helper.matchCombineExtendingLoads(${A}, ${B});
>      }]>;
>      def extending_loads : GICombineRule<
>        (defs operand:$D, reg:$A, extending_load_matchdata:$matchinfo),
>        (match (G_LOAD $D, $A),
>               (extending_load_predicate operand:$A,
>                                      
>     extending_load_matchdata:$matchinfo)),
>        (apply (exec [{ Helper.applyCombineExtendingLoads(${D}, 
> ${matchinfo.B}); }],
>                     reg:$D, extending_load_matchdata:$matchinfo)>;
> 
> The main problem with this is that this prevents access to the contents 
> of matchinfo from outside of C++. To overcome this, I'd suggest allowing 
> the '${foo.bar}' syntax in tablegen's dag type. It should be a fairly 
> simple change and there's no tblgen-internal reason to not allow dots in 
> the names. Existing backends would have to check for it though which 
> could be a tblgen performance issue.

What does the ${matchinfo.B} mean, exactly? Why is $matchinfo alone not 
good enough? It appears syntactically in the place that I would expect 
corresponds to the out argument $B of extending_load_predicate.


> _Macros_
> 
> Aside from changing to the DAG style, the main change here is that the 
> children of 'oneof' all have 'match' at the top-level. This is for the 
> same reason 'match' was kept on the simple example: the relaxed type 
> checking on 'dag' compared to 'list<X>' allows us to freely mix 
> DAG-style matches and predicates.
>      def ANYLOAD : GIMacro<(defs def:$R, use:$S, uint64_t:$IDX),
>                            (match (oneof (match (G_LOAD $R, $S)),
>                                          (match (G_SEXTLOAD $R, $S)),
>                                          (match (G_ZEXTLOAD $R, $S))):$IDX>;
>      def extending_loads : GICombineRule<
>        (defs reg:$D, reg:$A, extending_load_matchdata:$matchinfo, 
> ANYLOAD:$ANYLOAD),
>        (match (ANYLOAD $D, $A),
>               (extending_load_predicate operand:$A,
>                                      
>     extending_load_matchdata:$matchinfo)),
>        (apply (exec [{ Helper.applyCombineExtendingLoads(${D}, 
> ${matchinfo.B}); }],
>                     reg:$D, extending_load_matchdata:$matchinfo)>;

$IDX doesn't seem to be used in the (apply) part.


> _'Upside-down' matches (i.e. roots at the top) and similar_
> 
> As mentioned back in the simple example, these need to specify the match 
> roots using a 'let' statement to override the default:
> def : GICombineRule<
>    (defs reg:$D, reg:$A),
>    (match (G_LOAD $t1, $D),
>           (G_SEXT $A, $t1)),
>    (apply (G_SEXTLOAD $A, $D))> {
>    let MatchStartsFrom = (roots $D);
> };
> 
> def : GICombineRule<
>    (defs reg:$D, reg:$A, reg:$B, reg:$C),
>    (match (G_TRUNC s32:$t1, s64:$A),
>           (G_TRUNC s32:$t2, s64:$B),
>           (G_ADD $D, $t1, $t2)
>           (G_SEXT s64:$C, $D)),
>    (apply (G_ADD $D, $A, $B),
>           (G_SEXT_INREG $C, $D))> {
> let MatchStartsFrom = (roots $D);
> };
> 
> _Multiple roots_
> 
> All the changes in this section have already been discussed above:
> def : GICombineRule<
>    (defs reg:$D1, reg:$D2, reg:$A, reg:$B),
>    (match (G_ADD $D1, $A, $B),
>           (G_SUB $D2, $A, $B)),
>    (apply (BUTTERFLY $D1, $D2, $A, $B))>;
> $D1 and $D2 are both automatically chosen as roots since they are def'd 
> but not used.

Both of these sound good to me!


> _Subregisters_
> 
> This is a new example based on our discussion about MIR having direct 
> support for subregisters. We use subregister indexes to specify that 
> it's a subregister that should be emitted by the apply step.
> def : GICombineRule<
>    (defs reg:$D, reg:$A, reg:$B),
>    (match (G_TRUNC s32:$t1, s64:$A),
>           (G_TRUNC s32:$t2, s64:$B),
>           (G_ADD $D, $t1, $t2),
>    (apply (ADD32 $D, (sub_lo $A), (sub_lo $B)))>;
> 
> _Matching MachineMemOperands_
> 
> While re-writing these examples, I also realized I didn't have any 
> examples for testing properties of the MachineMemOperand, so here's one:
>      def mmo_is_load_8 : GIMatchPredicate<
>           (ins instr:$A), (outs), [{
>        if (!${A}.hasOneMemOperand())
>          return false;
>        const auto &MMO = *${A}.memoperands_begin();
>        return MMO.isLoad() && MMO.getSize() == 1;
>      }]>;
>      def : GICombineRule<
>        (defs operand:$D, operand:$A),
>        (match (G_LOAD $D, $A):$MI,
>               (mmo_is_load8 instr:$MI)),
>        (apply ...)>;
> I've made use of the naming instructions here since this was better in 
> the debug info section. If we choose the alternative in that section 
> then we can change it to $D or $A instead (either way the generated code 
> would call getParent()).

Looks good to me. How would getParent() work if $A also appears as a def 
of an address calculation?

Cheers,
Nicolai
-- 
Lerne, wie die Welt wirklich ist,
Aber vergiss niemals, wie sie sein sollte.


More information about the llvm-dev mailing list