[llvm-commits] [llvm] r137232 - in /llvm/trunk/utils/TableGen: Record.cpp Record.h TGParser.cpp

David A. Greene greened at obbligato.org
Thu Aug 11 10:03:30 PDT 2011


Chris Lattner <clattner at apple.com> writes:

>> It's clunky and
>> requires special processing in TGParser.  This change and some related
>> ones I have coming can be used to replace #NAME# with strconcat
>> operators.  Then the processing falls out natually from how operators
>> work.
>
> How is it clunky, why is this better?

It's clunky because it's a special case of !strconcat() which requires
special processing at particular points in TGParser.

> Makes sense, but please give me a concrete example of what this
> enables (i.e. "before and after" .td snippets), not an abstract idea
> :)

Fair enough.  Here's a moderately complex example.  Take HADDPD.  The
way we have this specified in our X86InstrSIMD.td file looks like
this:

defm HADDPD :
  vs3xy_fp_binary_vv_intrinsic_rm<0x7C, "haddpd", "hadd_pd", X86f64>;

All variants of HADD look like this.  There is not a pattern in sight
for HADD in the top-level .td file.  Almost every x86 SIMD instruction
that maps to an intrinsic matches a pattern with one of just a few
forms.  Typically something like the binary rm form:

  [(set (DstType DstRegClass:$dst),
        (DstType (Intr (Src1Type SrcRegClass:$src1),
                       (SrcMemOptn addr:$src2))))]),

This pattern is contained in the very lowest-level of the .td class
heirarchy and DstType, DstRegClass, etc. are parameters to it.  It is
reused for tons of instructions and is general enough to cover the x86
scalar instructions as well if so desired.  We just use it for the SIMD
stuff at the moment.  We write this pattern once and reuse it over and
over again rather than having multiple versions of the pattern, each for
a different combination of type, register class, intrinsic name, etc.

This pattern gets used for every single HADD variant.  It's only ever
written once.  The same pattern is likely to be reusable for future ISA
extensions as well.

Compare to how all the HADD variants are described in X86InstrSSE.td
today.  There is a lot of reuse there but a lot is left on the table,
too, leading to redundancy and in some cases inconsistency.

vs3xy_fp_binary_vv_intrinsic_rm derives from a few multiclasses in an
attempt to share as much implementation detail as possible.  At the
root multiclass level we have a class that looks like this:

multiclass b_vx_binary_vv_intrinsic_rrm<
  [...],
  string defrrsfx = "",
  string defrmsfx = "",
  [...]
> {
  // AVX reg+reg+reg
  def !strconcat(!strconcat("Vx",#DEFM#),
                 !strconcat(!strconcat(!toupper(defrrsfx), "rrr"), "_Int"))
     : i_v_binary_i_rrr<[...]> { ... }

  // AVX reg+reg+mem
  def !strconcat(!strconcat("Vx",#DEFM#),
                 !strconcat(!strconcat(!toupper(defrmsfx), "rrm"), "_Int"))
     : i_v_binary_i_rrm<[...]> { ... }

  [...]
}

The name of this multiclass is a clue to its use.  This multiclass
covers all 128-bit AVX vector/vector binary instructions that have an
intrinsic form.

#DEFM# here playes a similar role to #NAME# in that it specifies the
user-provided identifier of the top-level defm.  The difference is that
while #NAME# works like an operator (#NAME# was named so to be
reminiscent of the C preprocessor paste operation), #DEFM# is purely a
record field.  The actual name concatenation is left to the !strconcat()
operator.  Thus there is only one place in TableGen where we need to
worry about string concatenation.

This one multiclass handles 67 x86 AVX instructions, including those
that have MMX, SSE2, SSE3 and SSE41 analogues as well as some "pure"
AVX-only instructions.  It can do this because the def name is computed
at TableGen time, producing a custom set of defs for each instruction
based on the suffix (ss, sd, ps, pd, b, w, etc.) information passed to
the multiclass.  Without this capability we would either have to write
multiple multiclasses, one for each kind of suffix, or have many more
defs inside this multiclass, one per suffix.  Either way, it's
redundant.  Computing the def name this way means we only have to write
a handful of defs to cover a large number of instructions.

I'm not totally happy with all of the !strconcat() stuff.  An infix
notation would be easier to read.  But I figure that can be added later
if desired.  The key point to remember is that adding instructions
should not require tinkering with these low-level classes.  The intent
is to write them once and forget about them.  They're designed to be
flexible and reusable.  They're more complex but the complexity is
hidden by the subclasses in the hierarchy.  The intent is to make the
top-level .td as simple and straightforward as possible.

I expect lots of questions.  Fire away!  :)

                              -Dave



More information about the llvm-commits mailing list