1//===------- CGHLSLBuiltins.cpp - Emit LLVM Code for HLSL builtins --------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This contains code to emit HLSL Builtin calls as LLVM code.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CGBuiltin.h"
14#include "CGHLSLRuntime.h"
15#include "CodeGenFunction.h"
16
17using namespace clang;
18using namespace CodeGen;
19using namespace llvm;
20
21static Value *handleAsDoubleBuiltin(CodeGenFunction &CGF, const CallExpr *E) {
22 assert((E->getArg(0)->getType()->hasUnsignedIntegerRepresentation() &&
23 E->getArg(1)->getType()->hasUnsignedIntegerRepresentation()) &&
24 "asdouble operands types mismatch");
25 Value *OpLowBits = CGF.EmitScalarExpr(E: E->getArg(Arg: 0));
26 Value *OpHighBits = CGF.EmitScalarExpr(E: E->getArg(Arg: 1));
27
28 llvm::Type *ResultType = CGF.DoubleTy;
29 int N = 1;
30 if (auto *VTy = E->getArg(Arg: 0)->getType()->getAs<clang::VectorType>()) {
31 N = VTy->getNumElements();
32 ResultType = llvm::FixedVectorType::get(ElementType: CGF.DoubleTy, NumElts: N);
33 }
34
35 if (CGF.CGM.getTarget().getTriple().isDXIL())
36 return CGF.Builder.CreateIntrinsic(
37 /*ReturnType=*/RetTy: ResultType, ID: Intrinsic::dx_asdouble,
38 Args: {OpLowBits, OpHighBits}, FMFSource: nullptr, Name: "hlsl.asdouble");
39
40 if (!E->getArg(Arg: 0)->getType()->isVectorType()) {
41 OpLowBits = CGF.Builder.CreateVectorSplat(NumElts: 1, V: OpLowBits);
42 OpHighBits = CGF.Builder.CreateVectorSplat(NumElts: 1, V: OpHighBits);
43 }
44
45 llvm::SmallVector<int> Mask;
46 for (int i = 0; i < N; i++) {
47 Mask.push_back(Elt: i);
48 Mask.push_back(Elt: i + N);
49 }
50
51 Value *BitVec = CGF.Builder.CreateShuffleVector(V1: OpLowBits, V2: OpHighBits, Mask);
52
53 return CGF.Builder.CreateBitCast(V: BitVec, DestTy: ResultType);
54}
55
56static Value *handleHlslClip(const CallExpr *E, CodeGenFunction *CGF) {
57 Value *Op0 = CGF->EmitScalarExpr(E: E->getArg(Arg: 0));
58
59 Constant *FZeroConst = ConstantFP::getZero(Ty: CGF->FloatTy);
60 Value *CMP;
61 Value *LastInstr;
62
63 if (const auto *VecTy = E->getArg(Arg: 0)->getType()->getAs<clang::VectorType>()) {
64 FZeroConst = ConstantVector::getSplat(
65 EC: ElementCount::getFixed(MinVal: VecTy->getNumElements()), Elt: FZeroConst);
66 auto *FCompInst = CGF->Builder.CreateFCmpOLT(LHS: Op0, RHS: FZeroConst);
67 CMP = CGF->Builder.CreateIntrinsic(
68 RetTy: CGF->Builder.getInt1Ty(), ID: CGF->CGM.getHLSLRuntime().getAnyIntrinsic(),
69 Args: {FCompInst});
70 } else {
71 CMP = CGF->Builder.CreateFCmpOLT(LHS: Op0, RHS: FZeroConst);
72 }
73
74 if (CGF->CGM.getTarget().getTriple().isDXIL()) {
75 LastInstr = CGF->Builder.CreateIntrinsic(ID: Intrinsic::dx_discard, Args: {CMP});
76 } else if (CGF->CGM.getTarget().getTriple().isSPIRV()) {
77 BasicBlock *LT0 = CGF->createBasicBlock(name: "lt0", parent: CGF->CurFn);
78 BasicBlock *End = CGF->createBasicBlock(name: "end", parent: CGF->CurFn);
79
80 CGF->Builder.CreateCondBr(Cond: CMP, True: LT0, False: End);
81
82 CGF->Builder.SetInsertPoint(LT0);
83
84 CGF->Builder.CreateIntrinsic(ID: Intrinsic::spv_discard, Args: {});
85
86 LastInstr = CGF->Builder.CreateBr(Dest: End);
87 CGF->Builder.SetInsertPoint(End);
88 } else {
89 llvm_unreachable("Backend Codegen not supported.");
90 }
91
92 return LastInstr;
93}
94
95static Value *handleHlslSplitdouble(const CallExpr *E, CodeGenFunction *CGF) {
96 Value *Op0 = CGF->EmitScalarExpr(E: E->getArg(Arg: 0));
97 const auto *OutArg1 = dyn_cast<HLSLOutArgExpr>(Val: E->getArg(Arg: 1));
98 const auto *OutArg2 = dyn_cast<HLSLOutArgExpr>(Val: E->getArg(Arg: 2));
99
100 CallArgList Args;
101 LValue Op1TmpLValue =
102 CGF->EmitHLSLOutArgExpr(E: OutArg1, Args, Ty: OutArg1->getType());
103 LValue Op2TmpLValue =
104 CGF->EmitHLSLOutArgExpr(E: OutArg2, Args, Ty: OutArg2->getType());
105
106 if (CGF->getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee())
107 Args.reverseWritebacks();
108
109 Value *LowBits = nullptr;
110 Value *HighBits = nullptr;
111
112 if (CGF->CGM.getTarget().getTriple().isDXIL()) {
113 llvm::Type *RetElementTy = CGF->Int32Ty;
114 if (auto *Op0VecTy = E->getArg(Arg: 0)->getType()->getAs<clang::VectorType>())
115 RetElementTy = llvm::VectorType::get(
116 ElementType: CGF->Int32Ty, EC: ElementCount::getFixed(MinVal: Op0VecTy->getNumElements()));
117 auto *RetTy = llvm::StructType::get(elt1: RetElementTy, elts: RetElementTy);
118
119 CallInst *CI = CGF->Builder.CreateIntrinsic(
120 RetTy, ID: Intrinsic::dx_splitdouble, Args: {Op0}, FMFSource: nullptr, Name: "hlsl.splitdouble");
121
122 LowBits = CGF->Builder.CreateExtractValue(Agg: CI, Idxs: 0);
123 HighBits = CGF->Builder.CreateExtractValue(Agg: CI, Idxs: 1);
124 } else {
125 // For Non DXIL targets we generate the instructions.
126
127 if (!Op0->getType()->isVectorTy()) {
128 FixedVectorType *DestTy = FixedVectorType::get(ElementType: CGF->Int32Ty, NumElts: 2);
129 Value *Bitcast = CGF->Builder.CreateBitCast(V: Op0, DestTy);
130
131 LowBits = CGF->Builder.CreateExtractElement(Vec: Bitcast, Idx: (uint64_t)0);
132 HighBits = CGF->Builder.CreateExtractElement(Vec: Bitcast, Idx: 1);
133 } else {
134 int NumElements = 1;
135 if (const auto *VecTy =
136 E->getArg(Arg: 0)->getType()->getAs<clang::VectorType>())
137 NumElements = VecTy->getNumElements();
138
139 FixedVectorType *Uint32VecTy =
140 FixedVectorType::get(ElementType: CGF->Int32Ty, NumElts: NumElements * 2);
141 Value *Uint32Vec = CGF->Builder.CreateBitCast(V: Op0, DestTy: Uint32VecTy);
142 if (NumElements == 1) {
143 LowBits = CGF->Builder.CreateExtractElement(Vec: Uint32Vec, Idx: (uint64_t)0);
144 HighBits = CGF->Builder.CreateExtractElement(Vec: Uint32Vec, Idx: 1);
145 } else {
146 SmallVector<int> EvenMask, OddMask;
147 for (int I = 0, E = NumElements; I != E; ++I) {
148 EvenMask.push_back(Elt: I * 2);
149 OddMask.push_back(Elt: I * 2 + 1);
150 }
151 LowBits = CGF->Builder.CreateShuffleVector(V: Uint32Vec, Mask: EvenMask);
152 HighBits = CGF->Builder.CreateShuffleVector(V: Uint32Vec, Mask: OddMask);
153 }
154 }
155 }
156 CGF->Builder.CreateStore(Val: LowBits, Addr: Op1TmpLValue.getAddress());
157 auto *LastInst =
158 CGF->Builder.CreateStore(Val: HighBits, Addr: Op2TmpLValue.getAddress());
159 CGF->EmitWritebacks(Args);
160 return LastInst;
161}
162
163// Return dot product intrinsic that corresponds to the QT scalar type
164static Intrinsic::ID getDotProductIntrinsic(CGHLSLRuntime &RT, QualType QT) {
165 if (QT->isFloatingType())
166 return RT.getFDotIntrinsic();
167 if (QT->isSignedIntegerType())
168 return RT.getSDotIntrinsic();
169 assert(QT->isUnsignedIntegerType());
170 return RT.getUDotIntrinsic();
171}
172
173static Intrinsic::ID getFirstBitHighIntrinsic(CGHLSLRuntime &RT, QualType QT) {
174 if (QT->hasSignedIntegerRepresentation()) {
175 return RT.getFirstBitSHighIntrinsic();
176 }
177
178 assert(QT->hasUnsignedIntegerRepresentation());
179 return RT.getFirstBitUHighIntrinsic();
180}
181
182// Return wave active sum that corresponds to the QT scalar type
183static Intrinsic::ID getWaveActiveSumIntrinsic(llvm::Triple::ArchType Arch,
184 CGHLSLRuntime &RT, QualType QT) {
185 switch (Arch) {
186 case llvm::Triple::spirv:
187 return Intrinsic::spv_wave_reduce_sum;
188 case llvm::Triple::dxil: {
189 if (QT->isUnsignedIntegerType())
190 return Intrinsic::dx_wave_reduce_usum;
191 return Intrinsic::dx_wave_reduce_sum;
192 }
193 default:
194 llvm_unreachable("Intrinsic WaveActiveSum"
195 " not supported by target architecture");
196 }
197}
198
199// Return wave active sum that corresponds to the QT scalar type
200static Intrinsic::ID getWaveActiveMaxIntrinsic(llvm::Triple::ArchType Arch,
201 CGHLSLRuntime &RT, QualType QT) {
202 switch (Arch) {
203 case llvm::Triple::spirv:
204 if (QT->isUnsignedIntegerType())
205 return Intrinsic::spv_wave_reduce_umax;
206 return Intrinsic::spv_wave_reduce_max;
207 case llvm::Triple::dxil: {
208 if (QT->isUnsignedIntegerType())
209 return Intrinsic::dx_wave_reduce_umax;
210 return Intrinsic::dx_wave_reduce_max;
211 }
212 default:
213 llvm_unreachable("Intrinsic WaveActiveMax"
214 " not supported by target architecture");
215 }
216}
217
218// Returns the mangled name for a builtin function that the SPIR-V backend
219// will expand into a spec Constant.
220static std::string getSpecConstantFunctionName(clang::QualType SpecConstantType,
221 ASTContext &Context) {
222 // The parameter types for our conceptual intrinsic function.
223 QualType ClangParamTypes[] = {Context.IntTy, SpecConstantType};
224
225 // Create a temporary FunctionDecl for the builtin fuction. It won't be
226 // added to the AST.
227 FunctionProtoType::ExtProtoInfo EPI;
228 QualType FnType =
229 Context.getFunctionType(ResultTy: SpecConstantType, Args: ClangParamTypes, EPI);
230 DeclarationName FuncName = &Context.Idents.get(Name: "__spirv_SpecConstant");
231 FunctionDecl *FnDeclForMangling = FunctionDecl::Create(
232 C&: Context, DC: Context.getTranslationUnitDecl(), StartLoc: SourceLocation(),
233 NLoc: SourceLocation(), N: FuncName, T: FnType, /*TSI=*/TInfo: nullptr, SC: SC_Extern);
234
235 // Attach the created parameter declarations to the function declaration.
236 SmallVector<ParmVarDecl *, 2> ParamDecls;
237 for (QualType ParamType : ClangParamTypes) {
238 ParmVarDecl *PD = ParmVarDecl::Create(
239 C&: Context, DC: FnDeclForMangling, StartLoc: SourceLocation(), IdLoc: SourceLocation(),
240 /*IdentifierInfo*/ Id: nullptr, T: ParamType, /*TSI*/ TInfo: nullptr, S: SC_None,
241 /*DefaultArg*/ DefArg: nullptr);
242 ParamDecls.push_back(Elt: PD);
243 }
244 FnDeclForMangling->setParams(ParamDecls);
245
246 // Get the mangled name.
247 std::string Name;
248 llvm::raw_string_ostream MangledNameStream(Name);
249 std::unique_ptr<MangleContext> Mangler(Context.createMangleContext());
250 Mangler->mangleName(GD: FnDeclForMangling, MangledNameStream);
251 MangledNameStream.flush();
252
253 return Name;
254}
255
256Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
257 const CallExpr *E,
258 ReturnValueSlot ReturnValue) {
259 if (!getLangOpts().HLSL)
260 return nullptr;
261
262 switch (BuiltinID) {
263 case Builtin::BI__builtin_hlsl_adduint64: {
264 Value *OpA = EmitScalarExpr(E: E->getArg(Arg: 0));
265 Value *OpB = EmitScalarExpr(E: E->getArg(Arg: 1));
266 QualType Arg0Ty = E->getArg(Arg: 0)->getType();
267 uint64_t NumElements = Arg0Ty->castAs<VectorType>()->getNumElements();
268 assert(Arg0Ty == E->getArg(1)->getType() &&
269 "AddUint64 operand types must match");
270 assert(Arg0Ty->hasIntegerRepresentation() &&
271 "AddUint64 operands must have an integer representation");
272 assert((NumElements == 2 || NumElements == 4) &&
273 "AddUint64 operands must have 2 or 4 elements");
274
275 llvm::Value *LowA;
276 llvm::Value *HighA;
277 llvm::Value *LowB;
278 llvm::Value *HighB;
279
280 // Obtain low and high words of inputs A and B
281 if (NumElements == 2) {
282 LowA = Builder.CreateExtractElement(Vec: OpA, Idx: (uint64_t)0, Name: "LowA");
283 HighA = Builder.CreateExtractElement(Vec: OpA, Idx: (uint64_t)1, Name: "HighA");
284 LowB = Builder.CreateExtractElement(Vec: OpB, Idx: (uint64_t)0, Name: "LowB");
285 HighB = Builder.CreateExtractElement(Vec: OpB, Idx: (uint64_t)1, Name: "HighB");
286 } else {
287 LowA = Builder.CreateShuffleVector(V: OpA, Mask: {0, 2}, Name: "LowA");
288 HighA = Builder.CreateShuffleVector(V: OpA, Mask: {1, 3}, Name: "HighA");
289 LowB = Builder.CreateShuffleVector(V: OpB, Mask: {0, 2}, Name: "LowB");
290 HighB = Builder.CreateShuffleVector(V: OpB, Mask: {1, 3}, Name: "HighB");
291 }
292
293 // Use an uadd_with_overflow to compute the sum of low words and obtain a
294 // carry value
295 llvm::Value *Carry;
296 llvm::Value *LowSum = EmitOverflowIntrinsic(
297 CGF&: *this, IntrinsicID: Intrinsic::uadd_with_overflow, X: LowA, Y: LowB, Carry);
298 llvm::Value *ZExtCarry =
299 Builder.CreateZExt(V: Carry, DestTy: HighA->getType(), Name: "CarryZExt");
300
301 // Sum the high words and the carry
302 llvm::Value *HighSum = Builder.CreateAdd(LHS: HighA, RHS: HighB, Name: "HighSum");
303 llvm::Value *HighSumPlusCarry =
304 Builder.CreateAdd(LHS: HighSum, RHS: ZExtCarry, Name: "HighSumPlusCarry");
305
306 if (NumElements == 4) {
307 return Builder.CreateShuffleVector(V1: LowSum, V2: HighSumPlusCarry, Mask: {0, 2, 1, 3},
308 Name: "hlsl.AddUint64");
309 }
310
311 llvm::Value *Result = PoisonValue::get(T: OpA->getType());
312 Result = Builder.CreateInsertElement(Vec: Result, NewElt: LowSum, Idx: (uint64_t)0,
313 Name: "hlsl.AddUint64.upto0");
314 Result = Builder.CreateInsertElement(Vec: Result, NewElt: HighSumPlusCarry, Idx: (uint64_t)1,
315 Name: "hlsl.AddUint64");
316 return Result;
317 }
318 case Builtin::BI__builtin_hlsl_resource_getpointer: {
319 Value *HandleOp = EmitScalarExpr(E: E->getArg(Arg: 0));
320 Value *IndexOp = EmitScalarExpr(E: E->getArg(Arg: 1));
321
322 llvm::Type *RetTy = ConvertType(T: E->getType());
323 return Builder.CreateIntrinsic(
324 RetTy, ID: CGM.getHLSLRuntime().getCreateResourceGetPointerIntrinsic(),
325 Args: ArrayRef<Value *>{HandleOp, IndexOp});
326 }
327 case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: {
328 llvm::Type *HandleTy = CGM.getTypes().ConvertType(T: E->getType());
329 return llvm::PoisonValue::get(T: HandleTy);
330 }
331 case Builtin::BI__builtin_hlsl_resource_handlefrombinding: {
332 llvm::Type *HandleTy = CGM.getTypes().ConvertType(T: E->getType());
333 Value *RegisterOp = EmitScalarExpr(E: E->getArg(Arg: 1));
334 Value *SpaceOp = EmitScalarExpr(E: E->getArg(Arg: 2));
335 Value *RangeOp = EmitScalarExpr(E: E->getArg(Arg: 3));
336 Value *IndexOp = EmitScalarExpr(E: E->getArg(Arg: 4));
337 Value *Name = EmitScalarExpr(E: E->getArg(Arg: 5));
338 // FIXME: NonUniformResourceIndex bit is not yet implemented
339 // (llvm/llvm-project#135452)
340 Value *NonUniform =
341 llvm::ConstantInt::get(Ty: llvm::Type::getInt1Ty(C&: getLLVMContext()), V: false);
342
343 llvm::Intrinsic::ID IntrinsicID =
344 CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic();
345 SmallVector<Value *> Args{SpaceOp, RegisterOp, RangeOp,
346 IndexOp, NonUniform, Name};
347 return Builder.CreateIntrinsic(RetTy: HandleTy, ID: IntrinsicID, Args);
348 }
349 case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: {
350 llvm::Type *HandleTy = CGM.getTypes().ConvertType(T: E->getType());
351 Value *SpaceOp = EmitScalarExpr(E: E->getArg(Arg: 1));
352 Value *RangeOp = EmitScalarExpr(E: E->getArg(Arg: 2));
353 Value *IndexOp = EmitScalarExpr(E: E->getArg(Arg: 3));
354 Value *OrderID = EmitScalarExpr(E: E->getArg(Arg: 4));
355 Value *Name = EmitScalarExpr(E: E->getArg(Arg: 5));
356 // FIXME: NonUniformResourceIndex bit is not yet implemented
357 // (llvm/llvm-project#135452)
358 Value *NonUniform =
359 llvm::ConstantInt::get(Ty: llvm::Type::getInt1Ty(C&: getLLVMContext()), V: false);
360
361 llvm::Intrinsic::ID IntrinsicID =
362 CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();
363 SmallVector<Value *> Args{OrderID, SpaceOp, RangeOp,
364 IndexOp, NonUniform, Name};
365 return Builder.CreateIntrinsic(RetTy: HandleTy, ID: IntrinsicID, Args);
366 }
367 case Builtin::BI__builtin_hlsl_all: {
368 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
369 return Builder.CreateIntrinsic(
370 /*ReturnType=*/RetTy: llvm::Type::getInt1Ty(C&: getLLVMContext()),
371 ID: CGM.getHLSLRuntime().getAllIntrinsic(), Args: ArrayRef<Value *>{Op0}, FMFSource: nullptr,
372 Name: "hlsl.all");
373 }
374 case Builtin::BI__builtin_hlsl_and: {
375 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
376 Value *Op1 = EmitScalarExpr(E: E->getArg(Arg: 1));
377 return Builder.CreateAnd(LHS: Op0, RHS: Op1, Name: "hlsl.and");
378 }
379 case Builtin::BI__builtin_hlsl_or: {
380 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
381 Value *Op1 = EmitScalarExpr(E: E->getArg(Arg: 1));
382 return Builder.CreateOr(LHS: Op0, RHS: Op1, Name: "hlsl.or");
383 }
384 case Builtin::BI__builtin_hlsl_any: {
385 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
386 return Builder.CreateIntrinsic(
387 /*ReturnType=*/RetTy: llvm::Type::getInt1Ty(C&: getLLVMContext()),
388 ID: CGM.getHLSLRuntime().getAnyIntrinsic(), Args: ArrayRef<Value *>{Op0}, FMFSource: nullptr,
389 Name: "hlsl.any");
390 }
391 case Builtin::BI__builtin_hlsl_asdouble:
392 return handleAsDoubleBuiltin(CGF&: *this, E);
393 case Builtin::BI__builtin_hlsl_elementwise_clamp: {
394 Value *OpX = EmitScalarExpr(E: E->getArg(Arg: 0));
395 Value *OpMin = EmitScalarExpr(E: E->getArg(Arg: 1));
396 Value *OpMax = EmitScalarExpr(E: E->getArg(Arg: 2));
397
398 QualType Ty = E->getArg(Arg: 0)->getType();
399 if (auto *VecTy = Ty->getAs<VectorType>())
400 Ty = VecTy->getElementType();
401
402 Intrinsic::ID Intr;
403 if (Ty->isFloatingType()) {
404 Intr = CGM.getHLSLRuntime().getNClampIntrinsic();
405 } else if (Ty->isUnsignedIntegerType()) {
406 Intr = CGM.getHLSLRuntime().getUClampIntrinsic();
407 } else {
408 assert(Ty->isSignedIntegerType());
409 Intr = CGM.getHLSLRuntime().getSClampIntrinsic();
410 }
411 return Builder.CreateIntrinsic(
412 /*ReturnType=*/RetTy: OpX->getType(), ID: Intr,
413 Args: ArrayRef<Value *>{OpX, OpMin, OpMax}, FMFSource: nullptr, Name: "hlsl.clamp");
414 }
415 case Builtin::BI__builtin_hlsl_crossf16:
416 case Builtin::BI__builtin_hlsl_crossf32: {
417 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
418 Value *Op1 = EmitScalarExpr(E: E->getArg(Arg: 1));
419 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
420 E->getArg(1)->getType()->hasFloatingRepresentation() &&
421 "cross operands must have a float representation");
422 // make sure each vector has exactly 3 elements
423 assert(
424 E->getArg(0)->getType()->castAs<VectorType>()->getNumElements() == 3 &&
425 E->getArg(1)->getType()->castAs<VectorType>()->getNumElements() == 3 &&
426 "input vectors must have 3 elements each");
427 return Builder.CreateIntrinsic(
428 /*ReturnType=*/RetTy: Op0->getType(), ID: CGM.getHLSLRuntime().getCrossIntrinsic(),
429 Args: ArrayRef<Value *>{Op0, Op1}, FMFSource: nullptr, Name: "hlsl.cross");
430 }
431 case Builtin::BI__builtin_hlsl_dot: {
432 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
433 Value *Op1 = EmitScalarExpr(E: E->getArg(Arg: 1));
434 llvm::Type *T0 = Op0->getType();
435 llvm::Type *T1 = Op1->getType();
436
437 // If the arguments are scalars, just emit a multiply
438 if (!T0->isVectorTy() && !T1->isVectorTy()) {
439 if (T0->isFloatingPointTy())
440 return Builder.CreateFMul(L: Op0, R: Op1, Name: "hlsl.dot");
441
442 if (T0->isIntegerTy())
443 return Builder.CreateMul(LHS: Op0, RHS: Op1, Name: "hlsl.dot");
444
445 llvm_unreachable(
446 "Scalar dot product is only supported on ints and floats.");
447 }
448 // For vectors, validate types and emit the appropriate intrinsic
449 assert(CGM.getContext().hasSameUnqualifiedType(E->getArg(0)->getType(),
450 E->getArg(1)->getType()) &&
451 "Dot product operands must have the same type.");
452
453 auto *VecTy0 = E->getArg(Arg: 0)->getType()->castAs<VectorType>();
454 assert(VecTy0 && "Dot product argument must be a vector.");
455
456 return Builder.CreateIntrinsic(
457 /*ReturnType=*/RetTy: T0->getScalarType(),
458 ID: getDotProductIntrinsic(RT&: CGM.getHLSLRuntime(), QT: VecTy0->getElementType()),
459 Args: ArrayRef<Value *>{Op0, Op1}, FMFSource: nullptr, Name: "hlsl.dot");
460 }
461 case Builtin::BI__builtin_hlsl_dot4add_i8packed: {
462 Value *X = EmitScalarExpr(E: E->getArg(Arg: 0));
463 Value *Y = EmitScalarExpr(E: E->getArg(Arg: 1));
464 Value *Acc = EmitScalarExpr(E: E->getArg(Arg: 2));
465
466 Intrinsic::ID ID = CGM.getHLSLRuntime().getDot4AddI8PackedIntrinsic();
467 // Note that the argument order disagrees between the builtin and the
468 // intrinsic here.
469 return Builder.CreateIntrinsic(
470 /*ReturnType=*/RetTy: Acc->getType(), ID, Args: ArrayRef<Value *>{Acc, X, Y},
471 FMFSource: nullptr, Name: "hlsl.dot4add.i8packed");
472 }
473 case Builtin::BI__builtin_hlsl_dot4add_u8packed: {
474 Value *X = EmitScalarExpr(E: E->getArg(Arg: 0));
475 Value *Y = EmitScalarExpr(E: E->getArg(Arg: 1));
476 Value *Acc = EmitScalarExpr(E: E->getArg(Arg: 2));
477
478 Intrinsic::ID ID = CGM.getHLSLRuntime().getDot4AddU8PackedIntrinsic();
479 // Note that the argument order disagrees between the builtin and the
480 // intrinsic here.
481 return Builder.CreateIntrinsic(
482 /*ReturnType=*/RetTy: Acc->getType(), ID, Args: ArrayRef<Value *>{Acc, X, Y},
483 FMFSource: nullptr, Name: "hlsl.dot4add.u8packed");
484 }
485 case Builtin::BI__builtin_hlsl_elementwise_firstbithigh: {
486 Value *X = EmitScalarExpr(E: E->getArg(Arg: 0));
487
488 return Builder.CreateIntrinsic(
489 /*ReturnType=*/RetTy: ConvertType(T: E->getType()),
490 ID: getFirstBitHighIntrinsic(RT&: CGM.getHLSLRuntime(), QT: E->getArg(Arg: 0)->getType()),
491 Args: ArrayRef<Value *>{X}, FMFSource: nullptr, Name: "hlsl.firstbithigh");
492 }
493 case Builtin::BI__builtin_hlsl_elementwise_firstbitlow: {
494 Value *X = EmitScalarExpr(E: E->getArg(Arg: 0));
495
496 return Builder.CreateIntrinsic(
497 /*ReturnType=*/RetTy: ConvertType(T: E->getType()),
498 ID: CGM.getHLSLRuntime().getFirstBitLowIntrinsic(), Args: ArrayRef<Value *>{X},
499 FMFSource: nullptr, Name: "hlsl.firstbitlow");
500 }
501 case Builtin::BI__builtin_hlsl_lerp: {
502 Value *X = EmitScalarExpr(E: E->getArg(Arg: 0));
503 Value *Y = EmitScalarExpr(E: E->getArg(Arg: 1));
504 Value *S = EmitScalarExpr(E: E->getArg(Arg: 2));
505 if (!E->getArg(Arg: 0)->getType()->hasFloatingRepresentation())
506 llvm_unreachable("lerp operand must have a float representation");
507 return Builder.CreateIntrinsic(
508 /*ReturnType=*/RetTy: X->getType(), ID: CGM.getHLSLRuntime().getLerpIntrinsic(),
509 Args: ArrayRef<Value *>{X, Y, S}, FMFSource: nullptr, Name: "hlsl.lerp");
510 }
511 case Builtin::BI__builtin_hlsl_normalize: {
512 Value *X = EmitScalarExpr(E: E->getArg(Arg: 0));
513
514 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
515 "normalize operand must have a float representation");
516
517 return Builder.CreateIntrinsic(
518 /*ReturnType=*/RetTy: X->getType(),
519 ID: CGM.getHLSLRuntime().getNormalizeIntrinsic(), Args: ArrayRef<Value *>{X},
520 FMFSource: nullptr, Name: "hlsl.normalize");
521 }
522 case Builtin::BI__builtin_hlsl_elementwise_degrees: {
523 Value *X = EmitScalarExpr(E: E->getArg(Arg: 0));
524
525 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
526 "degree operand must have a float representation");
527
528 return Builder.CreateIntrinsic(
529 /*ReturnType=*/RetTy: X->getType(), ID: CGM.getHLSLRuntime().getDegreesIntrinsic(),
530 Args: ArrayRef<Value *>{X}, FMFSource: nullptr, Name: "hlsl.degrees");
531 }
532 case Builtin::BI__builtin_hlsl_elementwise_frac: {
533 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
534 if (!E->getArg(Arg: 0)->getType()->hasFloatingRepresentation())
535 llvm_unreachable("frac operand must have a float representation");
536 return Builder.CreateIntrinsic(
537 /*ReturnType=*/RetTy: Op0->getType(), ID: CGM.getHLSLRuntime().getFracIntrinsic(),
538 Args: ArrayRef<Value *>{Op0}, FMFSource: nullptr, Name: "hlsl.frac");
539 }
540 case Builtin::BI__builtin_hlsl_elementwise_isinf: {
541 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
542 llvm::Type *Xty = Op0->getType();
543 llvm::Type *retType = llvm::Type::getInt1Ty(C&: this->getLLVMContext());
544 if (Xty->isVectorTy()) {
545 auto *XVecTy = E->getArg(Arg: 0)->getType()->castAs<VectorType>();
546 retType = llvm::VectorType::get(
547 ElementType: retType, EC: ElementCount::getFixed(MinVal: XVecTy->getNumElements()));
548 }
549 if (!E->getArg(Arg: 0)->getType()->hasFloatingRepresentation())
550 llvm_unreachable("isinf operand must have a float representation");
551 return Builder.CreateIntrinsic(RetTy: retType, ID: Intrinsic::dx_isinf,
552 Args: ArrayRef<Value *>{Op0}, FMFSource: nullptr, Name: "dx.isinf");
553 }
554 case Builtin::BI__builtin_hlsl_mad: {
555 Value *M = EmitScalarExpr(E: E->getArg(Arg: 0));
556 Value *A = EmitScalarExpr(E: E->getArg(Arg: 1));
557 Value *B = EmitScalarExpr(E: E->getArg(Arg: 2));
558 if (E->getArg(Arg: 0)->getType()->hasFloatingRepresentation())
559 return Builder.CreateIntrinsic(
560 /*ReturnType*/ RetTy: M->getType(), ID: Intrinsic::fmuladd,
561 Args: ArrayRef<Value *>{M, A, B}, FMFSource: nullptr, Name: "hlsl.fmad");
562
563 if (E->getArg(Arg: 0)->getType()->hasSignedIntegerRepresentation()) {
564 if (CGM.getTarget().getTriple().getArch() == llvm::Triple::dxil)
565 return Builder.CreateIntrinsic(
566 /*ReturnType*/ RetTy: M->getType(), ID: Intrinsic::dx_imad,
567 Args: ArrayRef<Value *>{M, A, B}, FMFSource: nullptr, Name: "dx.imad");
568
569 Value *Mul = Builder.CreateNSWMul(LHS: M, RHS: A);
570 return Builder.CreateNSWAdd(LHS: Mul, RHS: B);
571 }
572 assert(E->getArg(0)->getType()->hasUnsignedIntegerRepresentation());
573 if (CGM.getTarget().getTriple().getArch() == llvm::Triple::dxil)
574 return Builder.CreateIntrinsic(
575 /*ReturnType=*/RetTy: M->getType(), ID: Intrinsic::dx_umad,
576 Args: ArrayRef<Value *>{M, A, B}, FMFSource: nullptr, Name: "dx.umad");
577
578 Value *Mul = Builder.CreateNUWMul(LHS: M, RHS: A);
579 return Builder.CreateNUWAdd(LHS: Mul, RHS: B);
580 }
581 case Builtin::BI__builtin_hlsl_elementwise_rcp: {
582 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
583 if (!E->getArg(Arg: 0)->getType()->hasFloatingRepresentation())
584 llvm_unreachable("rcp operand must have a float representation");
585 llvm::Type *Ty = Op0->getType();
586 llvm::Type *EltTy = Ty->getScalarType();
587 Constant *One = Ty->isVectorTy()
588 ? ConstantVector::getSplat(
589 EC: ElementCount::getFixed(
590 MinVal: cast<FixedVectorType>(Val: Ty)->getNumElements()),
591 Elt: ConstantFP::get(Ty: EltTy, V: 1.0))
592 : ConstantFP::get(Ty: EltTy, V: 1.0);
593 return Builder.CreateFDiv(L: One, R: Op0, Name: "hlsl.rcp");
594 }
595 case Builtin::BI__builtin_hlsl_elementwise_rsqrt: {
596 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
597 if (!E->getArg(Arg: 0)->getType()->hasFloatingRepresentation())
598 llvm_unreachable("rsqrt operand must have a float representation");
599 return Builder.CreateIntrinsic(
600 /*ReturnType=*/RetTy: Op0->getType(), ID: CGM.getHLSLRuntime().getRsqrtIntrinsic(),
601 Args: ArrayRef<Value *>{Op0}, FMFSource: nullptr, Name: "hlsl.rsqrt");
602 }
603 case Builtin::BI__builtin_hlsl_elementwise_saturate: {
604 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
605 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
606 "saturate operand must have a float representation");
607 return Builder.CreateIntrinsic(
608 /*ReturnType=*/RetTy: Op0->getType(),
609 ID: CGM.getHLSLRuntime().getSaturateIntrinsic(), Args: ArrayRef<Value *>{Op0},
610 FMFSource: nullptr, Name: "hlsl.saturate");
611 }
612 case Builtin::BI__builtin_hlsl_select: {
613 Value *OpCond = EmitScalarExpr(E: E->getArg(Arg: 0));
614 RValue RValTrue = EmitAnyExpr(E: E->getArg(Arg: 1));
615 Value *OpTrue =
616 RValTrue.isScalar()
617 ? RValTrue.getScalarVal()
618 : RValTrue.getAggregatePointer(PointeeType: E->getArg(Arg: 1)->getType(), CGF&: *this);
619 RValue RValFalse = EmitAnyExpr(E: E->getArg(Arg: 2));
620 Value *OpFalse =
621 RValFalse.isScalar()
622 ? RValFalse.getScalarVal()
623 : RValFalse.getAggregatePointer(PointeeType: E->getArg(Arg: 2)->getType(), CGF&: *this);
624 if (auto *VTy = E->getType()->getAs<VectorType>()) {
625 if (!OpTrue->getType()->isVectorTy())
626 OpTrue =
627 Builder.CreateVectorSplat(NumElts: VTy->getNumElements(), V: OpTrue, Name: "splat");
628 if (!OpFalse->getType()->isVectorTy())
629 OpFalse =
630 Builder.CreateVectorSplat(NumElts: VTy->getNumElements(), V: OpFalse, Name: "splat");
631 }
632
633 Value *SelectVal =
634 Builder.CreateSelect(C: OpCond, True: OpTrue, False: OpFalse, Name: "hlsl.select");
635 if (!RValTrue.isScalar())
636 Builder.CreateStore(Val: SelectVal, Addr: ReturnValue.getAddress(),
637 IsVolatile: ReturnValue.isVolatile());
638
639 return SelectVal;
640 }
641 case Builtin::BI__builtin_hlsl_step: {
642 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
643 Value *Op1 = EmitScalarExpr(E: E->getArg(Arg: 1));
644 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
645 E->getArg(1)->getType()->hasFloatingRepresentation() &&
646 "step operands must have a float representation");
647 return Builder.CreateIntrinsic(
648 /*ReturnType=*/RetTy: Op0->getType(), ID: CGM.getHLSLRuntime().getStepIntrinsic(),
649 Args: ArrayRef<Value *>{Op0, Op1}, FMFSource: nullptr, Name: "hlsl.step");
650 }
651 case Builtin::BI__builtin_hlsl_wave_active_all_true: {
652 Value *Op = EmitScalarExpr(E: E->getArg(Arg: 0));
653 assert(Op->getType()->isIntegerTy(1) &&
654 "Intrinsic WaveActiveAllTrue operand must be a bool");
655
656 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveActiveAllTrueIntrinsic();
657 return EmitRuntimeCall(
658 callee: Intrinsic::getOrInsertDeclaration(M: &CGM.getModule(), id: ID), args: {Op});
659 }
660 case Builtin::BI__builtin_hlsl_wave_active_any_true: {
661 Value *Op = EmitScalarExpr(E: E->getArg(Arg: 0));
662 assert(Op->getType()->isIntegerTy(1) &&
663 "Intrinsic WaveActiveAnyTrue operand must be a bool");
664
665 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveActiveAnyTrueIntrinsic();
666 return EmitRuntimeCall(
667 callee: Intrinsic::getOrInsertDeclaration(M: &CGM.getModule(), id: ID), args: {Op});
668 }
669 case Builtin::BI__builtin_hlsl_wave_active_count_bits: {
670 Value *OpExpr = EmitScalarExpr(E: E->getArg(Arg: 0));
671 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveActiveCountBitsIntrinsic();
672 return EmitRuntimeCall(
673 callee: Intrinsic::getOrInsertDeclaration(M: &CGM.getModule(), id: ID),
674 args: ArrayRef{OpExpr});
675 }
676 case Builtin::BI__builtin_hlsl_wave_active_sum: {
677 // Due to the use of variadic arguments, explicitly retreive argument
678 Value *OpExpr = EmitScalarExpr(E: E->getArg(Arg: 0));
679 Intrinsic::ID IID = getWaveActiveSumIntrinsic(
680 Arch: getTarget().getTriple().getArch(), RT&: CGM.getHLSLRuntime(),
681 QT: E->getArg(Arg: 0)->getType());
682
683 return EmitRuntimeCall(callee: Intrinsic::getOrInsertDeclaration(
684 M: &CGM.getModule(), id: IID, Tys: {OpExpr->getType()}),
685 args: ArrayRef{OpExpr}, name: "hlsl.wave.active.sum");
686 }
687 case Builtin::BI__builtin_hlsl_wave_active_max: {
688 // Due to the use of variadic arguments, explicitly retreive argument
689 Value *OpExpr = EmitScalarExpr(E: E->getArg(Arg: 0));
690 Intrinsic::ID IID = getWaveActiveMaxIntrinsic(
691 Arch: getTarget().getTriple().getArch(), RT&: CGM.getHLSLRuntime(),
692 QT: E->getArg(Arg: 0)->getType());
693
694 return EmitRuntimeCall(callee: Intrinsic::getOrInsertDeclaration(
695 M: &CGM.getModule(), id: IID, Tys: {OpExpr->getType()}),
696 args: ArrayRef{OpExpr}, name: "hlsl.wave.active.max");
697 }
698 case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
699 // We don't define a SPIR-V intrinsic, instead it is a SPIR-V built-in
700 // defined in SPIRVBuiltins.td. So instead we manually get the matching name
701 // for the DirectX intrinsic and the demangled builtin name
702 switch (CGM.getTarget().getTriple().getArch()) {
703 case llvm::Triple::dxil:
704 return EmitRuntimeCall(callee: Intrinsic::getOrInsertDeclaration(
705 M: &CGM.getModule(), id: Intrinsic::dx_wave_getlaneindex));
706 case llvm::Triple::spirv:
707 return EmitRuntimeCall(callee: CGM.CreateRuntimeFunction(
708 Ty: llvm::FunctionType::get(Result: IntTy, Params: {}, isVarArg: false),
709 Name: "__hlsl_wave_get_lane_index", ExtraAttrs: {}, Local: false, AssumeConvergent: true));
710 default:
711 llvm_unreachable(
712 "Intrinsic WaveGetLaneIndex not supported by target architecture");
713 }
714 }
715 case Builtin::BI__builtin_hlsl_wave_is_first_lane: {
716 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveIsFirstLaneIntrinsic();
717 return EmitRuntimeCall(
718 callee: Intrinsic::getOrInsertDeclaration(M: &CGM.getModule(), id: ID));
719 }
720 case Builtin::BI__builtin_hlsl_wave_get_lane_count: {
721 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveGetLaneCountIntrinsic();
722 return EmitRuntimeCall(
723 callee: Intrinsic::getOrInsertDeclaration(M: &CGM.getModule(), id: ID));
724 }
725 case Builtin::BI__builtin_hlsl_wave_read_lane_at: {
726 // Due to the use of variadic arguments we must explicitly retreive them and
727 // create our function type.
728 Value *OpExpr = EmitScalarExpr(E: E->getArg(Arg: 0));
729 Value *OpIndex = EmitScalarExpr(E: E->getArg(Arg: 1));
730 return EmitRuntimeCall(
731 callee: Intrinsic::getOrInsertDeclaration(
732 M: &CGM.getModule(), id: CGM.getHLSLRuntime().getWaveReadLaneAtIntrinsic(),
733 Tys: {OpExpr->getType()}),
734 args: ArrayRef{OpExpr, OpIndex}, name: "hlsl.wave.readlane");
735 }
736 case Builtin::BI__builtin_hlsl_elementwise_sign: {
737 auto *Arg0 = E->getArg(Arg: 0);
738 Value *Op0 = EmitScalarExpr(E: Arg0);
739 llvm::Type *Xty = Op0->getType();
740 llvm::Type *retType = llvm::Type::getInt32Ty(C&: this->getLLVMContext());
741 if (Xty->isVectorTy()) {
742 auto *XVecTy = Arg0->getType()->castAs<VectorType>();
743 retType = llvm::VectorType::get(
744 ElementType: retType, EC: ElementCount::getFixed(MinVal: XVecTy->getNumElements()));
745 }
746 assert((Arg0->getType()->hasFloatingRepresentation() ||
747 Arg0->getType()->hasIntegerRepresentation()) &&
748 "sign operand must have a float or int representation");
749
750 if (Arg0->getType()->hasUnsignedIntegerRepresentation()) {
751 Value *Cmp = Builder.CreateICmpEQ(LHS: Op0, RHS: ConstantInt::get(Ty: Xty, V: 0));
752 return Builder.CreateSelect(C: Cmp, True: ConstantInt::get(Ty: retType, V: 0),
753 False: ConstantInt::get(Ty: retType, V: 1), Name: "hlsl.sign");
754 }
755
756 return Builder.CreateIntrinsic(
757 RetTy: retType, ID: CGM.getHLSLRuntime().getSignIntrinsic(),
758 Args: ArrayRef<Value *>{Op0}, FMFSource: nullptr, Name: "hlsl.sign");
759 }
760 case Builtin::BI__builtin_hlsl_elementwise_radians: {
761 Value *Op0 = EmitScalarExpr(E: E->getArg(Arg: 0));
762 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
763 "radians operand must have a float representation");
764 return Builder.CreateIntrinsic(
765 /*ReturnType=*/RetTy: Op0->getType(),
766 ID: CGM.getHLSLRuntime().getRadiansIntrinsic(), Args: ArrayRef<Value *>{Op0},
767 FMFSource: nullptr, Name: "hlsl.radians");
768 }
769 case Builtin::BI__builtin_hlsl_buffer_update_counter: {
770 Value *ResHandle = EmitScalarExpr(E: E->getArg(Arg: 0));
771 Value *Offset = EmitScalarExpr(E: E->getArg(Arg: 1));
772 Value *OffsetI8 = Builder.CreateIntCast(V: Offset, DestTy: Int8Ty, isSigned: true);
773 return Builder.CreateIntrinsic(
774 /*ReturnType=*/RetTy: Offset->getType(),
775 ID: CGM.getHLSLRuntime().getBufferUpdateCounterIntrinsic(),
776 Args: ArrayRef<Value *>{ResHandle, OffsetI8}, FMFSource: nullptr);
777 }
778 case Builtin::BI__builtin_hlsl_elementwise_splitdouble: {
779
780 assert((E->getArg(0)->getType()->hasFloatingRepresentation() &&
781 E->getArg(1)->getType()->hasUnsignedIntegerRepresentation() &&
782 E->getArg(2)->getType()->hasUnsignedIntegerRepresentation()) &&
783 "asuint operands types mismatch");
784 return handleHlslSplitdouble(E, CGF: this);
785 }
786 case Builtin::BI__builtin_hlsl_elementwise_clip:
787 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
788 "clip operands types mismatch");
789 return handleHlslClip(E, CGF: this);
790 case Builtin::BI__builtin_hlsl_group_memory_barrier_with_group_sync: {
791 Intrinsic::ID ID =
792 CGM.getHLSLRuntime().getGroupMemoryBarrierWithGroupSyncIntrinsic();
793 return EmitRuntimeCall(
794 callee: Intrinsic::getOrInsertDeclaration(M: &CGM.getModule(), id: ID));
795 }
796 case Builtin::BI__builtin_get_spirv_spec_constant_bool:
797 case Builtin::BI__builtin_get_spirv_spec_constant_short:
798 case Builtin::BI__builtin_get_spirv_spec_constant_ushort:
799 case Builtin::BI__builtin_get_spirv_spec_constant_int:
800 case Builtin::BI__builtin_get_spirv_spec_constant_uint:
801 case Builtin::BI__builtin_get_spirv_spec_constant_longlong:
802 case Builtin::BI__builtin_get_spirv_spec_constant_ulonglong:
803 case Builtin::BI__builtin_get_spirv_spec_constant_half:
804 case Builtin::BI__builtin_get_spirv_spec_constant_float:
805 case Builtin::BI__builtin_get_spirv_spec_constant_double: {
806 llvm::Function *SpecConstantFn = getSpecConstantFunction(SpecConstantType: E->getType());
807 llvm::Value *SpecId = EmitScalarExpr(E: E->getArg(Arg: 0));
808 llvm::Value *DefaultVal = EmitScalarExpr(E: E->getArg(Arg: 1));
809 llvm::Value *Args[] = {SpecId, DefaultVal};
810 return Builder.CreateCall(Callee: SpecConstantFn, Args);
811 }
812 }
813 return nullptr;
814}
815
816llvm::Function *clang::CodeGen::CodeGenFunction::getSpecConstantFunction(
817 const clang::QualType &SpecConstantType) {
818
819 // Find or create the declaration for the function.
820 llvm::Module *M = &CGM.getModule();
821 std::string MangledName =
822 getSpecConstantFunctionName(SpecConstantType, Context&: getContext());
823 llvm::Function *SpecConstantFn = M->getFunction(Name: MangledName);
824
825 if (!SpecConstantFn) {
826 llvm::Type *IntType = ConvertType(T: getContext().IntTy);
827 llvm::Type *RetTy = ConvertType(T: SpecConstantType);
828 llvm::Type *ArgTypes[] = {IntType, RetTy};
829 llvm::FunctionType *FnTy = llvm::FunctionType::get(Result: RetTy, Params: ArgTypes, isVarArg: false);
830 SpecConstantFn = llvm::Function::Create(
831 Ty: FnTy, Linkage: llvm::GlobalValue::ExternalLinkage, N: MangledName, M);
832 }
833 return SpecConstantFn;
834}
835