Skip to content

[HLSL] [DXIL] Implement the AddUint64 HLSL function and the UAddc DXIL op #127137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4765,6 +4765,12 @@ def GetDeviceSideMangledName : LangBuiltin<"CUDA_LANG"> {
}

// HLSL
def HLSLAddUint64: LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_adduint64"];
let Attributes = [NoThrow, Const];
let Prototype = "void(...)";
}

def HLSLResourceGetPointer : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_resource_getpointer"];
let Attributes = [NoThrow];
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10709,6 +10709,11 @@ def err_vector_incorrect_num_elements : Error<
"%select{too many|too few}0 elements in vector %select{initialization|operand}3 (expected %1 elements, have %2)">;
def err_altivec_empty_initializer : Error<"expected initializer">;

def err_vector_incorrect_bit_count : Error<
"incorrect number of bits in vector operand (expected %select{|a multiple of}0 %1 bits, have %2)">;
def err_integer_incorrect_bit_count : Error<
"incorrect number of bits in integer (expected %0 bits, have %1)">;

def err_invalid_neon_type_code : Error<
"incompatible constant for this __builtin_neon function">;
def err_argument_invalid_range : Error<
Expand Down
56 changes: 56 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19470,6 +19470,62 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
return nullptr;

switch (BuiltinID) {
case Builtin::BI__builtin_hlsl_adduint64: {
Value *OpA = EmitScalarExpr(E->getArg(0));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forwarding: #125319 (comment)

Value *OpB = EmitScalarExpr(E->getArg(1));
QualType Arg0Ty = E->getArg(0)->getType();
uint64_t NumElements = Arg0Ty->castAs<VectorType>()->getNumElements();
assert(Arg0Ty == E->getArg(1)->getType() &&
"AddUint64 operand types must match");
assert(Arg0Ty->hasIntegerRepresentation() &&
"AddUint64 operands must have an integer representation");
assert((NumElements == 2 || NumElements == 4) &&
"AddUint64 operands must have 2 or 4 elements");

llvm::Value *LowA;
llvm::Value *HighA;
llvm::Value *LowB;
llvm::Value *HighB;

// Obtain low and high words of inputs A and B
if (NumElements == 2) {
LowA = Builder.CreateExtractElement(OpA, (uint64_t)0, "LowA");
HighA = Builder.CreateExtractElement(OpA, (uint64_t)1, "HighA");
LowB = Builder.CreateExtractElement(OpB, (uint64_t)0, "LowB");
HighB = Builder.CreateExtractElement(OpB, (uint64_t)1, "HighB");
} else {
LowA = Builder.CreateShuffleVector(OpA, ArrayRef<int>{0, 2}, "LowA");
HighA = Builder.CreateShuffleVector(OpA, ArrayRef<int>{1, 3}, "HighA");
LowB = Builder.CreateShuffleVector(OpB, ArrayRef<int>{0, 2}, "LowB");
HighB = Builder.CreateShuffleVector(OpB, ArrayRef<int>{1, 3}, "HighB");
}

// Use an uadd_with_overflow to compute the sum of low words and obtain a
// carry value
llvm::Value *Carry;
llvm::Value *LowSum = EmitOverflowIntrinsic(
*this, llvm::Intrinsic::uadd_with_overflow, LowA, LowB, Carry);
llvm::Value *ZExtCarry =
Builder.CreateZExt(Carry, HighA->getType(), "CarryZExt");

// Sum the high words and the carry
llvm::Value *HighSum = Builder.CreateAdd(HighA, HighB, "HighSum");
llvm::Value *HighSumPlusCarry =
Builder.CreateAdd(HighSum, ZExtCarry, "HighSumPlusCarry");

if (NumElements == 4) {
return Builder.CreateShuffleVector(LowSum, HighSumPlusCarry,
ArrayRef<int>{0, 2, 1, 3},
"hlsl.AddUint64");
}

llvm::Value *Result = PoisonValue::get(OpA->getType());
Result = Builder.CreateInsertElement(Result, LowSum, (uint64_t)0,
"hlsl.AddUint64.upto0");
Result = Builder.CreateInsertElement(Result, HighSumPlusCarry, (uint64_t)1,
"hlsl.AddUint64");
return Result;
}
case Builtin::BI__builtin_hlsl_resource_getpointer: {
Value *HandleOp = EmitScalarExpr(E->getArg(0));
Value *IndexOp = EmitScalarExpr(E->getArg(1));
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_alias_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,27 @@ float3 acos(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_acos)
float4 acos(float4);

//===----------------------------------------------------------------------===//
// AddUint64 builtins
//===----------------------------------------------------------------------===//

/// \fn T AddUint64(T a, T b)
/// \brief Implements unsigned 64-bit integer addition using pairs of unsigned
/// 32-bit integers.
/// \param x [in] The first unsigned 32-bit integer pair(s)
/// \param y [in] The second unsigned 32-bit integer pair(s)
///
/// This function takes one or two pairs (low, high) of unsigned 32-bit integer
/// values and returns pairs (low, high) of unsigned 32-bit integer
/// values representing the result of unsigned 64-bit integer addition.

_HLSL_AVAILABILITY(shadermodel, 6.0)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_adduint64)
uint32_t2 AddUint64(uint32_t2, uint32_t2);
_HLSL_AVAILABILITY(shadermodel, 6.0)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_adduint64)
uint32_t4 AddUint64(uint32_t4, uint32_t4);

//===----------------------------------------------------------------------===//
// all builtins
//===----------------------------------------------------------------------===//
Expand Down
58 changes: 58 additions & 0 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2086,6 +2086,18 @@ static bool CheckAllArgsHaveFloatRepresentation(Sema *S, CallExpr *TheCall) {
checkAllFloatTypes);
}

static bool CheckUnsignedIntRepresentations(Sema *S, CallExpr *TheCall) {
auto checkUnsignedInteger = [](clang::QualType PassedType) -> bool {
clang::QualType BaseType =
PassedType->isVectorType()
? PassedType->getAs<clang::VectorType>()->getElementType()
: PassedType;
return !BaseType->isUnsignedIntegerType();
};
return CheckAllArgTypesAreCorrect(S, TheCall, S->Context.UnsignedIntTy,
checkUnsignedInteger);
}

static bool CheckFloatOrHalfRepresentations(Sema *S, CallExpr *TheCall) {
auto checkFloatorHalf = [](clang::QualType PassedType) -> bool {
clang::QualType BaseType =
Expand Down Expand Up @@ -2277,6 +2289,52 @@ static bool CheckResourceHandle(
// returning an ExprError
bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
switch (BuiltinID) {
case Builtin::BI__builtin_hlsl_adduint64: {
if (SemaRef.checkArgCount(TheCall, 2))
return true;
if (CheckVectorElementCallArgs(&SemaRef, TheCall))
return true;
if (CheckUnsignedIntRepresentations(&SemaRef, TheCall))
return true;

// CheckVectorElementCallArgs(...) guarantees both args are the same type.
assert(TheCall->getArg(0)->getType() == TheCall->getArg(1)->getType() &&
"Both args must be of the same type");

// ensure both args are vectors
auto *VTy = TheCall->getArg(0)->getType()->getAs<VectorType>();
if (!VTy) {
SemaRef.Diag(TheCall->getBeginLoc(), diag::err_vec_builtin_non_vector)
<< TheCall->getDirectCallee() << /*all*/ 1;
return true;
}

// ensure arg integers are 32-bits
uint64_t ElementBitCount = getASTContext()
.getTypeSizeInChars(VTy->getElementType())
.getQuantity() *
8;
if (ElementBitCount != 32) {
SemaRef.Diag(TheCall->getBeginLoc(),
diag::err_integer_incorrect_bit_count)
<< 32 << ElementBitCount;
return true;
}

// ensure both args are vectors of total bit size of a multiple of 64
int NumElementsArg = VTy->getNumElements();
if (NumElementsArg != 2 && NumElementsArg != 4) {
SemaRef.Diag(TheCall->getBeginLoc(), diag::err_vector_incorrect_bit_count)
<< 1 /*a multiple of*/ << 64 << NumElementsArg * ElementBitCount;
return true;
}

ExprResult A = TheCall->getArg(0);
QualType ArgTyA = A.get()->getType();
// return type is the same as the input type
TheCall->setType(ArgTyA);
break;
}
case Builtin::BI__builtin_hlsl_resource_getpointer: {
if (SemaRef.checkArgCount(TheCall, 2) ||
CheckResourceHandle(&SemaRef, TheCall, 0) ||
Expand Down
58 changes: 58 additions & 0 deletions clang/test/CodeGenHLSL/builtins/AddUint64.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s \
// RUN: -emit-llvm -disable-llvm-passes -o - | \
// RUN: FileCheck %s --check-prefixes=CHECK


// CHECK-LABEL: define noundef <2 x i32> @_Z20test_AddUint64_uint2Dv2_jS_(
// CHECK-SAME: <2 x i32> noundef [[A:%.*]], <2 x i32> noundef [[B:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca <2 x i32>, align 8
// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <2 x i32>, align 8
// CHECK-NEXT: store <2 x i32> [[A]], ptr [[A_ADDR]], align 8
// CHECK-NEXT: store <2 x i32> [[B]], ptr [[B_ADDR]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load <2 x i32>, ptr [[A_ADDR]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = load <2 x i32>, ptr [[B_ADDR]], align 8
// CHECK-NEXT: [[LOWA:%.*]] = extractelement <2 x i32> [[TMP0]], i64 0
// CHECK-NEXT: [[HIGHA:%.*]] = extractelement <2 x i32> [[TMP0]], i64 1
// CHECK-NEXT: [[LOWB:%.*]] = extractelement <2 x i32> [[TMP1]], i64 0
// CHECK-NEXT: [[HIGHB:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1
// CHECK-NEXT: [[TMP2:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[LOWA]], i32 [[LOWB]])
// CHECK-NEXT: [[TMP3:%.*]] = extractvalue { i32, i1 } [[TMP2]], 1
// CHECK-NEXT: [[TMP4:%.*]] = extractvalue { i32, i1 } [[TMP2]], 0
// CHECK-NEXT: [[CARRYZEXT:%.*]] = zext i1 [[TMP3]] to i32
// CHECK-NEXT: [[HIGHSUM:%.*]] = add i32 [[HIGHA]], [[HIGHB]]
// CHECK-NEXT: [[HIGHSUMPLUSCARRY:%.*]] = add i32 [[HIGHSUM]], [[CARRYZEXT]]
// CHECK-NEXT: [[HLSL_ADDUINT64_UPTO0:%.*]] = insertelement <2 x i32> poison, i32 [[TMP4]], i64 0
// CHECK-NEXT: [[HLSL_ADDUINT64:%.*]] = insertelement <2 x i32> [[HLSL_ADDUINT64_UPTO0]], i32 [[HIGHSUMPLUSCARRY]], i64 1
// CHECK-NEXT: ret <2 x i32> [[HLSL_ADDUINT64]]
//
uint2 test_AddUint64_uint2(uint2 a, uint2 b) {
return AddUint64(a, b);
}

// CHECK-LABEL: define noundef <4 x i32> @_Z20test_AddUint64_uint4Dv4_jS_(
// CHECK-SAME: <4 x i32> noundef [[A:%.*]], <4 x i32> noundef [[B:%.*]]) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca <4 x i32>, align 16
// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <4 x i32>, align 16
// CHECK-NEXT: store <4 x i32> [[A]], ptr [[A_ADDR]], align 16
// CHECK-NEXT: store <4 x i32> [[B]], ptr [[B_ADDR]], align 16
// CHECK-NEXT: [[TMP0:%.*]] = load <4 x i32>, ptr [[A_ADDR]], align 16
// CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, ptr [[B_ADDR]], align 16
// CHECK-NEXT: [[LOWA:%.*]] = shufflevector <4 x i32> [[TMP0]], <4 x i32> poison, <2 x i32> <i32 0, i32 2>
// CHECK-NEXT: [[HIGHA:%.*]] = shufflevector <4 x i32> [[TMP0]], <4 x i32> poison, <2 x i32> <i32 1, i32 3>
// CHECK-NEXT: [[LOWB:%.*]] = shufflevector <4 x i32> [[TMP1]], <4 x i32> poison, <2 x i32> <i32 0, i32 2>
// CHECK-NEXT: [[HIGHB:%.*]] = shufflevector <4 x i32> [[TMP1]], <4 x i32> poison, <2 x i32> <i32 1, i32 3>
// CHECK-NEXT: [[TMP2:%.*]] = call { <2 x i32>, <2 x i1> } @llvm.uadd.with.overflow.v2i32(<2 x i32> [[LOWA]], <2 x i32> [[LOWB]])
// CHECK-NEXT: [[TMP3:%.*]] = extractvalue { <2 x i32>, <2 x i1> } [[TMP2]], 1
// CHECK-NEXT: [[TMP4:%.*]] = extractvalue { <2 x i32>, <2 x i1> } [[TMP2]], 0
// CHECK-NEXT: [[CARRYZEXT:%.*]] = zext <2 x i1> [[TMP3]] to <2 x i32>
// CHECK-NEXT: [[HIGHSUM:%.*]] = add <2 x i32> [[HIGHA]], [[HIGHB]]
// CHECK-NEXT: [[HIGHSUMPLUSCARRY:%.*]] = add <2 x i32> [[HIGHSUM]], [[CARRYZEXT]]
// CHECK-NEXT: [[HLSL_ADDUINT64:%.*]] = shufflevector <2 x i32> [[TMP4]], <2 x i32> [[HIGHSUMPLUSCARRY]], <4 x i32> <i32 0, i32 2, i32 1, i32 3>
// CHECK-NEXT: ret <4 x i32> [[HLSL_ADDUINT64]]
//
uint4 test_AddUint64_uint4(uint4 a, uint4 b) {
return AddUint64(a, b);
}
46 changes: 46 additions & 0 deletions clang/test/SemaHLSL/BuiltIns/AddUint64-errors.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm-only -disable-llvm-passes -verify

uint2 test_too_few_arg() {
return __builtin_hlsl_adduint64();
// expected-error@-1 {{too few arguments to function call, expected 2, have 0}}
}

uint4 test_too_many_arg(uint4 a) {
return __builtin_hlsl_adduint64(a, a, a);
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}
}

uint2 test_mismatched_arg_types(uint2 a, uint4 b) {
return __builtin_hlsl_adduint64(a, b);
// expected-error@-1 {{all arguments to '__builtin_hlsl_adduint64' must have the same type}}
}

uint2 test_bad_num_arg_elements(uint3 a, uint3 b) {
return __builtin_hlsl_adduint64(a, b);
// expected-error@-1 {{incorrect number of bits in vector operand (expected a multiple of 64 bits, have 96)}}
}

uint2 test_scalar_arg_type(uint a) {
return __builtin_hlsl_adduint64(a, a);
// expected-error@-1 {{all arguments to '__builtin_hlsl_adduint64' must be vectors}}
}

uint2 test_uint64_args(uint16_t2 a) {
return __builtin_hlsl_adduint64(a, a);
// expected-error@-1 {{incorrect number of bits in integer (expected 32 bits, have 16)}}
}

uint2 test_signed_integer_args(int2 a, int2 b) {
return __builtin_hlsl_adduint64(a, b);
// expected-error@-1 {{passing 'int2' (aka 'vector<int, 2>') to parameter of incompatible type '__attribute__((__vector_size__(2 * sizeof(unsigned int)))) unsigned int' (vector of 2 'unsigned int' values)}}
}

struct S {
uint2 a;
};

uint2 test_incorrect_arg_type(S a) {
return __builtin_hlsl_adduint64(a, a);
// expected-error@-1 {{passing 'S' to parameter of incompatible type 'unsigned int'}}
}

11 changes: 11 additions & 0 deletions llvm/lib/Target/DirectX/DXIL.td
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def HandleTy : DXILOpParamType;
def ResBindTy : DXILOpParamType;
def ResPropsTy : DXILOpParamType;
def SplitDoubleTy : DXILOpParamType;
def BinaryWithCarryTy : DXILOpParamType;

class DXILOpClass;

Expand Down Expand Up @@ -744,6 +745,16 @@ def UMin : DXILOp<40, binary> {
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}

def UAddc : DXILOp<44, binaryWithCarryOrBorrow > {
let Doc = "unsigned add of 32-bit operand with the carry";
let intrinsics = [IntrinSelect<int_uadd_with_overflow>];
Copy link
Member

@farzonl farzonl Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I am a little worried about is if we are misusing int_uadd_with_overflow here. This kind feels like a square is a rectangle but rectangles are not squares cases where llvm is using int_uadd_with_overflow much more broadly than we are. It makes me think If something else (ie a different frontend targeting directX) were to come along and emit int_uadd_with_overflow and it wasn't a uint32_t2 or uint32_t4 case would we be doing the right thing by associating the overflow intrinsic with UAddc?

Copy link
Contributor Author

@Icohedron Icohedron Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the concern. UAddc and int_uadd_with_overflow are equivalent operations on inputs of i32 type. Vectors of i32 must be scalarized to be lowered to UAddc.
Other types accepted by int_uadd_with_overflow (i16 and i64 scalars or vectors) are not currently able to be lowered to DXIL. Attempting to lower to DXIL results in an error like the following: error: <unknown>:0:0: in function uaddci16 i16 (i16, i16): Cannot create UAddc operation: Invalid overload type.

Copy link
Contributor Author

@Icohedron Icohedron Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the AddUint64 HLSL function that only accepts uint32_t2 and uint32_t4 to perform a 64-bit integer add operation. AddUint64 lowers into several instructions, one (or two) of which is the int_uadd_with_overflow intrinsic used to compute an i32 add with carry, which is the same exact operation performed by UAddc.

It should be no issue for some other frontend to emit int_uadd_with_overflow. It will get scalarized and lowered to UAddc for i32 inputs and be the same behavior.

Copy link
Contributor Author

@Icohedron Icohedron Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i16 and i64 could be supported by UAddc as well with minimal effort, but I'm not sure if that would conform to spec. It would, however, still be equivalent to int_uadd_with_overflow behavior.

I think it's the driver compiler that would interpret the DXIL? I don't know if driver compilers would handle UAddc with i16 or i64 inputs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would need to rev the DXIL version to introduce UAddc for other types I think. I think Farzon's concern is that we might want to (at some point) handle int_uadd_with_overflow generically, and since there isn't a DXIL op for it that would presumably involve an expansion. If we use the 32 bit int_uadd_with_overflow to map to UAddc, then whatever pass did that expansion would need to know not to do it for certain overloads and that might be awkward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whatever does the lowering for int_uadd_with_overflow generically would still need to know that the 32-bit int_uadd_with_overflow maps directly to the UAddc DXIL op in order to codegen optimally, as opposed to creating some expansion that does the same thing using more instructions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a nice tablegen solution, but we have the power to check if we have a non 32 bit case in DXILIntrinsicExpansion.cpp. It would be a little bit of a new pattern for us because it would be the first time we check an intrinsic and might not apply a transformation.

Downside is it assumes the DXIL version doesn't update UAddc for other types and if it ever did we would have to introduces some versioning to only do this for pre DXIL 1.6.x.

Upside it lets you keep the direct mapping of int_uadd_with_overflow to UAddc and only modify for the expansion cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All that said I don't think a solution here is pressing atm.

We could alternatively track this as technical debt we need to revisit as part of fully supporting *_with_overflow intrinsics. I wanted to use these for DXIL ops documented here: #128638

That will give us time to develop a more complete solution.

let arguments = [OverloadTy, OverloadTy];
let result = BinaryWithCarryTy;
let overloads = [Overloads<DXIL1_0, [Int32Ty]>];
let stages = [Stages<DXIL1_0, [all_stages]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}

def FMad : DXILOp<46, tertiary> {
let Doc = "Floating point arithmetic multiply/add operation. fmad(m,a,b) = m "
"* a + b.";
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/Target/DirectX/DXILOpBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ static StructType *getSplitDoubleType(LLVMContext &Context) {
return StructType::create({Int32Ty, Int32Ty}, "dx.types.splitdouble");
}

static StructType *getBinaryWithCarryType(LLVMContext &Context) {
if (auto *ST = StructType::getTypeByName(Context, "dx.types.i32c"))
return ST;
Type *Int32Ty = Type::getInt32Ty(Context);
Type *Int1Ty = Type::getInt1Ty(Context);
return StructType::create({Int32Ty, Int1Ty}, "dx.types.i32c");
}

static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,
Type *OverloadTy) {
switch (Kind) {
Expand Down Expand Up @@ -308,6 +316,8 @@ static Type *getTypeFromOpParamType(OpParamType Kind, LLVMContext &Ctx,
return getResPropsType(Ctx);
case OpParamType::SplitDoubleTy:
return getSplitDoubleType(Ctx);
case OpParamType::BinaryWithCarryTy:
return getBinaryWithCarryType(Ctx);
}
llvm_unreachable("Invalid parameter kind");
return nullptr;
Expand Down
Loading
Loading