<html><head><meta http-equiv="Content-Type" content="text/html; charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><font size="2" class="">Hi All,</font><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">I've been working on the GlobalISel combiner recently and I'd like to share the plan for how Combine Rules will be defined in GlobalISel and solicit feedback on it.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">This email ended up rather long so:</font></div><div class=""><span style="font-size: small;" class=""><span class="Apple-tab-span" style="white-space:pre"> </span>TL;DR: We're planning to define GlobalISel Combine Rules using MIR syntax with a few bits glued on to interface with the algorithm and escape into C++ when we need to. Eventually, ISel rules may follow suit.</span></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><div class=""><font size="2" class="">Thanks to the following people for their help in playing with this syntax and discussing it with me over the last couple weeks:</font></div><div class=""><ul class="MailOutline"><li class=""><font size="2" class="">Aditya Nandakumar</font></li><li class=""><font size="2" class="">Adrian Prantl</font></li><li class=""><font size="2" class="">Ahmed Bougacha</font></li><li class=""><font size="2" class="">Amara Emerson</font></li><li class=""><font size="2" class="">Jessica Paquette</font></li><li class=""><font size="2" class="">Michael Berg</font></li><li class=""><font size="2" class="">Justin Bogner</font></li><li class=""><font size="2" class="">Roman Tereshin</font></li><li class=""><font size="2" class="">Vedant Kumar</font></li><li class=""><font size="2" class="">Volkan Keles</font></li></ul><div class=""><font size="2" class="">Their feedback has vastly improved this over the original version and it was through those discussions that the idea of re-using MIR as a declarative definition was born.</font></div><div class=""><font size="2" class=""><br class=""></font></div></div></div><div class=""><font size="2" class="">Also thanks to the person who raised the issue of debug info at the BoF. I'm afraid I didn't write your name in my notes on the day so hopefully you'll make yourself known so we can give you the credit you're due. The debug info part of the proposal is still a little hand-wavy but it wouldn't have been here at all without you :-)</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><div class=""><u class=""><font size="4" class="">Scope of this RFC</font></u></div><div class=""><font size="2" class=""><br class=""></font></div></div><div class=""><font size="2" class="">This RFC is intended to solicit feedback on the syntax, semantics, and overall appearance of the tablegen classes and definitions used to define Combine Rules for GlobalISel and see if there are any flaws in the usability of such definitions. I'd like to keep the mechanics of the Combine algorithm fairly abstract for this discussion as a few things that are syntactically possible will require improvements to the combine algorithm before they can be used and I'd like to leave my thoughts on the algorithm to a later discussion once I have something more concrete. I'll label these areas as they come up.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><u class=""><font size="4" class="">Overview</font></u></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">GlobalISel requires some passes that replace groups of MachineInstrs with simpler equivalents just like DAGCombine does for SelectionDAG. DAGCombine as a whole is composed of multiple large monoliths of C++ code with one covering the target independent combines, and one for each target. While this gives a lot of power and freedom to do whatever is needed, such an architecture also makes several things difficult:</font></div><div class=""><ul class="MailOutline"><li class=""><font size="2" class="">It's difficult to test an individual combine as others can modify the test case before it reaches the relevant rule.</font></li><li class=""><font size="2" class="">It's difficult to debug due to the quantity of transformations being applied, the iterative nature of the algorithm, and the lack of scope-reducing techniques (e.g. bisection) beyond the top-level opcode.</font></li><li class=""><font size="2" class="">It's difficult for a target to opt-out of or specialize a common combine. In-tree targets must inject triple checks, while out-of-tree targets must also pay the price of the resulting merge conflicts.</font></li><li class=""><font size="2" class="">It's difficult to write a target specific combine with higher priority than one common combine but lower priority than another.</font></li><li class=""><font size="2" class="">It's difficult to write analysis tools for the combines such as those to detect unreachable combines, or to prove them correct, etc.</font></li><li class=""><font size="2" class="">It's difficult to investigate improvements to the overall algorithm as there's no means to change the code for all rules.</font></li><li class=""><font size="2" class="">and so on</font></li></ul><div class=""><br class=""></div></div><div class=""><font size="2" class="">For these reasons and more it would be useful to have at least partial generation of the combine rules used by GlobalISel. </font><span style="font-size: small;" class="">In addition to this, the bulk of matching a combine is boilerplate. It would be useful to defer the boilerplate code to a generator and focus more on the details that make each combine unique.</span></div><div class=""><span style="font-size: small;" class=""><br class=""></span></div><div class=""><span style="font-size: small;" class="">To that end (and also to eventually drop the SelectionDAG importer for ISel), I've been looking into how we would declare Combine Rules in tablegen and with the help of the people credited above I think we've ended up somewhere that should be nice to work with and opens up a lot of possibilities.</span></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><u class=""><font size="4" class="">Individual Combine Rules</font></u></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><u class=""><font size="2" class="">A Simple Example</font></u></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">Here's a simple example that eliminates a redundant G_TRUNC.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class="">def : GICombineRule<</span><span style="font-family: Menlo;" class="">(defs root:$D, operand:$S),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (match [{MIR</span><font face="Menlo" class=""> %1(s32) = G_ZEXT %S(s8)</font><br class="" style="font-family: Menlo;"><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> %D(s16) = G_TRUNC %1(s32) </span><span style="font-family: Menlo;" class="">}]),</span><br class="" style="font-family: Menlo;"><span style="font-family: Menlo;" class=""> </span><font face="Menlo" class=""> (apply [{MIR %D(s16) = G_ZEXT %S(s8) }])>;</font></font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">There's three parts to this rule:</font></div><div class=""><ul class="MailOutline"><li class=""><font size="2" class=""><font face="Menlo" class="">defs</font> declares the interface for the rule. The definitions in the defs section are the glue between the Combine algorithm and the rule, as well as between the match and apply sections. In this case we only define the root of the match and that we have a register variable called $S.</font></li><li class=""><font size="2" class=""><font face="Menlo" class="">match</font> declares the conditions that must be true for the rule to fire. In this case, we require that we match a G_TRUNC whose result is of type s16 and whose operand is type s32 as well as a G_ZEXT whose result is the same as the G_TRUNC's operand, and with an operand of type s8. If all these are true, then we have the option of applying the rule using the apply section. Some algorithms may take every opportunity it sees but others might weigh up different possible choices more carefully.</font></li><li class=""><font size="2" class=""><font face="Menlo" class="">apply</font> declares the result of the rule firing. In this case, we'd build a G_ZEXT whose result is %D and whose operand is %S from the match.</font></li></ul><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">Generally speaking, the Combine rule patterns declare what needs to happen and it's up to tablegen to choose how to achieve it. However, Combines can be rather complicated and a DSL cannot hope to cover everything that might be needed. As such there will be trapdoors into arbitrary C++.</font></div><div class=""><font size="2" class=""><br class=""></font></div></div><div class=""><span style="font-size: small;" class="">The MIR match block matches by following the use-def edges and does not require that the MachineInstr's are in the exact order specified. It only requires that the instructions are connected to each other as described and that the input MIR is valid (e.g. no uses before defs). It can reach across basic blocks when it's safe to do so. We may need to introduce a means to limit the inter-basic-block reach as I can foresee situations where it may pull expensive operations into a loop if it's not careful about this. My current thinking on this is that it should be limited to moving to similar or cheaper blocks. I can potentially see some use in choosing the insertion point to select cheaper blocks however I think that's best left to passes like MachineLICM rather than having Combine try to do everything.</span></div><div class=""><span style="font-size: small;" class=""><br class=""></span></div><div class=""><span style="font-size: small;" class="">Syntactically in tablegen, the '[{MIR ... }]' is just an ordinary code block that happens to start with the string 'MIR'. The 'MIR' string is there for editors to use to switch syntax highlighting modes to MIR instead of C++.</span></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><u class=""><font size="2" class="">Generalizing the Simple Example</font></u></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">Now let's generalize the match section a bit so that it handles all intermediate scalar types. All we need to do is delete the type from %1 like so:</font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">def : GICombineRule<</span><span style="font-family: Menlo;" class="">(defs root:$D</span><span style="font-family: Menlo;" class="">, operand:$S</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (match [{MIR</span><font face="Menlo" class=""> %1 = G_ZEXT %S(s8)</font><br class="" style="font-family: Menlo;"><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> %D(s16) = G_TRUNC %1 </span><span style="font-family: Menlo;" class="">}]),</span><br class="" style="font-family: Menlo;"><span style="font-family: Menlo;" class=""> </span><font face="Menlo" class=""> (apply [{MIR %D(s16) = G_ZEXT %S(s8) }])>;</font></font></div></div><div class=""><font size="2" class="">what we've done here is removed the type check on %1 entirely. This is safe for this particular rule since, by definition, G_ZEXT will always produce a larger scalar result type than its scalar input type and G_TRUNC will always produce a smaller one. The only way these can both be the case is if %1 is a a scalar of at least s17. We're still not covering every type combination that we ought to though since %S and %D are still restricted to s8 and s16 respectively. To fix this, we'll need some custom predicates.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">A custom predicate is declared like so:</font></div><div class=""><div class=""><span style="font-family: Menlo;" class=""><font size="2" class=""> def isScalarType : GIMatchPredicate<(ins type:$A), bool, [{</font></span></div><div class=""><span style="font-family: Menlo;" class=""><font size="2" class=""> return ${A}.isScalar();</font></span></div><div class=""><span style="font-family: Menlo;" class=""><font size="2" class=""> }]>;</font></span></div></div><div class=""><font size="2" class="">this declares that the predicate takes a type (LLT) named A and returns a bool. It also declares the code used to test the predicate and the place that the variable for A must be inserted. We can potentially use types other than bool such as structures but we'll leave that for later. Completing the generalized example using this (and another) predicate, we get:</font></div><div class=""><span style="font-family: Menlo;" class=""><font size="2" class=""> def isLargerType : GIMatchPredicate<(ins type:$A, type:$B), bool, [{</font></span></div><div class=""><span style="font-family: Menlo;" class=""><font size="2" class=""> return ${A}.getSizeInBits() > ${B}.getSizeInBits();</font></span></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> }]>;</span><br class="" style="font-family: Menlo;"></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">def : GICombineRule<</span><span style="font-family: Menlo;" class="">(defs root:$D</span><span style="font-family: Menlo;" class="">, operand:$S, </span><span style="font-family: Menlo;" class="">operand:$T</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (match [{MIR</span><font face="Menlo" class=""> %T</font><span style="font-family: Menlo;" class=""> = G_ZEXT %S</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> %D = G_TRUNC %T </span><span style="font-family: Menlo;" class="">}],</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (isScalar</span><span style="font-family: Menlo;" class="">Type</span><span style="font-family: Menlo;" class=""> type:$D),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (isLargerT</span><span style="font-family: Menlo;" class="">ype</span><span style="font-family: Menlo;" class=""> type:$D, type:$S),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (</span><span style="font-family: Menlo;" class="">isLargerT</span><span style="font-family: Menlo;" class="">ype</span><span style="font-family: Menlo;" class=""> type:$T, type:$S),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (</span><span style="font-family: Menlo;" class="">isLargerT</span><span style="font-family: Menlo;" class="">ype</span><span style="font-family: Menlo;" class=""> type:$T, type:$D)</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><font face="Menlo" class=""> (apply [{MIR %D = G_ZEXT %S }])></font>;</font></div></div><div class=""><font size="2" class="">We've declared a C++ predicate that takes checks whether a given type is a scalar. We've also declared another which checks that one type has more bits than another. We've then extended the defs for the rule with an additional operand which is used to name the intermediate operand to allow it to be referenced from C++ predicates. Lastly we've called the C++ predicates as part of the match. Each argument is of type 'operand' and needs to be provided as type 'type' so tablegen emits code to test for convertability (MO.isReg() in this case), and to do the conversion (MRI.getType(MO.getReg()) in this case). The end result is that this rule now supports all the type combinations where we extend to a larger type, then truncate to a type that lies between the intermediate and the source in size.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">As noted earlier, we can simplify this to:</font></div><div class=""><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">def : GICombineRule<</span><span style="font-family: Menlo;" class="">(defs root:$D</span><span style="font-family: Menlo;" class="">, operand:$S</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (match [{MIR</span><font face="Menlo" class=""> %0</font><span style="font-family: Menlo;" class=""> = G_ZEXT %S</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> %D = G_TRUNC %0 </span><span style="font-family: Menlo;" class="">}],</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (isScalar</span><span style="font-family: Menlo;" class="">Type</span><span style="font-family: Menlo;" class=""> type:$D),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (isLargerT</span><span style="font-family: Menlo;" class="">ype</span><span style="font-family: Menlo;" class=""> type:$D, type:$S)</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><font face="Menlo" class=""> (apply [{MIR %D = G_ZEXT %S }])></font>;</font></div></div></div><div class=""><font size="2" class="">since the definition of G_ZEXT/G_TRUNC is such that $T > $S and $T > $D by definition.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class=""><u class="">Subtarget specific Rules</u></font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><div class=""><font size="2" class="">Facilities like rule predication based on subtarget features will work just as they do in SelectionDAG:</font></div><div class=""><span style="font-family: Menlo;" class=""><font size="2" class=""> def : GICombineRule<...>, Requires<Foo>;</font></span></div></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><u class=""><font size="2" class="">Preserving DILocation and other debug info</font></u></div><div class=""><u class=""><font size="2" class=""><br class=""></font></u></div><div class=""><font size="2" class="">This section is still fairly vague and has had less investigation than the others. Here's the current thinking on it though.</font></div><div class=""><u class=""><font size="2" class=""><br class=""></font></u></div><div class=""><font size="2" class="">Because, we're using MIR it becomes fairly easy to represent debug line information in the rules themselves. Here we expand on the above example to merge the line info for the G_ZEXT and the G_TRUNC into the resulting G_ZEXT using DILocation::getMergedLocation() by simply specifying both debug-location metadata on the new instruction.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">def : GICombineRule<</span><span style="font-family: Menlo;" class="">(defs root:$D</span><span style="font-family: Menlo;" class="">, operand:$S</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (match [{MIR</span><font face="Menlo" class=""> %0</font><span style="font-family: Menlo;" class=""> = G_ZEXT %S</span><font face="Menlo" class="">, debug-location !1</font></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> %D = G_TRUNC %0, debug-location !2 </span><span style="font-family: Menlo;" class="">}],</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (isScalar</span><span style="font-family: Menlo;" class="">Type</span><span style="font-family: Menlo;" class=""> type:$D),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (isLargerT</span><span style="font-family: Menlo;" class="">ype</span><span style="font-family: Menlo;" class=""> type:$D, type:$S)</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><font face="Menlo" class=""> (apply [{MIR %D = G_ZEXT %S</font><span style="font-family: Menlo;" class="">, debug-location !1</span><span style="font-family: Menlo;" class="">, debug-location !2</span><font face="Menlo" class=""> }])></font>;</font></div></div></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">If we add DBG_VALUE to this, then we get:</font></div><div class=""><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">def : GICombineRule<</span><span style="font-family: Menlo;" class="">(defs root:$D</span><span style="font-family: Menlo;" class="">, operand:$S, diexpression:$DExpr</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (match [{MIR</span><font face="Menlo" class=""> %0</font><span style="font-family: Menlo;" class=""> = G_ZEXT %S</span><font face="Menlo" class="">, debug-location !1</font></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> %D = G_TRUNC %0, debug-location !2</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> DBG_VALUE %0, !3,</span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">!metadata($DExpr),</span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">debug-location !4</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> DBG_VALUE %D, !5,</span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">!6,</span><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class="">debug-location !7</span></font></div><div class=""><span style="font-family: Menlo;" class=""><font size="2" class=""> }],</font></span></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (isScalar</span><span style="font-family: Menlo;" class="">Type</span><span style="font-family: Menlo;" class=""> type:$D),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> (isLargerT</span><span style="font-family: Menlo;" class="">ype</span><span style="font-family: Menlo;" class=""> type:$D, type:$S)</span><span style="font-family: Menlo;" class="">),</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><font face="Menlo" class=""> (apply (createDIExprLLVMFragment diexpression:$DExpr, type:$D):$DNewExpr,</font></font></div><div class=""><font size="2" class=""><font face="Menlo" class=""> [{MIR %D = G_ZEXT %S</font><span style="font-family: Menlo;" class="">, debug-location !1, debug-location !2</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> DBG_VALUE %D</span><span style="font-family: Menlo;" class="">, !3, !metadata($DNewExpr), debug-location !4</span></font></div><div class=""><font size="2" class=""><span style="font-family: Menlo;" class=""> </span><span style="font-family: Menlo;" class=""> DBG_VALUE %D, !5, !6, debug-location !7</span></font></div><div class=""><font size="2" class=""><font face="Menlo" class=""> }])></font>;</font></div></div></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">The semantics of DBG_VALUE in the match and apply sections are a little different from most instructions. They still match based on use-def edges rather than the exact order written like normal instructions. However, it's an optional match so that we don't fail to match due to missing DBG_VALUE's. It can also match multiple DBG_VALUE's if there happen to be multiple variables described by a single vreg. The apply step's DBG_VALUE creates DBG_VALUEs corresponding to each of those that were matched. createDIExprLLVMFragment invokes C++ code which creates a new DIExpression by concatenating a 'DW_OP_LLVM_fragment, 0, sizeof(type)' for the given type to the existing DIExpression.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">One interesting benefit of this is that it allows us to verify that all DILocations and DBG_VALUE's are accounted for and handled in some way.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">I should mention that I'm not terribly keen on the current syntax for modifying the diexpression above (createDIExprLLVMFragment). I feel we ought to be able to improve on that and make it more inline in the MIR.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><u class=""><font size="2" class="">Matching immediates and G_CONSTANT/G_FCONSTANT</font></u></div><div class=""><u class=""><font size="2" class=""><br class=""></font></u></div><div class=""><font size="2" class="">In GlobalISel, an immediate is in one of three forms. There's int64_t for MachineOperand::isImm() and ConstantInt * for MachineOperand::isCImm() but those forms are uncommon and are only really seen on target instructions. The most common form is a vreg defined by G_CONSTANT or G_FCONSTANT. All three forms are handled by the 'imm' declaration.</font></div><div class=""><font size="2" class=""><br class=""></font></div><div class=""><font size="2" class="">Here we declare rule that matches X * 2 using a custom predicate to check the immediate is 2:</font></div><div class=""><span style="font-family: Menlo;" class=""><font size="2" class=""> def : GICombineRule<(defs root:$D, reg:$A, imm:$VAL),</font></span></div><div class=""><span class="" style="font-family: Menlo;"><font face="Menlo" class="" size="2"> (match [{MIR %D = G_MUL %A, %VAL }],</font></span></div><div class=""><span class="" style="font-family: Menlo;"><font size="2" class=""><font face="Menlo" class=""> (isTwo imm:$VAL)),<br class=""> (apply [{MIR %root = MYTGT_DOUBLE</font><span class=""> %A }])>;</span></font></span></div><div class=""><span class="" style="font-family: Menlo;"><span class=""><font size="2" class=""><br class=""></font></span></span></div><div class=""><font size="2" class="">Listing the C++ predicates like that will lead to a lot of repetitive and bug-prone code so it will also be possible to embed the predicates in custom variants of imm.</font></div><div class=""><span class="" style="font-family: Menlo;"><span class=""><font size="2" class=""> def imm_2 : GIPredicatedDefKind<(isTwo imm)>;</font></span></span></div><div class=""><span class=""><span class=""><div style="font-size: small; font-family: Helvetica;" class=""><span style="font-family: Menlo;" class=""> def : GICombineRule<(defs root:$D, reg:$A, imm_2:$VAL),</span></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><font face="Menlo" class=""> (match [{MIR %D = G_MUL %A, %VAL }])</font></span></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><font face="Menlo" class=""> (apply [{MIR %root = MYTGT_DOUBLE</font><span class=""> %A }])>;</span></span></div><div style="font-size: small;" class=""><span class=""><span class="">These are effectively appended to the match section.</span></span></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><span class=""><br class=""></span></span></div><div style="font-size: small;" class="">Just matching an immediate isn't enough though. We'd also want to be able to use them in the result and modify them. This works the same way as registers. Here's an example of using the immediate in the apply step in a rule that replaces 2 * (A + B) with 2A + 2B:</div><div style="font-size: small;" class=""><span class=""><span class=""><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> def : GICombineRule<</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> (defs root:$root, reg:$A, imm:$B, imm_2:$C),</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> (match [{MIR</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> %1 = G_ADD %A, %B</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> %root = G_MUL %1, %C</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> }]),</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> (apply [{MIR %1 = G_ADD %A, %A</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> %2 = G_ADD %B, %B</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> %root = G_ADD %1, %2 }])>;</div><div class="">and of course, we'd also want to constant fold the 2B which is achieved in this rule by creating a new operand before we generate the new instructions:</div><div class=""><span style="font-family: Menlo;" class=""> def : GICombineRule<</span></div><div class=""><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> (defs root:$root, reg:$A, imm:$B, imm_2:$C),</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> (match [{MIR</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> %1 = G_ADD %A, %B</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> %root = G_MUL %1, %C</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> }]),</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> (apply (create_imm [{ 2 * ${B}->getZExtValue() }], apint_value:$B):$NB,</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> [{MIR %1 = G_ADD %A, %A</div><div style="font-family: Menlo; margin: 0px; font-stretch: normal; line-height: normal;" class=""> %root = G_ADD %1, %NB }])>;</div><div style="margin: 0px; font-stretch: normal; line-height: normal;" class="">In this definition the (create_imm ...):$NB creates a new operand with MachineOperand::CreateImm(...) and names it $NB for use in the pattern. The code block inside the create_imm used as the argument to CreateImm(), and the apint_value:$B converts the operand to an APInt before being given to the code block much like the custom predicates did earlier.</div></div></span></span></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><span class=""><br class=""></span></span></div><div style="font-size: small;" class=""><span class=""><span class=""><u class="">Passing arbitrary data from match to apply</u></span></span></div><div style="font-size: small;" class=""><span class=""><span class=""><u class=""><br class=""></u></span></span></div><div style="font-size: small;" class=""><span class=""><span class="">As mentioned earlier, the defs block defines the interface between the match and apply steps. This can be used to arrange for arbitrary data to be passed from match to apply. </span></span></div><div style="font-size: small;" class=""><span class=""><span class=""><br class=""></span></span></div><div style="font-size: small;" class=""><span class=""><span class="">In the current AArch64PreLegalizeCombiner we have a rule that matches a G_LOAD and all its uses simultaneously and decides on the best way to rewrite it to minimize the sign/zero/any-extend operations. This rule passes a struct (PreferredTuple) between the current C++ equivalent for the match to the current C++ equivalent to the apply. Converting that into this tablegen syntax, we'd write:</span></span></div><div style="font-size: small;" class=""><span class=""><span class=""><font face="Menlo" class=""> def extending_load_matchdata : GIDefMatchData<"PreferredTuple">;</font></span></span></div><div style="font-size: small;" class=""><span class=""><span class=""><div class=""><span style="font-family: Menlo;" class=""> def extending_load_predicate : GIMatchPredicate<</span></div><div class=""><span style="font-family: Menlo;" class=""> (ins reg:$A, </span><span style="font-family: Menlo;" class="">extending_load_matchdata</span><span style="font-family: Menlo;" class="">:$B), bool, [{</span></div><div class=""><span style="font-family: Menlo;" class=""> return </span><span style="font-family: Menlo;" class="">Helper.matchCombineExtendingLoads(${A}, ${matchinfo});</span></div><div class=""><span style="font-family: Menlo;" class=""> }]>;</span></div></span></span></div><div style="font-size: small;" class=""><span class=""><span class=""><font face="Menlo" class=""> def extending_loads : GICombineRule<<br class=""> (defs root:$root, reg:$A, extending_load_matchdata:$matchinfo),<br class=""> (match [{MIR %root = G_LOAD %A }],<br class=""> (extending_load_predicate root:$A,</font></span></span></div><div style="font-size: small;" class=""><span style="font-family: Menlo;" class=""> extending_load_matchdata:$matchinfo))</span><span style="font-family: Menlo;" class="">,</span></div><div style="font-size: small;" class=""><span class=""><span class=""><font face="Menlo" class=""> (apply (exec [{ Helper.applyCombineExtendingLoads(${root}, ${matchinfo}); }],</font></span></span></div><div style="font-size: small;" class=""><span class=""><span class=""><font face="Menlo" class=""> reg:$root, </font></span></span><span style="font-family: Menlo;" class="">extending_load_matchdata:$matchinfo</span><span style="font-family: Menlo;" class="">)>;</span></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><span class=""><br class=""></span></span></div><div style="font-size: small;" class=""><span class=""><span class="">The </span></span>GIDefMatchData declares a new type of data that can be passed from the match to the apply. Tablegen is responsible for arranging for appropriate storage during the Combine algorithm. The GIMatchPredicate declares a C++ predicate that fills out the PreferredTuple (passed by reference) whenever it returns true for a successful match. We could have made the predicate return std::pair<bool, PreferredTuple> instead but that's less efficient (it would be an sret return on many targets) and would require us to define the truthiness (no examples of are in this email as I expect it to be a rare thing to need) in order to act as a predicate. Normally, you'd feed this into a (create_imm ...) or a (create_operand ...) in the apply section. However, in this particular case the data being passed determines the entirety of the replacement so we escape into arbitrary C++ instead and arrange for the variables to be injected appropriately using the 'exec' operator.</div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><span class=""><br class=""></span></span></div><div style="font-size: small;" class=""><span class=""><span class=""><u class="">Macros</u></span></span></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><span class=""><u class=""><br class=""></u></span></span></div><div style="font-size: small;" class="">I simplified the previous example a bit. Rather than only matching a G_LOAD, the current rule in AArch64 can match any of G_LOAD, G_SEXTLOAD, and G_ZEXTLOAD. We need some means to match one of several alternatives as well as collect and re-use common subpatterns. I've yet to look into how this would be practically implemented and this section is a bit vague as a result but here's the current thinking on how it should look and behave:</div><div style="font-size: small;" class=""><div class=""><span class=""><span class=""><font face="Menlo" class=""> def ANYLOAD : GIMacro<</font></span></span><span style="font-family: Menlo;" class="">(defs def:$R, use:$S, uint64_t:$IDX),</span></div><div class=""><span class=""><span class=""><font face="Menlo" class=""> (match (oneof [{MIR %R = G_LOAD %S}],</font></span></span></div><div class=""><span class=""><span class=""><font face="Menlo" class=""> [{MIR %R = G_SEXTLOAD %S}],</font></span></span></div><div class=""><span class=""><span class=""><font face="Menlo" class=""> [{MIR %R = G_ZEXTLOAD %S}]</font></span></span><span style="font-family: Menlo;" class="">):$IDX>;</span></div></div><div style="font-size: small;" class=""><div class=""><span class=""><span class=""><font face="Menlo" class=""> def extending_loads : GICombineRule<<br class=""> (defs root:$root, reg:$A, extending_load_matchdata:$matchinfo, ANYLOAD:$ANYLOAD),</font></span></span></div><div class=""><span class=""><span class=""><font face="Menlo" class=""> (match [{MIR %root = ANYLOAD %A }],<br class=""> (extending_load_predicate root:$A,</font></span></span></div><div class=""><span style="font-family: Menlo;" class=""> extending_load_matchdata:$matchinfo))</span><span style="font-family: Menlo;" class="">,</span></div><div class=""><span class=""><span class=""><font face="Menlo" class=""> (apply (exec [{ Helper.applyCombineExtendingLoads(${root}, ${matchinfo}); }],</font></span></span></div><div class=""><span class=""><span class=""><font face="Menlo" class=""> reg:$root, </font></span></span><span style="font-family: Menlo;" class="">extending_load_matchdata:$matchinfo</span><span style="font-family: Menlo;" class="">)>;</span></div><div class="">Effectively, we're declaring a fake instruction and import it into this rule, possibly renaming it using the argument name ($ANYLOAD in this case) to provide a more convenient name in the MIR block or to disambiguate multiple instances. Once we've parsed the MIR, we would recursively replace any instance of ANYLOAD with code match one of the alternatives. The variables in the 'defs' section of the macro would be available as $ANYLOAD_R, $ANYLOAD_S, and $ANYLOAD_IDX (we have to use '_' instead of a '.' to fit within tablegen's syntax) with $ANYLOAD_IDX indicating which alternative the (oneof ...) matched. When nested by including a macro in the macro's defs section and using it, the names to access the sub-macros variables would grow longer by underscore separated concatenation, for example '$ANYLOAD_ANYEXT_A'.</div></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><span class=""><br class=""></span></span></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><span class=""><div style="font-family: Helvetica;" class=""><u class="">'Upside-down' matches (i.e. roots at the top) and similar</u></div><div style="font-family: Helvetica;" class=""><font face="Menlo" class=""><br class=""></font></div><div style="font-family: Helvetica;" class="">This one requires algorithm changes which I'd prefer not to discuss in this RFC. Assuming the underlying algorithm gains support for this, this is how the syntax would look:</div><div style="font-family: Helvetica;" class=""><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class="">def : GICombineRule<</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (defs root:$root, reg:$A),</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (match [{MIR %1 = G_LOAD %root</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> %A = G_SEXT %1 }]),</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (apply [{MIR %A = G_SEXTLOAD %root }])>;</div></div><div style="font-family: Helvetica;" class=""><br class=""></div><div style="font-family: Helvetica;" class="">The only unusual thing about this rule is that the root isn't at the bottom. Instead of starting at a use and matching towards defs, we're starting at the def and matching towards uses. This has some potentially useful properties. The combine algorithm has to chose an insertion point for the replacement code and the traditional choice has been the root of the match. Assuming we keep doing the same thing, 'upside-down' matching like this is able to avoid the checks that the load is safe to move, is non-volatile, has one use, etc. that would be necessary if we moved the G_LOAD down to the G_SEXT. Also, assuming we keep the same broadly bottom-up processing order as the existing Combine algorithm this kind of rule also has relatively lower priority than conventional rules because the root is seen later. This can be useful as (algorithm-dependent) the MIR may be more settled by the time it tries to match.</div><div style="font-family: Helvetica;" class=""><br class=""></div><div style="font-family: Helvetica;" class="">Along the same lines, the syntax can potentially support the root being in the middle of a match. This could be used in a similar way to the upside-down match above to control the insertion point and priority. For example:</div><div style="font-family: Helvetica;" class=""><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class="">def : GICombineRule<</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (defs root:$root, reg:$A, reg:$B, reg:$C),</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (match [{MIR %1(s32) = G_TRUNC %A(s64)</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> %2(s32) = G_TRUNC %B(s64)</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> %root = G_ADD %1, %2</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> %C(s64) = G_SEXT %root }]),</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (emit [{MIR %root = G_ADD %A, %B</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> %C = G_SEXT_INREG %root }])>;</div></div><div style="font-family: Helvetica;" class="">Unfortunately, I don't have any concrete examples where this would be useful in comparison to a conventional or upside-down match at the moment. I'm mostly keeping the door open as I can see potential for benefits (mostly w.r.t sinking and hoisting safety around an instr that would be difficult to test for that) given an appropriate rule and also because I'm inclined to say that the tablegen syntax shouldn't be the reason it's not possible. It should be up to the Combine algorithm and and tablegen-erate pass involved in specializing the algorithm for a target.</div></span></span></div><div style="font-size: small; font-family: Helvetica;" class=""><span class="" style="font-family: Menlo;"><span class=""><br class=""></span></span></div><div style="font-size: small;" class=""><u class="">Multiple roots</u></div><div style="font-size: small;" class=""><font face="Menlo" class=""><br class=""></font></div><div style="font-size: small;" class="">This one requires algorithm changes which I'd prefer not to discuss in this RFC. Assuming the underlying algorithm gains support for this, this is how the syntax would look:</div><div style="font-size: small;" class=""><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class="">def : GICombineRule<</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (defs root:$root1, root:$root2, reg:$A, reg:$B),</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (match [{MIR %root1 = G_ADD %A, %B }],</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> [{MIR %root2 = G_SUB %A, %B }]),</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo;" class=""> (emit [{MIR %root1, %root2 = BUTTERFLY %A, %B }])>;</div></div><div style="font-size: small;" class="">This would match a G_ADD and G_SUB with operands in common and combine them into a BUTTERFLY operation. You can think of this as two normal rules, one with %root1 as the root and the other with %root2 as the root.</div><div style="font-size: small;" class=""><br class=""></div><div class=""><u class=""><font size="4" class="">Grouping and Ordering Rules</font></u></div><div style="font-size: small;" class=""><br class=""></div><div style="font-size: small;" class="">Combine rules are ordered and grouped using definitions of the form:</div><div style="font-size: small;" class=""><font face="Menlo" class=""> def trivial_combines : GICombineGroup<[copy_prop]>;</font></div><div style="font-size: small;" class=""><font face="Menlo" class=""> def combines_for_extload: GICombineGroup<[extending_loads]>; </font></div><div style="font-size: small;" class=""><font face="Menlo" class=""> def all_combines : GICombineGroup<[trivial_combines, combines_for_extload]>; </font></div><div style="font-size: small;" class="">Essentially, we create a long ordered list of all the combines. Tablegen is free to re-order rules so long as the resulting ruleset always behaves as if it were in this specific order. So for example, the list [sext_trunc_sext, zext_trunc_zext, sext_sext, zext_zexts] is free to re-order to [sext_trunc_sext, sext_sext, zext_trunc_zext, zext_zexts] because the rules involved are mutually exclusive due to the root opcode.</div><div style="font-size: small;" class=""><br class=""></div><div style="font-size: small;" class="">So why not make tablegen figure out the order like ISel does? ISel attempts to figure out an order using an scoring system called Complexity along with a hack (AddedComplexity) to allow the user to provide magic numbers to fix the cases it got it wrong. The simplest way to confuse it is with patterns like (add s32, complexpattern1) and (add s32, complexpattern2). These have the same Complexity score but in truth, each one has a (possibly overlapping) range of complexity depending on what C++ code is inside the complexpattern's and which path through that C++ is dynamically taken. If it matches nothing then the score should be 0 but if it matches a dozen nodes it should be 12 (or possibly higher). We don't know which until we try to match that specific pattern. Correctly figuring out an order in the presence of complexpatterns is impossible. Similarly, it's also possible to confuse it with patterns that differ but overlap and add up to the same complexity due to quirks of the scoring system.</div><div style="font-size: small;" class=""><br class=""></div><div class=""><u class=""><font size="4" class="">Declaring Combine Passes</font></u></div><div style="font-size: small;" class=""><br class=""></div><div style="font-size: small;" class="">Combiner passes are defined by subclassing GICombiner like so:</div><div style="font-size: small;" class=""><font face="Menlo" class=""> def CommonPreLegalizerCombiner: GICombiner<<br class=""> "</font><span style="font-family: Menlo;" class="">Common</span><font face="Menlo" class="">GenPreLegalizerCombiner", [all_combines]> {<br class=""> let DisableRuleOption = "common-prelegalizercombiner-disable-rule";</font></div><div style="font-size: small;" class=""><font face="Menlo" class=""> let Modifiers = [</font><span style="font-family: Menlo;" class="">(disable_rule copy_prop)</span><span style="font-family: Menlo;" class="">]</span></div><div style="font-size: small;" class=""><font face="Menlo" class=""> }</font></div><div style="font-size: small;" class=""><font face="Menlo" class=""><br class=""></font></div><div style="font-size: small;" class="">This causes tablegen to create a class named '<span style="font-family: Menlo;" class="">CommonPreLegalizerCombiner</span>' which you can use to perform combines. This combiner contains all the combines mentioned in the previous section because it includes the 'all_combines' group. However, it disables the copy_prop rule to prevent it from attempting to match. I'll discuss that a bit more below. It also generates a command-line option for asserts builds only which can be used to further disable rules at run-time which will be useful for tracking down bugs or for testing a rule in isolation. I'm hoping that one day tools like bugpoint will be able to search through the individual rules within a combine pass when searching for a minimal reproducer.</div><div style="font-size: small;" class=""><br class=""></div><div style="font-size: small;" class="">The Modifiers field is intended to allow targets to modify an existing ruleset (particularly a target independent one) with additional target specific quirks. For example, one particular rule might be doing more harm than good and should be disabled. Or maybe only a subset of the things it would normally match are safe in which case an extra predicate should be tested. Being able to make minor edits to the ruleset without taking on the whole maintenance burden of the common code or causing code bloat by duplicating tables would be useful for targets that are generally similar to the targets within LLVM but have minor quirks.</div><div style="font-size: small;" class=""><br class=""></div><div style="font-size: small;" class="">Larger scale changes should take an alternate approach to modifiers. It's expected that even targets that are very different from the rest of the pack still have features in common. Such targets can declare their own combiner to replace the common one but still generally make use of the improvements made by the wider community. This is where GICombinerGroup will start to shine as such a target can declare a combiner like so:</div><div style="font-size: small;" class=""><div class=""><font face="Menlo" class=""> def MyTargetPreLegalizerCombiner: GICombiner<<br class=""> "</font><span style="font-family: Menlo;" class="">MyTarget</span><font face="Menlo" class="">GenPreLegalizerCombiner",</font></div><div class=""><font face="Menlo" class=""> [common_extend_optimizations,</font></div><div class=""><font face="Menlo" class=""> common_extending_loads,</font></div><div class=""><font face="Menlo" class=""> // common_rsqrt_and_nr_iterations, // This target has a real sqrt operation</font></div><div class=""><span style="font-family: Menlo;" class=""> mytarget_special_fma,</span></div><div class=""><font face="Menlo" class=""> common_fma,</font></div><div class=""><span style="font-family: Menlo;" class=""> common_bswap_idioms,</span></div><div class=""><span style="font-family: Menlo;" class=""> mytarget_bcd_arithmetic</span></div><div class=""><span style="font-family: Menlo;" class=""> ]> {</span></div><div class=""><font face="Menlo" class=""> }</font></div></div><span style="font-size: small;" class="">As LLVM improves on the common_* groups, the target benefits from those improvements automatically. However, it doesn't benefit from new groups being added to common_all_combines because it's no longer using that group so entirely new categories of combines added to it would not appear in the combiner. It would still be nice to find out that a new category has appeared though so that a decision can be made on it. To that end, I'm considering adding:</span></span></span></div><div class=""><span class=""><span class=""><span style="font-size: small;" class=""><font face="Menlo" class=""> let Verify = [(has_all_of_except all_combines, common_rsqrt_and_nr_iterations)];</font><br class=""></span><span style="font-size: small;" class="">which would cause a warning if something new appeared in the original group.</span></span></span></div><div class=""><span class=""><span class=""><span style="font-size: small;" class=""><br class=""></span></span></span></div><div class=""><span class=""><span class=""><font size="4" class=""><u class="">Conclusion</u></font></span></span></div><div class=""><span class=""><span class=""><font size="4" class=""><br class=""></font></span></span></div><div class=""><span class=""><span class=""><font size="2" class="">There is a lot of work that needs to be done to get all this working and some of it may have to change once it runs into the reality of implementation :-). However, we think that this will prove to be a very convenient and powerful syntax with some potential a variety of tools from profilers, to bug reducers, to correctness checking tools.</font></span></span></div><div class=""><span class=""><span class=""><font size="2" class=""><br class=""></font></span></span></div><div class=""><span class=""><span class=""><font size="2" class="">There is a patch at <a href="https://reviews.llvm.org/" class="">https://reviews.llvm.org/</a></font></span></span><font size="2" class="">D54286 </font><span style="font-size: small;" class="">makes a start on it to try out the general feel of the syntax but currently lacks the core feature of generating match and apply code from the MIR. I'm going to be looking into that over the next few weeks.</span></div></body></html>