[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:
parent
2ae3d8ae57
commit
daec3b9fb6
320
clang/include/clang/Basic/HLSLIntrinsics.td
Normal file
320
clang/include/clang/Basic/HLSLIntrinsics.td
Normal 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
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
|
||||
namespace hlsl {
|
||||
|
||||
// Generated by clang-tblgen from HLSLIntrinsics.td (detail/inline intrinsics).
|
||||
#include "hlsl_inline_intrinsics_gen.inc"
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// asfloat builtins
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
469
clang/test/TableGen/hlsl-intrinsics.td
Normal file
469
clang/test/TableGen/hlsl-intrinsics.td
Normal 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
|
||||
|
||||
@ -21,6 +21,7 @@ add_tablegen(clang-tblgen CLANG
|
||||
ClangSACheckersEmitter.cpp
|
||||
ClangSyntaxEmitter.cpp
|
||||
ClangTypeNodesEmitter.cpp
|
||||
HLSLEmitter.cpp
|
||||
MveEmitter.cpp
|
||||
NeonEmitter.cpp
|
||||
RISCVVEmitter.cpp
|
||||
|
||||
590
clang/utils/TableGen/HLSLEmitter.cpp
Normal file
590
clang/utils/TableGen/HLSLEmitter.cpp
Normal 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);
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user