Reducing .td redundancy

Is it legal to do something like a !strconcat on a non-string entity? That
is, is there some operation that will let me do this (replace SOME_CONCAT with
an appropriate operator):

(WARNING! Hacked-up tablegen ahead!)

multiclass sse_fp_binop_bitwise_rm<bits<8> opc, string OpcodeStr,
                                    SDNode OpNode> {
  // Vector operation emulating scalar (fp)
  def FsPSrr : PSI<opc, MRMSrcReg, (outs FR32:$dst), (ins FR32:$src1, FR32:
$src2),
                      !strconcat(OpcodeStr, "ps"\t{$src2, $dst|$dst, $src2}"),
                      [(set FR32:$dst, (!SOME_CONCAT("x86f", OpNode) FR32:
$src1, FR32:$src2))]>;

  // Vector operation
  def PSrr : PSI<opc, MRMSrcReg, (outs VR128:$dst), (ins VR128:$src1, VR128:
$src2),
                      !strconcat(OpcodeStr, "ps"\t{$src2, $dst|$dst, $src2}"),
                      [(set VR128:$dst, v2i64 (OpNode VR128:$src1, VR128:
$src2))]>;

  // Bitconverted vector operation
  def PSrm : PSI<opc, MRMSrcMem,
                    (outs VR128:$dst), (ins VR128:$src1, f128mem:$src2),
                    !strconcat(OpcodeStr, "ps\t{$src2, $dst|$dst, $src2}"),
                    [(set VR128:$dst, (OpNode (bc_v2i64 (v4f32 VR128:$src1)),
                                       (memopv2i64 addr:$src2)))]>;
// ...
}

defm AND : ...
defm OR : ...

I suspect we could get rid of a lot of redundancy if SOME_CONCAT could really
work. I've run into a few places where I think something like this could be
used.

                                             -Dave

Is it legal to do something like a !strconcat on a non-string entity? That
is, is there some operation that will let me do this (replace SOME_CONCAT with
an appropriate operator):

I don't get it, can you try a simpler example on me? :slight_smile:

-Chris

> Is it legal to do something like a !strconcat on a non-string
> entity? That
> is, is there some operation that will let me do this (replace
> SOME_CONCAT with
> an appropriate operator):

I don't get it, can you try a simpler example on me? :slight_smile:

Ok, let see if I can construct something.

From the IntrinsicsX86.td file:

  def int_x86_sse_add_ss : GCCBuiltin<"__builtin_ia32_addss">,
              Intrinsic<[llvm_v4f32_ty, llvm_v4f32_ty,
                         llvm_v4f32_ty], [IntrNoMem, Commutative]>;

  def int_x86_sse2_add_sd : GCCBuiltin<"__builtin_ia32_addsd">,
              Intrinsic<[llvm_v2f64_ty, llvm_v2f64_ty,
                         llvm_v2f64_ty], [IntrNoMem, Commutative]>;

Untested multiclass! Look for SOME_CONCAT.

multiclass myintrinsics<bits<8> opc, string OpcodeStr, Intrinsic Intr> {
   // Scalar intrinsics
  def SSrr_Int SSI<opc, MRMSrcReg, (outs FR32:$dst), (ins FR32:$src1, FR32:
$src2),
                 !strconcat(OpcodeStr, "ss\t{$src2, $dst|$dst, $src2}"),
                 [(set FR32:$dst, (SOME_CONCAT(Intr, _ss) FR32:$src1, FR32:
$src2))]> {

  def SDrr_Int SSI<opc, MRMSrcReg, (outs FR64:$dst), (ins FR64:$src1, FR64:
$src2),
                 !strconcat(OpcodeStr, "ss\t{$src2, $dst|$dst, $src2}"),
                 [(set FR32:$dst, (SOME_CONCAT(Intr, _sd) FR32:$src1, FR32:
$src2))]> {
}

defm ADD : myintrinsics<0x58, "add", int_x86_sse2_add>;

I want the single ADD defm to generate patterns for both the "ss" and "sd"
intrinsic variants. I need a way to append "_ss" or "_sd" to the intrinsic
name in the pattern.

I don't believe there's any way to do this right now but I'm looking into it.
I'm currently stepping through TableGen to figure out when pattern fragments
(which get instantiated as CodeInits if I understand correctly) have their
arguments substituted. At that point I want to scan the pattern and look
for special concat operations and munge the resulting strings / names /
whatever to do the right thing. Any hints on where to look?

                                         -Dave

So I figured out the magic happens in VarInit::resolveReferences as called
by TGParser::ParseDefm. Now I have to figure out how to substitute a
different Init for the one returned by R:

/// resolveReferences - This method is used by classes that refer to other
/// variables which may not be defined at the time they expression is formed.
/// If a value is set for the variable later, this method will be called on
/// users of the value to allow the value to propagate out.
///
Init *VarInit::resolveReferences(Record &R, const RecordVal *RV) {
  if (RecordVal *Val = R.getValue(VarName))
    if (RV == Val || (RV == 0 && !dynamic_cast<UnsetInit*>(Val->getValue())))
       /******* Do some trickiness here to munge the result ********/
      return Val->getValue();
  return this;
}

I was thinking about using a ## paste operator to indicate that names should
be glued together. Let's say as in our example, the value for Intr is
int_x86_sse2_add. That will be the value returned by
VarInit::resolveReferences. How do I get a pointer to the Init for, say,
int_x86_sse2_add_ss (the value I want after concatenation) so I can return
that instead? Can I just create one? My assumption is that Inits are unique
objects and that creating duplicates would be a bad idea.

                                                 -Dave

Ah, I see what you mean. Sure, you could add an operator like !nameconcat(x,y) that does what you want. I'd start it could with a simple testcase (see the examples in llvm/test/TableGen for examples). This should work just like strconcat.

-Chris