[HLSL] Implement TableGen for builtin HLSL intrinsics (#187610)

This PR introduces a TableGen-based code generation system for HLSL
intrinsic overloads as described in proposal
[[0043]](https://github.com/llvm/wg-hlsl/blob/main/proposals/0043-hlsl-intrinsic-tablegen.md)
for replacing hand-written boilerplate with declarative .td definitions.

Actual changes to `hlsl_intrinsics.h` and `hlsl_alias_intrinsics.h` to
replace handwritten HLSL intrinsic overloads with TableGen is left to
follow-up PRs.

Assisted-by: GitHub Copilot (powered by Claude Opus 4.6)
This commit is contained in:
Deric C. 2026-03-24 13:30:13 -07:00 committed by GitHub
parent 2ae3d8ae57
commit daec3b9fb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1415 additions and 1 deletions

View File

@ -0,0 +1,320 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines HLSL intrinsic functions in tablegen. The HLSLEmitter
// backend processes these definitions and generates two .inc files:
// - hlsl_alias_intrinsics_gen.inc: builtin alias declarations using
// _HLSL_BUILTIN_ALIAS, included by hlsl_alias_intrinsics.h.
// - hlsl_inline_intrinsics_gen.inc: inline function definitions (detail
// helper calls and literal bodies), included by hlsl_intrinsics.h.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Argument and return type base classes
//===----------------------------------------------------------------------===//
// Base class for argument and return type positions.
class HLSLArgType;
// Base class for return type positions.
class HLSLReturnType;
// Void return type.
def VoidTy : HLSLReturnType;
//===----------------------------------------------------------------------===//
// HLSL element types
//===----------------------------------------------------------------------===//
// Represents a concrete HLSL scalar element type.
// Can be used directly as an argument or return type for a fixed scalar
// (e.g., FloatTy in Args produces a 'float' argument).
class HLSLType<string name> : HLSLArgType, HLSLReturnType {
string Name = name;
string TypeName = name;
// When set, overloads using this type are guarded by
// #ifdef __HLSL_ENABLE_16_BIT and emitted with
// _HLSL_AVAILABILITY(shadermodel, 6.2), or the intrinsic's Availability
// if it is greater.
bit Is16Bit = 0;
// When set, overloads using this type are emitted with
// _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) instead of _HLSL_AVAILABILITY.
// This macro expands to an availability attribute only when
// __HLSL_ENABLE_16_BIT is defined (i.e. half is a true 16-bit float);
// otherwise it expands to nothing since half is an alias for float.
// If the intrinsic's Availability is >= SM6.2, _HLSL_AVAILABILITY is used
// instead because 16-bit support is already implied.
bit IsConditionally16Bit = 0;
}
def BoolTy : HLSLType<"bool">;
def HalfTy : HLSLType<"half"> { let IsConditionally16Bit = 1; }
def FloatTy : HLSLType<"float">;
def DoubleTy : HLSLType<"double">;
def Int16Ty : HLSLType<"int16_t"> { let Is16Bit = 1; }
def UInt16Ty : HLSLType<"uint16_t"> { let Is16Bit = 1; }
def IntTy : HLSLType<"int">;
def UIntTy : HLSLType<"uint">;
def Int64Ty : HLSLType<"int64_t">;
def UInt64Ty : HLSLType<"uint64_t">;
//===----------------------------------------------------------------------===//
// Element type groups
//===----------------------------------------------------------------------===//
defvar AllFloatTypes = [HalfTy, FloatTy, DoubleTy];
defvar SignedIntTypes = [Int16Ty, IntTy, Int64Ty];
defvar UnsignedIntTypes = [UInt16Ty, UIntTy, UInt64Ty];
defvar AllIntTypes = [Int16Ty, UInt16Ty, IntTy, UIntTy,
Int64Ty, UInt64Ty];
defvar SignedTypes = [Int16Ty, HalfTy, IntTy, FloatTy,
Int64Ty, DoubleTy];
defvar AllNumericTypes = [Int16Ty, UInt16Ty, HalfTy, IntTy, UIntTy,
FloatTy, Int64Ty, UInt64Ty, DoubleTy];
defvar AllTypesWithBool = [BoolTy, Int16Ty, UInt16Ty, HalfTy,
IntTy, UIntTy, FloatTy, Int64Ty,
UInt64Ty, DoubleTy];
defvar NumericTypesNoDbl = [Int16Ty, UInt16Ty, HalfTy, IntTy, UIntTy,
FloatTy, Int64Ty, UInt64Ty];
//===----------------------------------------------------------------------===//
// Argument/return types
//
// These classes are usable in both argument and return type positions.
//===----------------------------------------------------------------------===//
// The varying type expanded per VaryingTypes.
// As an argument: the arg type varies with each overload.
// As a return type: returns the same type as the varying arg.
def Varying : HLSLArgType, HLSLReturnType;
// The scalar element of the varying type.
// As an argument: always the scalar element type regardless of overload shape.
// As a return type: returns the scalar element type (e.g., float dot(float3, float3)).
def VaryingElemType : HLSLArgType, HLSLReturnType;
// The varying shape with a fixed element type.
// As an argument: same shape as Varying but with the given element type.
// As a return type: same shape as the varying arg but with the given element type.
// For example, VaryingShape<UIntTy> with a float3 overload produces uint3.
class VaryingShape<HLSLType ty> : HLSLArgType, HLSLReturnType {
HLSLType ElementType = ty;
}
// A concrete vector type (e.g., VectorType<UIntTy, 4> -> uint4).
// As an argument: the arg is always this vector type.
// As a return type: always returns this vector type, ignoring argument shape.
class VectorType<HLSLType ty, int size> : HLSLArgType, HLSLReturnType {
HLSLType ElementType = ty;
int Size = size;
}
//===----------------------------------------------------------------------===//
// Shader model versions
//===----------------------------------------------------------------------===//
// Represents a shader model version
class ShaderModel<int major, int minor> {
int Major = major;
int Minor = minor;
}
// Sentinel: no shader model requirement.
def NoSM : ShaderModel<0, 0>;
// Valid Shader Model records
foreach i = 0...9 in {
def SM6_ #i : ShaderModel<6, i>;
}
//===----------------------------------------------------------------------===//
// Matrix dimension records
//===----------------------------------------------------------------------===//
class MatDim<int rows, int cols> {
int Rows = rows;
int Cols = cols;
}
foreach r = 1...4 in
foreach c = 1...4 in
def Mat#r#"x"#c : MatDim<r, c>;
// All non-1x1 matrix dimensions (1x2 through 4x4).
defvar AllMatDims = [Mat1x2, Mat1x3, Mat1x4,
Mat2x1, Mat2x2, Mat2x3, Mat2x4,
Mat3x1, Mat3x2, Mat3x3, Mat3x4,
Mat4x1, Mat4x2, Mat4x3, Mat4x4];
//===----------------------------------------------------------------------===//
// HLSLBuiltin class
//===----------------------------------------------------------------------===//
class HLSLBuiltin<string name, string builtin = ""> {
string Name = name;
// When set, generates a _HLSL_BUILTIN_ALIAS(Builtin) declaration that
// aliases the named Clang builtin. Mutually exclusive with DetailFunc
// and Body.
string Builtin = builtin;
// Doxygen documentation comment emitted before overloads in generated code.
string Doc = "";
// When set, generates an inline function body calling
// __detail::DetailFunc(args...) instead of _HLSL_BUILTIN_ALIAS(Builtin).
// Parameters are named p0, p1, p2, ... by default, or use ParamNames to
// specify custom names. Mutually exclusive with Body and Builtin.
string DetailFunc = "";
// When set, generates an inline function with this literal body text.
// Intended for single-statement functions. Multi-line functions should
// instead be defined as a helper and called with DetailFunc.
// Parameters are named p0, p1, p2, ... by default, or use ParamNames to
// specify custom names. Mutually exclusive with DetailFunc and Builtin.
code Body = "";
// Determines how the return type is derived for each overload.
HLSLReturnType ReturnType = VoidTy;
// Argument list. Each entry is either:
// Varying - type varies with VaryingTypes (expanded per type)
// HLSLType - a fixed scalar type at that position (e.g., UIntTy)
// VectorType - a fixed vector type at that position
// The number of arguments is deduced from the length of this list.
// Examples:
// [Varying] -> func(T)
// [Varying, Varying, Varying] -> func(T, T, T)
// [Varying, UIntTy] -> func(T, uint)
// [UIntTy, UIntTy, IntTy] -> func(uint, uint, int)
// [] -> func()
list<HLSLArgType> Args = [];
// Custom parameter names for generated functions.
// When empty, inline functions (Body or DetailFunc) use p0, p1, p2, ...
// and alias functions omit parameter names.
list<string> ParamNames = [];
// When set, emits 'constexpr' instead of 'inline' for inline functions
// (i.e., functions using Body or DetailFunc).
bit IsConstexpr = 0;
// Whether the function has the convergent attribute
bit IsConvergent = 0;
// Argument element types drives overload expansion.
// One overload set is generated per type (scalar + vectors + matrices).
// Only used when Args contains Varying entries.
list<HLSLType> VaryingTypes = [];
// Whether to generate scalar overloads for Varying typed arguments.
bit VaryingScalar = 0;
// Vector sizes to generate for Varying typed arguments (e.g., [2,3,4]).
list<int> VaryingVecSizes = [];
// Matrix dimensions to generate for Varying typed arguments.
list<MatDim> VaryingMatDims = [];
// Default shader model availability version for all types.
// Use NoSM for no availability requirement.
ShaderModel Availability = NoSM;
}
//===----------------------------------------------------------------------===//
// HLSLBuiltin helper subclasses
//===----------------------------------------------------------------------===//
// T func(T) with scalar + vec2/3/4 + matrix overloads.
class HLSLOneArgBuiltin<string name, string builtin>
: HLSLBuiltin<name, builtin> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2, 3, 4];
let VaryingMatDims = AllMatDims;
}
// T func(T, T) with scalar + vec2/3/4 + matrix overloads.
class HLSLTwoArgBuiltin<string name, string builtin>
: HLSLBuiltin<name, builtin> {
let Args = [Varying, Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2, 3, 4];
let VaryingMatDims = AllMatDims;
}
// T func(T, T, T) with scalar + vec2/3/4 + matrix overloads.
class HLSLThreeArgBuiltin<string name, string builtin>
: HLSLBuiltin<name, builtin> {
let Args = [Varying, Varying, Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2, 3, 4];
let VaryingMatDims = AllMatDims;
}
// Detail function base: generates inline function bodies calling
// __detail::DetailFunc(args...) instead of _HLSL_BUILTIN_ALIAS.
class HLSLDetail<string name, string detail> : HLSLBuiltin<name> {
let DetailFunc = detail;
}
// T func(T) with scalar + vec2/3/4 + matrix overloads.
class HLSLOneArgDetail<string name, string detail>
: HLSLDetail<name, detail> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2, 3, 4];
let VaryingMatDims = AllMatDims;
}
// T func(T, T) with scalar + vec2/3/4 + matrix overloads.
class HLSLTwoArgDetail<string name, string detail>
: HLSLDetail<name, detail> {
let Args = [Varying, Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2, 3, 4];
let VaryingMatDims = AllMatDims;
}
// T func(T, T, T) with scalar + vec2/3/4 + matrix overloads.
class HLSLThreeArgDetail<string name, string detail>
: HLSLDetail<name, detail> {
let Args = [Varying, Varying, Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2, 3, 4];
let VaryingMatDims = AllMatDims;
}
// Inline body variant: T func(T) with a literal inline body (no builtin alias).
// Body must be specified (e.g., let Body = "return p0;").
class HLSLOneArgInlineBuiltin<string name> : HLSLBuiltin<name> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2, 3, 4];
let VaryingMatDims = AllMatDims;
}
//===----------------------------------------------------------------------===//
// Intrinsic definitions (sorted alphabetically by function name)
//===----------------------------------------------------------------------===//
// TODO: Convert hand-written overloads from hlsl_intrinsics.h and
// hlsl_alias_intrinsics.h into TableGen below.
// Include "hlsl_alias_intrinsics_gen.inc" in hlsl_alias_intrinsics.h
// Include "hlsl_inline_intrinsics_gen.inc" in hlsl_intrinsics.h

View File

@ -504,6 +504,14 @@ if(RISCV IN_LIST LLVM_TARGETS_TO_BUILD)
)
endif()
# Generate HLSL intrinsic overloads
clang_generate_header(-gen-hlsl-alias-intrinsics HLSLIntrinsics.td
hlsl/hlsl_alias_intrinsics_gen.inc)
clang_generate_header(-gen-hlsl-inline-intrinsics HLSLIntrinsics.td
hlsl/hlsl_inline_intrinsics_gen.inc)
set(hlsl_generated_files
"${CMAKE_CURRENT_BINARY_DIR}/hlsl/hlsl_alias_intrinsics_gen.inc"
"${CMAKE_CURRENT_BINARY_DIR}/hlsl/hlsl_inline_intrinsics_gen.inc")
# Check if the generated headers are included in a target specific lists
# Currently, all generated headers are target specific.
@ -511,6 +519,7 @@ set(all_target_specific_generated_files
${arm_common_generated_files}
${arm_only_generated_files}
${aarch64_only_generated_files}
${hlsl_generated_files}
${riscv_generated_files})
foreach( f ${generated_files} )
if (NOT ${f} IN_LIST all_target_specific_generated_files)
@ -579,7 +588,7 @@ add_header_target("x86-resource-headers" "${x86_files}")
add_header_target("gpu-resource-headers" "${gpu_files}")
# Other header groupings
add_header_target("hlsl-resource-headers" ${hlsl_files})
add_header_target("hlsl-resource-headers" "${hlsl_files};${hlsl_generated_files}")
add_header_target("spirv-resource-headers" ${spirv_files})
add_header_target("opencl-resource-headers" ${opencl_files})
add_header_target("llvm-libc-resource-headers" ${llvm_libc_wrapper_files})

View File

@ -38,6 +38,9 @@ namespace hlsl {
#define _HLSL_16BIT_AVAILABILITY_SHADERMODEL_DEFAULT()
#endif
// Generated by clang-tblgen from HLSLIntrinsics.td (alias intrinsics).
#include "hlsl_alias_intrinsics_gen.inc"
//===----------------------------------------------------------------------===//
// abs builtins
//===----------------------------------------------------------------------===//

View File

@ -13,6 +13,9 @@
namespace hlsl {
// Generated by clang-tblgen from HLSLIntrinsics.td (detail/inline intrinsics).
#include "hlsl_inline_intrinsics_gen.inc"
//===----------------------------------------------------------------------===//
// asfloat builtins
//===----------------------------------------------------------------------===//

View File

@ -0,0 +1,469 @@
// RUN: clang-tblgen -gen-hlsl-alias-intrinsics -I%p/../../include %s | FileCheck %s --check-prefix=ALIAS
// RUN: clang-tblgen -gen-hlsl-inline-intrinsics -I%p/../../include %s | FileCheck %s --check-prefix=INLINE
// Tests for the HLSL intrinsic TableGen backend (HLSLEmitter). Each test def
// exercises a specific feature. Def names are numbered (test_NN_) to control
// TableGen's alphabetical emission order so that CHECK lines read top-to-bottom.
include "clang/Basic/HLSLIntrinsics.td"
//===----------------------------------------------------------------------===//
// Basic alias builtin: scalar + vector overloads, no availability.
// Exercises: Varying arg/return, VaryingScalar, VaryingVecSizes, builtin
// alias emission, multiple regular types.
//===----------------------------------------------------------------------===//
def test_01_basic_alias : HLSLBuiltin<"myfunc", "__builtin_myfunc"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2, 3];
let VaryingTypes = [IntTy, FloatTy];
}
// ALIAS-LABEL: // myfunc overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_myfunc)
// ALIAS-NEXT: int myfunc(int);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_myfunc)
// ALIAS-NEXT: int2 myfunc(int2);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_myfunc)
// ALIAS-NEXT: int3 myfunc(int3);
//
// ALIAS-NOT: half myfunc
// ALIAS-NOT: uint myfunc
// ALIAS-NOT: double myfunc
//
// ALIAS: _HLSL_BUILTIN_ALIAS(__builtin_myfunc)
// ALIAS-NEXT: float myfunc(float);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_myfunc)
// ALIAS-NEXT: float2 myfunc(float2);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_myfunc)
// ALIAS-NEXT: float3 myfunc(float3);
//
// ALIAS-NOT: myfunc
//===----------------------------------------------------------------------===//
// VaryingElemType return: return type is scalar even for vector args.
// Exercises: VaryingElemType as ReturnType, two Varying args.
//===----------------------------------------------------------------------===//
def test_02_elem_return : HLSLBuiltin<"dotlike", "__builtin_dotlike"> {
let Args = [Varying, Varying];
let ReturnType = VaryingElemType;
let VaryingScalar = 1;
let VaryingVecSizes = [3];
let VaryingTypes = [FloatTy];
}
// ALIAS-LABEL: // dotlike overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_dotlike)
// ALIAS-NEXT: float dotlike(float, float);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_dotlike)
// ALIAS-NEXT: float dotlike(float3, float3);
//
// ALIAS-NOT: dotlike
//===----------------------------------------------------------------------===//
// VaryingShape return: return shape matches arg shape but with a different
// element type.
// Exercises: VaryingShape<BoolTy> as ReturnType.
//===----------------------------------------------------------------------===//
def test_03_shape_return : HLSLBuiltin<"isspecial", "__builtin_isspecial"> {
let Args = [Varying];
let ReturnType = VaryingShape<BoolTy>;
let VaryingScalar = 1;
let VaryingVecSizes = [2];
let VaryingTypes = [FloatTy];
}
// ALIAS-LABEL: // isspecial overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_isspecial)
// ALIAS-NEXT: bool isspecial(float);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_isspecial)
// ALIAS-NEXT: bool2 isspecial(float2);
//
// ALIAS-NOT: isspecial
//===----------------------------------------------------------------------===//
// Fixed VectorType args and return: no Varying types at all.
// Exercises: VectorType in Args and ReturnType, single overload.
//===----------------------------------------------------------------------===//
def test_04_fixed_vector : HLSLBuiltin<"fixedfunc", "__builtin_fixedfunc"> {
let Args = [VectorType<FloatTy, 3>, VectorType<FloatTy, 3>];
let ReturnType = VectorType<FloatTy, 3>;
}
// ALIAS-LABEL: // fixedfunc overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_fixedfunc)
// ALIAS-NEXT: float3 fixedfunc(float3, float3);
//
// ALIAS-NOT: fixedfunc
//===----------------------------------------------------------------------===//
// Matrix overloads.
// Exercises: VaryingMatDims emission with matrix type names.
//===----------------------------------------------------------------------===//
def test_05_matrix : HLSLBuiltin<"matfunc", "__builtin_matfunc"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingMatDims = [Mat2x3];
let VaryingTypes = [FloatTy];
}
// ALIAS-LABEL: // matfunc overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_matfunc)
// ALIAS-NEXT: float2x3 matfunc(float2x3);
//
// ALIAS-NOT: matfunc
//===----------------------------------------------------------------------===//
// Half type: conditionally-16-bit availability.
// Exercises: _HLSL_16BIT_AVAILABILITY for half, no ifdef guard.
//===----------------------------------------------------------------------===//
def test_06_half : HLSLBuiltin<"halffunc", "__builtin_halffunc"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingTypes = [HalfTy, FloatTy];
}
// ALIAS-LABEL: // halffunc overloads
// ALIAS-NEXT: _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_halffunc)
// ALIAS-NEXT: half halffunc(half);
//
// ALIAS-NOT: int halffunc
// ALIAS-NOT: double halffunc
//
// ALIAS: _HLSL_BUILTIN_ALIAS(__builtin_halffunc)
// ALIAS-NEXT: float halffunc(float);
//
// ALIAS-NOT: halffunc
//===----------------------------------------------------------------------===//
// 16-bit integer type: ifdef guard + availability.
// Exercises: #ifdef __HLSL_ENABLE_16_BIT guard, _HLSL_AVAILABILITY for
// Is16Bit types, guard closing before non-16-bit types.
//===----------------------------------------------------------------------===//
def test_07_16bit : HLSLBuiltin<"i16func", "__builtin_i16func"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingTypes = [Int16Ty, IntTy];
}
// ALIAS-LABEL: // i16func overloads
// ALIAS-NEXT: #ifdef __HLSL_ENABLE_16_BIT
// ALIAS-NEXT: _HLSL_AVAILABILITY(shadermodel, 6.2)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_i16func)
// ALIAS-NEXT: int16_t i16func(int16_t);
// ALIAS-NEXT: #endif
//
// ALIAS-NOT: half i16func
// ALIAS-NOT: float i16func
// ALIAS-NOT: double i16func
//
// ALIAS: _HLSL_BUILTIN_ALIAS(__builtin_i16func)
// ALIAS-NEXT: int i16func(int);
//
// ALIAS-NOT: i16func
//===----------------------------------------------------------------------===//
// Explicit availability (>= SM6.2): half and int16_t both use
// _HLSL_AVAILABILITY with the intrinsic's version instead of the 16-bit
// specific macros, since SM >= 6.2 already implies 16-bit support.
// Exercises: Availability field, SM override for conditionally-16-bit and
// 16-bit integer types.
//===----------------------------------------------------------------------===//
def test_08_avail_ge62 : HLSLBuiltin<"availfunc", "__builtin_availfunc"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingTypes = [HalfTy, Int16Ty, IntTy, FloatTy];
let Availability = SM6_4;
}
// ALIAS-LABEL: // availfunc overloads
// ALIAS-NEXT: _HLSL_AVAILABILITY(shadermodel, 6.4)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_availfunc)
// ALIAS-NEXT: half availfunc(half);
// ALIAS: #ifdef __HLSL_ENABLE_16_BIT
// ALIAS-NEXT: _HLSL_AVAILABILITY(shadermodel, 6.4)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_availfunc)
// ALIAS-NEXT: int16_t availfunc(int16_t);
// ALIAS-NEXT: #endif
// ALIAS: _HLSL_AVAILABILITY(shadermodel, 6.4)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_availfunc)
// ALIAS-NEXT: int availfunc(int);
// ALIAS: _HLSL_AVAILABILITY(shadermodel, 6.4)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_availfunc)
// ALIAS-NEXT: float availfunc(float);
//
// ALIAS-NOT: availfunc
//===----------------------------------------------------------------------===//
// Explicit availability (< SM6.2) with 16-bit integer types: the ifdef guard
// and _HLSL_AVAILABILITY(shadermodel, 6.2) are used for int16_t regardless of
// the intrinsic's stated availability, because 16-bit types require SM6.2.
// Note: half (IsConditionally16Bit) cannot be used here the emitter asserts
// because neither _HLSL_AVAILABILITY nor _HLSL_16BIT_AVAILABILITY can correctly
// express availability for half when the intrinsic's SM < 6.2.
// Exercises: Availability < SM6.2 with Is16Bit types.
//===----------------------------------------------------------------------===//
def test_08b_avail_lt62 : HLSLBuiltin<"availlt62", "__builtin_availlt62"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingTypes = [Int16Ty, IntTy];
let Availability = SM6_1;
}
// ALIAS-LABEL: // availlt62 overloads
// ALIAS-NEXT: #ifdef __HLSL_ENABLE_16_BIT
// ALIAS-NEXT: _HLSL_AVAILABILITY(shadermodel, 6.2)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_availlt62)
// ALIAS-NEXT: int16_t availlt62(int16_t);
// ALIAS-NEXT: #endif
// ALIAS: _HLSL_AVAILABILITY(shadermodel, 6.1)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_availlt62)
// ALIAS-NEXT: int availlt62(int);
//
// ALIAS-NOT: availlt62
//===----------------------------------------------------------------------===//
// Fixed half VectorType arg: conditionally-16-bit flag from fixed arg.
// Exercises: 16-bit flag propagation through fixed VectorType args.
//===----------------------------------------------------------------------===//
def test_09_fixed_half_vec : HLSLBuiltin<"fixedhalffunc", "__builtin_fixedhalffunc"> {
let Args = [VectorType<HalfTy, 2>, VectorType<HalfTy, 2>, FloatTy];
let ReturnType = FloatTy;
}
// ALIAS-LABEL: // fixedhalffunc overloads
// ALIAS-NEXT: _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_fixedhalffunc)
// ALIAS-NEXT: float fixedhalffunc(half2, half2, float);
//
// ALIAS-NOT: fixedhalffunc
//===----------------------------------------------------------------------===//
// No-arg void function with convergent attribute.
// Exercises: VoidTy return, empty Args, IsConvergent.
//===----------------------------------------------------------------------===//
def test_10_void_convergent : HLSLBuiltin<"barrier", "__builtin_barrier"> {
let IsConvergent = 1;
}
// ALIAS-LABEL: // barrier overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_barrier)
// ALIAS-NEXT: __attribute__((convergent)) void barrier();
//
// ALIAS-NOT: barrier
//===----------------------------------------------------------------------===//
// Inline body: constexpr function with literal body.
// Exercises: Body field, IsConstexpr, ParamNames, inline backend only.
//===----------------------------------------------------------------------===//
def test_11_inline_body : HLSLBuiltin<"inline_only_func"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingTypes = [UIntTy];
let Body = "return V;";
let ParamNames = ["V"];
let IsConstexpr = 1;
}
// INLINE-NOT: _HLSL_BUILTIN_ALIAS
// INLINE-LABEL: // inline_only_func overloads
// INLINE-NEXT: constexpr uint inline_only_func(uint V) { return V; }
//
// INLINE-NOT: inline_only_func
//===----------------------------------------------------------------------===//
// Detail function: inline function calling __detail::helper.
// Exercises: DetailFunc, ParamNames, VaryingElemType arg, mixed Varying and
// non-Varying args in inline backend.
//===----------------------------------------------------------------------===//
def test_12_detail_func : HLSLBuiltin<"detailfunc"> {
let DetailFunc = "detail_impl";
let Args = [Varying, Varying, VaryingElemType];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2];
let VaryingTypes = [FloatTy];
let ParamNames = ["A", "B", "C"];
}
// INLINE-LABEL: // detailfunc overloads
// INLINE-NEXT: inline float detailfunc(float A, float B, float C) {
// INLINE-NEXT: return __detail::detail_impl(A, B, C);
// INLINE-NEXT: }
// INLINE-NEXT: inline float2 detailfunc(float2 A, float2 B, float C) {
// INLINE-NEXT: return __detail::detail_impl(A, B, C);
// INLINE-NEXT: }
//
// INLINE-NOT: detailfunc
//===----------------------------------------------------------------------===//
// Doc comment emission.
// Exercises: Doc field producing /// lines with blank-line handling.
//===----------------------------------------------------------------------===//
def test_13_doc : HLSLBuiltin<"docfunc", "__builtin_docfunc"> {
let Doc = [{
\fn void docfunc()
\brief A documented function.
}];
}
// ALIAS: /// \fn void docfunc()
// ALIAS-NEXT: /// \brief A documented function.
// ALIAS-NEXT: // docfunc overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_docfunc)
// ALIAS-NEXT: void docfunc();
//
// ALIAS-NOT: docfunc
//===----------------------------------------------------------------------===//
// Canonical type sort order: types emitted in priority order regardless of
// declaration order.
// Exercises: getTypeSortPriority ordering (half before int before float).
//===----------------------------------------------------------------------===//
def test_14_sort_order : HLSLBuiltin<"sortedfunc", "__builtin_sortedfunc"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingTypes = [FloatTy, IntTy, HalfTy];
}
// ALIAS-LABEL: // sortedfunc overloads
// ALIAS-NEXT: _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_sortedfunc)
// ALIAS-NEXT: half sortedfunc(half);
//
// ALIAS-NOT: double sortedfunc
// ALIAS-NOT: uint sortedfunc
//
// ALIAS: _HLSL_BUILTIN_ALIAS(__builtin_sortedfunc)
// ALIAS-NEXT: int sortedfunc(int);
// ALIAS: _HLSL_BUILTIN_ALIAS(__builtin_sortedfunc)
// ALIAS-NEXT: float sortedfunc(float);
//
// ALIAS-NOT: sortedfunc
//===----------------------------------------------------------------------===//
// Vector and matrix dimension sort order: vector sizes are emitted ascending
// and matrix dimensions sorted by rows then columns, regardless of declaration
// order.
// Exercises: VaryingVecSizes sorting, VaryingMatDims sorting.
//===----------------------------------------------------------------------===//
def test_15_shape_sort : HLSLBuiltin<"shapesorted", "__builtin_shapesorted"> {
let Args = [Varying];
let ReturnType = Varying;
let VaryingVecSizes = [4, 2, 3];
let VaryingMatDims = [Mat3x2, Mat2x1, Mat2x3];
let VaryingTypes = [FloatTy];
}
// ALIAS-LABEL: // shapesorted overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_shapesorted)
// ALIAS-NEXT: float2 shapesorted(float2);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_shapesorted)
// ALIAS-NEXT: float3 shapesorted(float3);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_shapesorted)
// ALIAS-NEXT: float4 shapesorted(float4);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_shapesorted)
// ALIAS-NEXT: float2x1 shapesorted(float2x1);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_shapesorted)
// ALIAS-NEXT: float2x3 shapesorted(float2x3);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_shapesorted)
// ALIAS-NEXT: float3x2 shapesorted(float3x2);
//
// ALIAS-NOT: shapesorted
//===----------------------------------------------------------------------===//
// VaryingElemType as argument: arg is always the scalar element type even when
// the overload shape is a vector.
// Exercises: VaryingElemType in Args position.
//===----------------------------------------------------------------------===//
def test_16_elemtype_arg : HLSLBuiltin<"elemarg", "__builtin_elemarg"> {
let Args = [Varying, VaryingElemType];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [3];
let VaryingTypes = [FloatTy];
}
// ALIAS-LABEL: // elemarg overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_elemarg)
// ALIAS-NEXT: float elemarg(float, float);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_elemarg)
// ALIAS-NEXT: float3 elemarg(float3, float);
//
// ALIAS-NOT: elemarg
//===----------------------------------------------------------------------===//
// VaryingShape as argument: arg uses the varying shape but with a fixed element
// type.
// Exercises: VaryingShape<UIntTy> in Args position.
//===----------------------------------------------------------------------===//
def test_17_shape_arg : HLSLBuiltin<"shapearg", "__builtin_shapearg"> {
let Args = [Varying, VaryingShape<UIntTy>];
let ReturnType = Varying;
let VaryingScalar = 1;
let VaryingVecSizes = [2];
let VaryingTypes = [FloatTy];
}
// ALIAS-LABEL: // shapearg overloads
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_shapearg)
// ALIAS-NEXT: float shapearg(float, uint);
// ALIAS-NEXT: _HLSL_BUILTIN_ALIAS(__builtin_shapearg)
// ALIAS-NEXT: float2 shapearg(float2, uint2);
//
// ALIAS-NOT: shapearg
//===----------------------------------------------------------------------===//
// Verify no alias intrinsics are generated in the inline header.
//===----------------------------------------------------------------------===//
// INLINE-NOT: myfunc
// INLINE-NOT: dotlike
// INLINE-NOT: isspecial
// INLINE-NOT: fixedfunc
// INLINE-NOT: matfunc
// INLINE-NOT: halffunc
// INLINE-NOT: i16func
// INLINE-NOT: availfunc
// INLINE-NOT: availlt62
// INLINE-NOT: fixedhalffunc
// INLINE-NOT: barrier
// INLINE-NOT: docfunc
// INLINE-NOT: sortedfunc
// INLINE-NOT: shapesorted
// INLINE-NOT: elemarg
// INLINE-NOT: shapearg
//===----------------------------------------------------------------------===//
// Verify no inline intrinsics are generated in the alias header.
//===----------------------------------------------------------------------===//
// ALIAS-NOT: inline_only_func
// ALIAS-NOT: detailfunc

View File

@ -21,6 +21,7 @@ add_tablegen(clang-tblgen CLANG
ClangSACheckersEmitter.cpp
ClangSyntaxEmitter.cpp
ClangTypeNodesEmitter.cpp
HLSLEmitter.cpp
MveEmitter.cpp
NeonEmitter.cpp
RISCVVEmitter.cpp

View File

@ -0,0 +1,590 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend generates hlsl_alias_intrinsics_gen.inc (alias
// overloads) and hlsl_inline_intrinsics_gen.inc (inline/detail overloads) for
// HLSL intrinsic functions.
//
//===----------------------------------------------------------------------===//
#include "TableGenBackends.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TableGen/Record.h"
using namespace llvm;
/// Minimum shader model version that supports 16-bit types.
static constexpr StringLiteral SM6_2 = "6.2";
//===----------------------------------------------------------------------===//
// Type name helpers
//===----------------------------------------------------------------------===//
static std::string getVectorTypeName(StringRef ElemType, unsigned N) {
return (ElemType + Twine(N)).str();
}
static std::string getMatrixTypeName(StringRef ElemType, unsigned Rows,
unsigned Cols) {
return (ElemType + Twine(Rows) + "x" + Twine(Cols)).str();
}
/// Get the fixed type name string for a VectorType or HLSLType record.
static std::string getFixedTypeName(const Record *R) {
if (R->isSubClassOf("VectorType"))
return getVectorTypeName(
R->getValueAsDef("ElementType")->getValueAsString("Name"),
R->getValueAsInt("Size"));
assert(R->isSubClassOf("HLSLType"));
return R->getValueAsString("Name").str();
}
/// For a VectorType, return its ElementType record; for an HLSLType, return
/// the record itself (it is already a scalar element type).
static const Record *getElementTypeRecord(const Record *R) {
if (R->isSubClassOf("VectorType"))
return R->getValueAsDef("ElementType");
assert(R->isSubClassOf("HLSLType"));
return R;
}
//===----------------------------------------------------------------------===//
// Type information
//===----------------------------------------------------------------------===//
namespace {
/// Classifies how a type varies across overloads.
enum TypeKindEnum {
TK_Varying = 0, ///< Type matches the full varying type (e.g. float3).
TK_ElemType = 1, ///< Type is the scalar element type (e.g. float).
TK_VaryingShape = 2, ///< Type uses the varying shape with a fixed element.
TK_FixedType = 3, ///< Type is a fixed concrete type (e.g. "half2").
TK_Void = 4 ///< Type is void (only valid for return types).
};
/// Metadata describing how a type (argument or return) varies across overloads.
struct TypeInfo {
/// Classification of how this type varies across overloads.
TypeKindEnum Kind = TK_Varying;
/// Fixed type name (e.g. "half2") for types with a concrete type that does
/// not vary across overloads. Empty for varying types.
std::string FixedType;
/// Element type name for TK_VaryingShape types (e.g. "bool" for
/// VaryingShape<BoolTy>). Empty for other type kinds.
StringRef ShapeElemType;
/// Explicit parameter name (e.g. "eta"). Empty to use the default "p0",
/// "p1", ... naming. Only meaningful for argument types.
StringRef Name;
/// Construct a TypeInfo from a TableGen record.
static TypeInfo resolve(const Record *Rec) {
TypeInfo TI;
if (Rec->getName() == "VoidTy") {
TI.Kind = TK_Void;
} else if (Rec->getName() == "Varying") {
TI.Kind = TK_Varying;
} else if (Rec->getName() == "VaryingElemType") {
TI.Kind = TK_ElemType;
} else if (Rec->isSubClassOf("VaryingShape")) {
TI.Kind = TK_VaryingShape;
TI.ShapeElemType =
Rec->getValueAsDef("ElementType")->getValueAsString("Name");
} else if (Rec->isSubClassOf("VectorType") ||
Rec->isSubClassOf("HLSLType")) {
TI.Kind = TK_FixedType;
TI.FixedType = getFixedTypeName(Rec);
} else {
llvm_unreachable("unhandled record for type resolution");
}
return TI;
}
/// Resolve this type to a concrete type name string.
/// \p ElemType is the scalar element type for the current overload.
/// \p FormatVarying formats a scalar element type into the shaped type name.
std::string
toTypeString(StringRef ElemType,
function_ref<std::string(StringRef)> FormatVarying) const {
switch (Kind) {
case TK_Void:
return "void";
case TK_Varying:
return FormatVarying(ElemType);
case TK_ElemType:
return ElemType.str();
case TK_VaryingShape:
return FormatVarying(ShapeElemType);
case TK_FixedType:
assert(!FixedType.empty() && "TK_FixedType requires non-empty FixedType");
return FixedType;
}
llvm_unreachable("unhandled TypeKindEnum");
}
};
} // anonymous namespace
//===----------------------------------------------------------------------===//
// Availability helpers
//===----------------------------------------------------------------------===//
static void emitAvailability(raw_ostream &OS, StringRef Version,
bool Use16Bit = false) {
if (Use16Bit)
OS << "_HLSL_16BIT_AVAILABILITY(shadermodel, " << Version << ")\n";
else
OS << "_HLSL_AVAILABILITY(shadermodel, " << Version << ")\n";
}
static std::string getVersionString(const Record *SM) {
unsigned Major = SM->getValueAsInt("Major");
unsigned Minor = SM->getValueAsInt("Minor");
if (Major == 0 && Minor == 0)
return "";
return (Twine(Major) + "." + Twine(Minor)).str();
}
//===----------------------------------------------------------------------===//
// Type work item — describes one element type to emit overloads for
//===----------------------------------------------------------------------===//
namespace {
/// A single entry in the worklist of types to process for an intrinsic.
struct TypeWorkItem {
/// Element type name (e.g. "half", "float"). Empty for fixed-arg-only
/// intrinsics with no type expansion.
StringRef ElemType;
/// Version string for the availability attribute (e.g. "6.2"). Empty if
/// no availability annotation is needed.
std::string Availability;
/// If true, emit _HLSL_16BIT_AVAILABILITY instead of _HLSL_AVAILABILITY.
bool Use16BitAvail = false;
/// If true, wrap overloads in #ifdef __HLSL_ENABLE_16_BIT / #endif.
bool NeedsIfdefGuard = false;
};
} // anonymous namespace
/// Fixed canonical ordering for overload types. Types are grouped as:
/// 0: conditionally-16-bit (half)
/// 1-2: 16-bit integers (int16_t, uint16_t) — ifdef-guarded
/// 3+: regular types (bool, int, uint, int64_t, uint64_t, float, double)
/// Within each group, signed precedes unsigned, smaller precedes larger,
/// and integer types precede floating-point types.
static int getTypeSortPriority(const Record *ET) {
return StringSwitch<int>(ET->getValueAsString("Name"))
.Case("half", 0)
.Case("int16_t", 1)
.Case("uint16_t", 2)
.Case("bool", 3)
.Case("int", 4)
.Case("uint", 5)
.Case("int64_t", 7)
.Case("uint64_t", 8)
.Case("float", 9)
.Case("double", 10)
.Default(11);
}
//===----------------------------------------------------------------------===//
// Overload context — shared state across all overloads of one intrinsic
//===----------------------------------------------------------------------===//
namespace {
/// Shared state for emitting all overloads of a single HLSL intrinsic.
struct OverloadContext {
/// Output stream to write generated code to.
raw_ostream &OS;
/// Builtin name for _HLSL_BUILTIN_ALIAS (e.g. "__builtin_hlsl_dot").
/// Empty for inline/detail intrinsics.
StringRef Builtin;
/// __detail helper function to call (e.g. "refract_impl").
/// Empty for alias and inline-body intrinsics.
StringRef DetailFunc;
/// Literal inline function body (e.g. "return p0;").
/// Empty for alias and detail intrinsics.
StringRef Body;
/// The HLSL function name to emit (e.g. "dot", "refract").
StringRef FuncName;
/// Metadata describing the return type and its variation behavior.
TypeInfo RetType;
/// Per-argument metadata describing type and variation behavior.
SmallVector<TypeInfo, 4> Args;
/// Whether to emit the function as constexpr.
bool IsConstexpr = false;
/// Whether to emit the __attribute__((convergent)) annotation.
bool IsConvergent = false;
/// Whether any fixed arg has a 16-bit integer type (e.g. int16_t).
bool Uses16BitType = false;
/// Whether any fixed arg has a conditionally-16-bit type (half).
bool UsesConditionally16BitType = false;
explicit OverloadContext(raw_ostream &OS) : OS(OS) {}
};
} // anonymous namespace
/// Emit a complete function declaration or definition with pre-resolved types.
static void emitDeclaration(const OverloadContext &Ctx, StringRef RetType,
ArrayRef<std::string> ArgTypes) {
raw_ostream &OS = Ctx.OS;
bool IsDetail = !Ctx.DetailFunc.empty();
bool IsInline = !Ctx.Body.empty();
bool HasBody = IsDetail || IsInline;
bool EmitNames = HasBody || llvm::any_of(Ctx.Args, [](const TypeInfo &A) {
return !A.Name.empty();
});
auto GetParamName = [&](unsigned I) -> std::string {
if (!Ctx.Args[I].Name.empty())
return Ctx.Args[I].Name.str();
return ("p" + Twine(I)).str();
};
if (!HasBody)
OS << "_HLSL_BUILTIN_ALIAS(" << Ctx.Builtin << ")\n";
if (Ctx.IsConvergent)
OS << "__attribute__((convergent)) ";
if (HasBody)
OS << (Ctx.IsConstexpr ? "constexpr " : "inline ");
OS << RetType << " " << Ctx.FuncName << "(";
{
ListSeparator LS;
for (unsigned I = 0, N = ArgTypes.size(); I < N; ++I) {
OS << LS << ArgTypes[I];
if (EmitNames)
OS << " " << GetParamName(I);
}
}
if (IsDetail) {
OS << ") {\n return __detail::" << Ctx.DetailFunc << "(";
ListSeparator LS;
for (unsigned I = 0, N = ArgTypes.size(); I < N; ++I)
OS << LS << GetParamName(I);
OS << ");\n}\n";
} else if (IsInline) {
OS << ") { " << Ctx.Body << " }\n";
} else {
OS << ");\n";
}
}
/// Emit a single overload declaration by resolving all types through
/// \p FormatVarying, which maps element types to their shaped form.
static void emitOverload(const OverloadContext &Ctx, StringRef ElemType,
function_ref<std::string(StringRef)> FormatVarying) {
std::string RetType = Ctx.RetType.toTypeString(ElemType, FormatVarying);
SmallVector<std::string> ArgTypes;
for (const TypeInfo &TI : Ctx.Args)
ArgTypes.push_back(TI.toTypeString(ElemType, FormatVarying));
emitDeclaration(Ctx, RetType, ArgTypes);
}
/// Emit a scalar overload for the given element type.
static void emitScalarOverload(const OverloadContext &Ctx, StringRef ElemType) {
emitOverload(Ctx, ElemType, [](StringRef ET) { return ET.str(); });
}
/// Emit a vector overload for the given element type and vector size.
static void emitVectorOverload(const OverloadContext &Ctx, StringRef ElemType,
unsigned VecSize) {
emitOverload(Ctx, ElemType, [VecSize](StringRef ET) {
return getVectorTypeName(ET, VecSize);
});
}
/// Emit a matrix overload for the given element type and matrix dimensions.
static void emitMatrixOverload(const OverloadContext &Ctx, StringRef ElemType,
unsigned Rows, unsigned Cols) {
emitOverload(Ctx, ElemType, [Rows, Cols](StringRef ET) {
return getMatrixTypeName(ET, Rows, Cols);
});
}
//===----------------------------------------------------------------------===//
// Main emission logic
//===----------------------------------------------------------------------===//
/// Build an OverloadContext from an HLSLBuiltin record.
static void buildOverloadContext(const Record *R, OverloadContext &Ctx) {
Ctx.Builtin = R->getValueAsString("Builtin");
Ctx.DetailFunc = R->getValueAsString("DetailFunc");
Ctx.Body = R->getValueAsString("Body");
Ctx.FuncName = R->getValueAsString("Name");
Ctx.IsConstexpr = R->getValueAsBit("IsConstexpr");
Ctx.IsConvergent = R->getValueAsBit("IsConvergent");
// Note use of 16-bit fixed types in the overload context.
auto Update16BitFlags = [&Ctx](const Record *Rec) {
const Record *ElemTy = getElementTypeRecord(Rec);
Ctx.Uses16BitType |= ElemTy->getValueAsBit("Is16Bit");
Ctx.UsesConditionally16BitType |=
ElemTy->getValueAsBit("IsConditionally16Bit");
};
// Resolve return and argument types.
const Record *RetRec = R->getValueAsDef("ReturnType");
Ctx.RetType = TypeInfo::resolve(RetRec);
if (Ctx.RetType.Kind == TK_FixedType)
Update16BitFlags(RetRec);
std::vector<const Record *> ArgRecords = R->getValueAsListOfDefs("Args");
std::vector<StringRef> ParamNames = R->getValueAsListOfStrings("ParamNames");
for (const auto &[I, Arg] : llvm::enumerate(ArgRecords)) {
TypeInfo TI = TypeInfo::resolve(Arg);
if (I < ParamNames.size())
TI.Name = ParamNames[I];
if (TI.Kind == TK_FixedType)
Update16BitFlags(Arg);
Ctx.Args.push_back(TI);
}
}
/// Build the worklist of element types to emit overloads for, sorted in
/// canonical order (see getTypeSortPriority).
static void buildWorklist(const Record *R,
SmallVectorImpl<TypeWorkItem> &Worklist,
const OverloadContext &Ctx) {
const Record *AvailRec = R->getValueAsDef("Availability");
std::string Availability = getVersionString(AvailRec);
bool AvailabilityIsAtLeastSM6_2 = AvailRec->getValueAsInt("Major") > 6 ||
(AvailRec->getValueAsInt("Major") == 6 &&
AvailRec->getValueAsInt("Minor") >= 2);
std::vector<const Record *> VaryingTypeRecords =
R->getValueAsListOfDefs("VaryingTypes");
// Populate the availability and guard fields of a TypeWorkItem based on
// whether the type is 16-bit, conditionally 16-bit, or a regular type.
auto SetAvailability = [&](TypeWorkItem &Item, bool Is16Bit,
bool IsCond16Bit) {
Item.NeedsIfdefGuard = Is16Bit;
if (Is16Bit || IsCond16Bit) {
if (AvailabilityIsAtLeastSM6_2) {
Item.Availability = Availability;
} else {
Item.Availability = SM6_2;
Item.Use16BitAvail = IsCond16Bit;
// Note: If Availability = x where x < 6.2 and a half type is used,
// neither _HLSL_AVAILABILITY(shadermodel, x) nor
// _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) are correct:
//
// _HLSL_AVAILABILITY(shadermodel, x) will set the availbility for the
// half overload to x even when 16-bit types are enabled, but x < 6.2
// and 6.2 is required for 16-bit half.
//
// _HLSL_16BIT_AVAILABILITY(shadermodel, 6.2) will set the
// availability for the half overload to 6.2 when 16-bit types are
// enabled, but there will be no availability set when 16-bit types
// are not enabled.
//
// A possible solution to this is to make _HLSL_16BIT_AVAILABILITY
// accept 3 args: (shadermodel, X, Y) where X is the availability for
// the 16-bit half type overload (which will typically be 6.2), and Y is
// the availability for the non-16-bit half overload. However, this
// situation does not currently arise, so we just assert below that this
// case will never occur.
assert(
!(IsCond16Bit && !Availability.empty()) &&
"Can not handle availability for an intrinsic using half types and"
" which has an explicit shader model requirement older than 6.2");
}
} else {
Item.Availability = Availability;
}
};
// If no Varying types are specified, just add a single work item.
// This is for HLSLBuiltin records that don't use Varying types.
if (VaryingTypeRecords.empty()) {
TypeWorkItem Item;
SetAvailability(Item, Ctx.Uses16BitType, Ctx.UsesConditionally16BitType);
Worklist.push_back(Item);
return;
}
// Sort Varying types so that overloads are always emitted in canonical order.
llvm::sort(VaryingTypeRecords, [](const Record *A, const Record *B) {
return getTypeSortPriority(A) < getTypeSortPriority(B);
});
// Add a work item for each Varying element type.
for (const Record *ElemTy : VaryingTypeRecords) {
TypeWorkItem Item;
Item.ElemType = ElemTy->getValueAsString("Name");
bool Is16Bit = Ctx.Uses16BitType || ElemTy->getValueAsBit("Is16Bit");
bool IsCond16Bit = Ctx.UsesConditionally16BitType ||
ElemTy->getValueAsBit("IsConditionally16Bit");
SetAvailability(Item, Is16Bit, IsCond16Bit);
Worklist.push_back(Item);
}
}
/// Emit a Doxygen documentation comment from the Doc field.
static void emitDocComment(raw_ostream &OS, const Record *R) {
StringRef Doc = R->getValueAsString("Doc");
if (Doc.empty())
return;
Doc = Doc.trim();
SmallVector<StringRef> DocLines;
Doc.split(DocLines, '\n');
for (StringRef Line : DocLines) {
if (Line.empty())
OS << "///\n";
else
OS << "/// " << Line << "\n";
}
}
/// Process the worklist: emit all shape variants for each type with
/// availability annotations and #ifdef guards.
static void emitWorklistOverloads(raw_ostream &OS, const OverloadContext &Ctx,
ArrayRef<TypeWorkItem> Worklist,
bool EmitScalarOverload,
ArrayRef<int64_t> VectorSizes,
ArrayRef<const Record *> MatrixDimensions) {
bool InIfdef = false;
for (const TypeWorkItem &Item : Worklist) {
if (Item.NeedsIfdefGuard && !InIfdef) {
OS << "#ifdef __HLSL_ENABLE_16_BIT\n";
InIfdef = true;
}
auto EmitAvail = [&]() {
if (!Item.Availability.empty())
emitAvailability(OS, Item.Availability, Item.Use16BitAvail);
};
if (EmitScalarOverload) {
EmitAvail();
emitScalarOverload(Ctx, Item.ElemType);
}
for (int64_t N : VectorSizes) {
EmitAvail();
emitVectorOverload(Ctx, Item.ElemType, N);
}
for (const Record *MD : MatrixDimensions) {
EmitAvail();
emitMatrixOverload(Ctx, Item.ElemType, MD->getValueAsInt("Rows"),
MD->getValueAsInt("Cols"));
}
if (InIfdef) {
bool NextIsUnguarded =
(&Item == &Worklist.back()) || !(&Item + 1)->NeedsIfdefGuard;
if (NextIsUnguarded) {
OS << "#endif\n";
InIfdef = false;
}
}
OS << "\n";
}
}
/// Emit all overloads for a single HLSLBuiltin record.
static void emitBuiltinOverloads(raw_ostream &OS, const Record *R) {
OverloadContext Ctx(OS);
buildOverloadContext(R, Ctx);
SmallVector<TypeWorkItem> Worklist;
buildWorklist(R, Worklist, Ctx);
emitDocComment(OS, R);
OS << "// " << Ctx.FuncName << " overloads\n";
// Emit a scalar overload if a scalar Varying overload was requested.
// If no Varying types are used at all, emit a scalar overload to handle
// emitting a single overload for fixed-typed args or arg-less functions.
bool EmitScalarOverload = R->getValueAsBit("VaryingScalar") ||
R->getValueAsListOfDefs("VaryingTypes").empty();
std::vector<int64_t> VectorSizes = R->getValueAsListOfInts("VaryingVecSizes");
std::vector<const Record *> MatrixDimensions =
R->getValueAsListOfDefs("VaryingMatDims");
// Sort vector sizes and matrix dimensions for consistent output order.
llvm::sort(VectorSizes);
llvm::sort(MatrixDimensions, [](const Record *A, const Record *B) {
int RowA = A->getValueAsInt("Rows"), RowB = B->getValueAsInt("Rows");
if (RowA != RowB)
return RowA < RowB;
return A->getValueAsInt("Cols") < B->getValueAsInt("Cols");
});
emitWorklistOverloads(OS, Ctx, Worklist, EmitScalarOverload, VectorSizes,
MatrixDimensions);
}
/// Emit alias overloads for a single HLSLBuiltin record.
/// Skips records that have inline bodies (DetailFunc or Body).
static void emitAliasBuiltin(raw_ostream &OS, const Record *R) {
if (!R->getValueAsString("DetailFunc").empty() ||
!R->getValueAsString("Body").empty())
return;
emitBuiltinOverloads(OS, R);
}
/// Emit inline overloads for a single HLSLBuiltin record.
/// Skips records that are pure alias declarations.
static void emitInlineBuiltin(raw_ostream &OS, const Record *R) {
if (R->getValueAsString("DetailFunc").empty() &&
R->getValueAsString("Body").empty())
return;
emitBuiltinOverloads(OS, R);
}
void clang::EmitHLSLAliasIntrinsics(const RecordKeeper &Records,
raw_ostream &OS) {
OS << "// This file is auto-generated by clang-tblgen from "
"HLSLIntrinsics.td.\n";
OS << "// Do not edit this file directly.\n\n";
for (const Record *R : Records.getAllDerivedDefinitions("HLSLBuiltin"))
emitAliasBuiltin(OS, R);
}
void clang::EmitHLSLInlineIntrinsics(const RecordKeeper &Records,
raw_ostream &OS) {
OS << "// This file is auto-generated by clang-tblgen from "
"HLSLIntrinsics.td.\n";
OS << "// Do not edit this file directly.\n\n";
for (const Record *R : Records.getAllDerivedDefinitions("HLSLBuiltin"))
emitInlineBuiltin(OS, R);
}

View File

@ -118,6 +118,8 @@ enum ActionType {
GenRISCVAndesVectorBuiltins,
GenRISCVAndesVectorBuiltinCG,
GenRISCVAndesVectorBuiltinSema,
GenHLSLAliasIntrinsics,
GenHLSLInlineIntrinsics,
GenAttrDocs,
GenBuiltinDocs,
GenDiagDocs,
@ -346,6 +348,12 @@ cl::opt<ActionType> Action(
clEnumValN(GenRISCVAndesVectorBuiltinSema,
"gen-riscv-andes-vector-builtin-sema",
"Generate riscv_andes_vector_builtin_sema.inc for clang"),
clEnumValN(GenHLSLAliasIntrinsics, "gen-hlsl-alias-intrinsics",
"Generate HLSL alias intrinsic overloads for "
"hlsl_alias_intrinsics.h"),
clEnumValN(GenHLSLInlineIntrinsics, "gen-hlsl-inline-intrinsics",
"Generate HLSL inline intrinsic overloads for "
"hlsl_intrinsics.h"),
clEnumValN(GenAttrDocs, "gen-attr-docs",
"Generate attribute documentation"),
clEnumValN(GenBuiltinDocs, "gen-builtin-docs",
@ -654,6 +662,12 @@ bool ClangTableGenMain(raw_ostream &OS, const RecordKeeper &Records) {
case GenRISCVAndesVectorBuiltinSema:
EmitRVVBuiltinSema(Records, OS);
break;
case GenHLSLAliasIntrinsics:
EmitHLSLAliasIntrinsics(Records, OS);
break;
case GenHLSLInlineIntrinsics:
EmitHLSLInlineIntrinsics(Records, OS);
break;
case GenAttrDocs:
EmitClangAttrDocs(Records, OS);
break;

View File

@ -191,6 +191,11 @@ void EmitCdeBuiltinCG(const llvm::RecordKeeper &Records, llvm::raw_ostream &OS);
void EmitCdeBuiltinAliases(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitHLSLAliasIntrinsics(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitHLSLInlineIntrinsics(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitClangAttrDocs(const llvm::RecordKeeper &Records,
llvm::raw_ostream &OS);
void EmitClangDiagDocs(const llvm::RecordKeeper &Records,