LLVM’s current model for overloaded intrinsics use broad type categories such as LLVMAnyType and LLVMMatchType. These categories accept almost any type, which makes intrinsics flexible, but opens up a few practical limitations:
- Late & Per-Intrinsic Basis Error Handling: Type validation occurs only during lowering on a per-intrinsic basis and cannot express target-specific type restrictions. As a result, LLVM may accept syntactically valid IR that later fails during lowering, causing late and hard-to-recognize errors (mostly in the backends).
- Uninformative Diagnostics: When a type mismatch does occur, the emitted diagnostic is frequently generic, offering little guidance on what the valid type set actually is.
This RFC proposes a fine-grained type constraint mechanism for overloaded LLVM intrinsics, by allowing them to explicitly declare permitted types, enabling IR-level verification of these constraints. To support this, a constrained type class is added:
AnyTypeOf<[list of LLVMType]>: Restricts types to a specified subset.
Features
- Backward Compatibility: Existing overloaded intrinsics workflow remains unchanged.
- No IR or .ll File Impact: Introducing constrained type class does not alter existing LLVM IR. Intrinsics that currently rely on LLVMAnyType continue to parse and behave as before, and the AnyTypeOf constraint affects only verification, not the structure or semantics of the emitted IR.
- Error Detection & Handling: Type violations are caught during IR verification as part of LLVM’s existing Verifier pipeline with precise error messages.
- Precise Type Control in TableGen: Overloaded intrinsic definitions can now specify exact allowed type subsets directly in TableGen. This provides a clear, declarative way to express type restrictions for overloaded intrinsics.
Examples
def foo.bar.example1 : Intrinsic<[llvm_i32_ty],
[AnyTypeOf<[llvm_i16_ty, llvm_i32_ty]>],
[IntrNoMem]>;
def foo.bar.example2 : Intrinsic<[AnyTypeOf<[llvm_float_ty, llvm_double_ty]>],
[LLVMMatchType<0>],
[IntrNoMem]>;
def foo.bar.example3 : Intrinsic<[llvm_i16_ty],
[AnyTypeOf<[llvm_ptr_ty, llvm_shared_ptr_ty]>, llvm_i32_ty],
[IntrNoMem]>;
def foo.bar.example4 : Intrinsic<[llvm_i16_ty],
[llvm_float_ty, AnyTypeOf<[llvm_i16_ty, llvm_v4i32_ty, llvm_v4f32_ty]>, AnyTypeOf<[llvm_i32_ty, llvm_float_ty]>],
[IntrNoMem]>;
The following error is emitted when an invalid type is provided:
“Intrinsic declaration ‘foo.bar.example1’ violates type constraint: Parameter 0 type ‘i8’ not in allowed types”
A prototype implementation has been developed including:
- TableGen support for specifying type constraints
- IR-level verification logic integrated into the LLVM Verifier
- Detailed diagnostic reporting for constraint violations
For those interested in the implementation details, a draft PR is available here: [LLVM-Tablegen] Explicit Type Constraints for Overloaded LLVM Intrinsics by DharuniRAcharya · Pull Request #172442 · llvm/llvm-project.