Intrinsic parameters verification

Hi,

I have target specific intrinsics (X86 in my case) with special constant parameters.
Rounding mode constant, or scale value in gather/scatter. The scale, for example, may be 0, 1, 2, 4, or 8 only.
How do I verify the values on IR level ?

I’m looking at Verifier::visitIntrinsicFunctionCall()
but I see only common intrinsics here, not target specific.

Thank you.

  • Elena

Hi Elena,

I think the checks are done by the front-end, then if the values provided to an intrinsic do not work for the IR, the backend aborts with cannot select.
I may be wrong of course, this is my recollection of how the ARM backend work for neon intrinsics.

The bottom line is you may want to ask this question to cfe dev.

Cheers,
Q.

FWIW, this is kind of lame, but this is how that work!

Q.

This is correct. clang allows defining a builtin, which will eventually map to an intrinsic call and allows you to enforce that the parameter folds to a constant integer. However, AFAIK there isn't an easy way to restrict the values there.

-Matt

Hey, that's a nice project! A target-specific verifier that scans for
all builtins and makes sure they conform to what the back-end accepts.

Not sure how they would stay relevant with back-end changes, though...
Maybe table-gen'ed?

Surely interesting to try next GSOC.

--renato

I’m looking at Verifier::visitIntrinsicFunctionCall()
but I see only common intrinsics here, not target specific.

Hey, that's a nice project! A target-specific verifier that scans for
all builtins and makes sure they conform to what the back-end accepts.

+1.

This is also something the IR optimizers have had to take in to consideration. For example, see SimplifyCFG SinkThenElseCodeToEnd which won’t sink intrinsic calls in if/else blocks to the end block if it would replace a constant parameter with a non-constant. Having some way to at least verify after that this has happened would be great. Having a way to annotate the intrinsic so that this code can check when its valid or not would be even better.

Not sure how they would stay relevant with back-end changes, though...
Maybe table-gen’ed?

Tagging the intrinsics with(for example) which parameters must be constants is ideal I think, as then we could even verify that the backend patterns don’t try to match a constant intrinsic parameter with a register. Then we know that the backend is consistent with the intrinsic definitions.

Cheers,
Pete

Maybe table-gen’ed?

Oh! This is a good idea I was looking for!

I can add a new parameter type, something like this:

class LLVMConstEnum<list<int> values> : LLVMType<i32> {
  list<int> Values = values;
}

And target specific::
def ScaleEnum : LLVMConstEnum<[0, 1, 2, 4, 8]>;

And use it:

  def int_x86_avx512_gather_dpd_512 : GCCBuiltin<"__builtin_ia32_gathersiv8df">,
          Intrinsic<[llvm_v8f64_ty], [llvm_v8f64_ty, llvm_ptr_ty,
                     llvm_v8i32_ty, llvm_i8_ty, ScaleEnum], [IntrReadArgMem]>;

And then I can add:

  if (R->isSubClassOf("LLVMConstEnum")) {
    Sig.push_back(IIT_ENUM);
    std::vector<int64_t> Values = R->getValueAsListOfInts("Values");
    Sig.push_back(Values.size()); // Number of values
    for (int64_t Val : Values)
      Sig.push_back((char)Val);
  }

The only issue here that the enumerator max value is 255, otherwise I can't put in "unsigned char". (Or I should split into 4 entries)

- Elena

I’m looking at Verifier::visitIntrinsicFunctionCall()
but I see only common intrinsics here, not target specific.

Hey, that's a nice project! A target-specific verifier that scans for
all builtins and makes sure they conform to what the back-end accepts.

+1.

+1. I also have an out of tree use which would benefit from such a thing.

I'd argue that we should start small here. Rather than asking for someone to write a tablegen based one, why don't we start with a bit of hand written c++ code for the examples at hand and then tablegen it once we have a couple examples?