I was trying to understand how intrinsics are implemented in LLVM. In Intrinsics.td file at path
llvm/IR/Intrinsics.td, following commenta are present for the class LLVMatchType :
// Match the type of another intrinsic parameter. Number is an index into the
// list of overloaded types for the intrinsic, excluding all the fixed types.
// The Number value must refer to a previously listed type. For example:
// Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_anyfloat_ty, LLVMMatchType<0>]>
// has two overloaded types, the 2nd and 3rd arguments. LLVMMatchType<0>
// refers to the first overloaded type, which is the 2nd argument.
- What is meant by ‘fixed types’ here?
- What are ‘overloaded types’ and how can we do overloading in tablegen?
- In the example give in these comments, does ‘LLVMMatchType<0>’ refers to ‘llvm_anyfloat_ty’? If yes, why ‘LLVMMatchType<0>’ has been used while we could have just used ‘llvm_anyfloat_ty’ directly?
“Fixed types” are the ones that cannot change, like plain
llvm_i32_ty. You don’t need to use them with
LLVMMatchType because you can just write down the original type again and get exactly the same thing.
“Overloaded types” are the ones that can take multiple values and the user has to decide what the actual type is (within the constraints) when creating the intrinsic. In that example there’s
llvm_anyfloat_ty a second time would mean the types don’t have to be the same, but often intrinsics need their two operands (& result) to be the same type. So for a contrived example we could have
def increment : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>], >;
def bad_increment : Intrinsic<[llvm_anyfloat_ty], [llvm_anyfloat_ty], >;
Then the only declarations you could write for the first one have the argument and return types the same. They’d look like
declare float @llvm.increment.f32(float %in). But for the second you could write
declare float @llvm.bad.increment.f32.f64(double) which the writer may not want to deal with.