clang 23.0.0git
CGHLSLBuiltins.cpp
Go to the documentation of this file.
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
24 "asdouble operands types mismatch");
25 Value *OpLowBits = CGF.EmitScalarExpr(E->getArg(0));
26 Value *OpHighBits = CGF.EmitScalarExpr(E->getArg(1));
27
28 llvm::Type *ResultType = CGF.DoubleTy;
29 int N = 1;
30 if (auto *VTy = E->getArg(0)->getType()->getAs<clang::VectorType>()) {
31 N = VTy->getNumElements();
32 ResultType = llvm::FixedVectorType::get(CGF.DoubleTy, N);
33 }
34
35 if (CGF.CGM.getTarget().getTriple().isDXIL())
36 return CGF.Builder.CreateIntrinsic(
37 /*ReturnType=*/ResultType, Intrinsic::dx_asdouble,
38 {OpLowBits, OpHighBits}, nullptr, "hlsl.asdouble");
39
40 if (!E->getArg(0)->getType()->isVectorType()) {
41 OpLowBits = CGF.Builder.CreateVectorSplat(1, OpLowBits);
42 OpHighBits = CGF.Builder.CreateVectorSplat(1, OpHighBits);
43 }
44
46 for (int i = 0; i < N; i++) {
47 Mask.push_back(i);
48 Mask.push_back(i + N);
49 }
50
51 Value *BitVec = CGF.Builder.CreateShuffleVector(OpLowBits, OpHighBits, Mask);
52
53 return CGF.Builder.CreateBitCast(BitVec, ResultType);
54}
55
57 Value *Op0 = CGF->EmitScalarExpr(E->getArg(0));
58
59 Constant *FZeroConst = ConstantFP::getZero(CGF->FloatTy);
60 Value *CMP;
61 Value *LastInstr;
62
63 if (const auto *VecTy = E->getArg(0)->getType()->getAs<clang::VectorType>()) {
64 FZeroConst = ConstantVector::getSplat(
65 ElementCount::getFixed(VecTy->getNumElements()), FZeroConst);
66 auto *FCompInst = CGF->Builder.CreateFCmpOLT(Op0, FZeroConst);
67 CMP = CGF->Builder.CreateIntrinsic(
68 CGF->Builder.getInt1Ty(), CGF->CGM.getHLSLRuntime().getAnyIntrinsic(),
69 {FCompInst});
70 } else {
71 CMP = CGF->Builder.CreateFCmpOLT(Op0, FZeroConst);
72 }
73
74 if (CGF->CGM.getTarget().getTriple().isDXIL()) {
75 LastInstr = CGF->Builder.CreateIntrinsic(Intrinsic::dx_discard, {CMP});
76 } else if (CGF->CGM.getTarget().getTriple().isSPIRV()) {
77 BasicBlock *LT0 = CGF->createBasicBlock("lt0", CGF->CurFn);
78 BasicBlock *End = CGF->createBasicBlock("end", CGF->CurFn);
79
80 CGF->Builder.CreateCondBr(CMP, LT0, End);
81
82 CGF->Builder.SetInsertPoint(LT0);
83
84 CGF->Builder.CreateIntrinsic(Intrinsic::spv_discard, {});
85
86 LastInstr = CGF->Builder.CreateBr(End);
87 CGF->Builder.SetInsertPoint(End);
88 } else {
89 llvm_unreachable("Backend Codegen not supported.");
90 }
91
92 return LastInstr;
93}
94
96 Value *Op0 = CGF->EmitScalarExpr(E->getArg(0));
97 const auto *OutArg1 = dyn_cast<HLSLOutArgExpr>(E->getArg(1));
98 const auto *OutArg2 = dyn_cast<HLSLOutArgExpr>(E->getArg(2));
99
100 CallArgList Args;
101 LValue Op1TmpLValue =
102 CGF->EmitHLSLOutArgExpr(OutArg1, Args, OutArg1->getType());
103 LValue Op2TmpLValue =
104 CGF->EmitHLSLOutArgExpr(OutArg2, Args, OutArg2->getType());
105
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(0)->getType()->getAs<clang::VectorType>())
115 RetElementTy = llvm::VectorType::get(
116 CGF->Int32Ty, ElementCount::getFixed(Op0VecTy->getNumElements()));
117 auto *RetTy = llvm::StructType::get(RetElementTy, RetElementTy);
118
119 CallInst *CI = CGF->Builder.CreateIntrinsic(
120 RetTy, Intrinsic::dx_splitdouble, {Op0}, nullptr, "hlsl.splitdouble");
121
122 LowBits = CGF->Builder.CreateExtractValue(CI, 0);
123 HighBits = CGF->Builder.CreateExtractValue(CI, 1);
124 } else {
125 // For Non DXIL targets we generate the instructions.
126
127 if (!Op0->getType()->isVectorTy()) {
128 FixedVectorType *DestTy = FixedVectorType::get(CGF->Int32Ty, 2);
129 Value *Bitcast = CGF->Builder.CreateBitCast(Op0, DestTy);
130
131 LowBits = CGF->Builder.CreateExtractElement(Bitcast, (uint64_t)0);
132 HighBits = CGF->Builder.CreateExtractElement(Bitcast, 1);
133 } else {
134 int NumElements = 1;
135 if (const auto *VecTy =
137 NumElements = VecTy->getNumElements();
138
139 FixedVectorType *Uint32VecTy =
140 FixedVectorType::get(CGF->Int32Ty, NumElements * 2);
141 Value *Uint32Vec = CGF->Builder.CreateBitCast(Op0, Uint32VecTy);
142 if (NumElements == 1) {
143 LowBits = CGF->Builder.CreateExtractElement(Uint32Vec, (uint64_t)0);
144 HighBits = CGF->Builder.CreateExtractElement(Uint32Vec, 1);
145 } else {
146 SmallVector<int> EvenMask, OddMask;
147 for (int I = 0, E = NumElements; I != E; ++I) {
148 EvenMask.push_back(I * 2);
149 OddMask.push_back(I * 2 + 1);
150 }
151 LowBits = CGF->Builder.CreateShuffleVector(Uint32Vec, EvenMask);
152 HighBits = CGF->Builder.CreateShuffleVector(Uint32Vec, OddMask);
153 }
154 }
155 }
156 CGF->Builder.CreateStore(LowBits, Op1TmpLValue.getAddress());
157 auto *LastInst =
158 CGF->Builder.CreateStore(HighBits, Op2TmpLValue.getAddress());
159 CGF->EmitWritebacks(Args);
160 return LastInst;
161}
162
164 const CallExpr *E) {
165 Value *Cond = CGF.EmitScalarExpr(E->getArg(0));
166 llvm::Type *I32 = CGF.Int32Ty;
167
168 llvm::Type *Vec4I32 = llvm::FixedVectorType::get(I32, 4);
169 [[maybe_unused]] llvm::StructType *Struct4I32 =
170 llvm::StructType::get(CGF.getLLVMContext(), {I32, I32, I32, I32});
171
172 if (CGF.CGM.getTarget().getTriple().isDXIL()) {
173 // Call DXIL intrinsic: returns { i32, i32, i32, i32 }
174 llvm::Function *Fn = CGF.CGM.getIntrinsic(Intrinsic::dx_wave_ballot, {I32});
175
176 Value *StructVal = CGF.EmitRuntimeCall(Fn, Cond);
177 assert(StructVal->getType() == Struct4I32 &&
178 "dx.wave.ballot must return {i32,i32,i32,i32}");
179
180 // Reassemble struct to <4 x i32>
181 llvm::Value *VecVal = llvm::PoisonValue::get(Vec4I32);
182 for (unsigned I = 0; I < 4; ++I) {
183 Value *Elt = CGF.Builder.CreateExtractValue(StructVal, I);
184 VecVal =
185 CGF.Builder.CreateInsertElement(VecVal, Elt, CGF.Builder.getInt32(I));
186 }
187
188 return VecVal;
189 }
190
191 if (CGF.CGM.getTarget().getTriple().isSPIRV())
192 return CGF.EmitRuntimeCall(
193 CGF.CGM.getIntrinsic(Intrinsic::spv_subgroup_ballot), Cond);
194
195 llvm_unreachable(
196 "WaveActiveBallot is only supported for DXIL and SPIRV targets");
197}
198
200 const CallExpr *E) {
201 Value *Op0 = CGF.EmitScalarExpr(E->getArg(0));
202 QualType Op0Ty = E->getArg(0)->getType();
203 llvm::Type *ResType = CGF.FloatTy;
204 uint64_t NumElements = 0;
205 if (Op0->getType()->isVectorTy()) {
206 NumElements =
207 E->getArg(0)->getType()->castAs<clang::VectorType>()->getNumElements();
208 ResType =
209 llvm::VectorType::get(ResType, ElementCount::getFixed(NumElements));
210 }
212 llvm_unreachable(
213 "f16tof32 operand must have an unsigned int representation");
214
215 if (CGF.CGM.getTriple().isDXIL())
216 return CGF.Builder.CreateIntrinsic(ResType, Intrinsic::dx_legacyf16tof32,
217 ArrayRef<Value *>{Op0}, nullptr,
218 "hlsl.f16tof32");
219
220 if (CGF.CGM.getTriple().isSPIRV()) {
221 // We use the SPIRV UnpackHalf2x16 operation to avoid the need for the
222 // Int16 and Float16 capabilities
223 auto *UnpackType =
224 llvm::VectorType::get(CGF.FloatTy, ElementCount::getFixed(2));
225
226 if (NumElements == 0) {
227 // a scalar input - simply extract the first element of the unpacked
228 // vector
229 Value *Unpack = CGF.Builder.CreateIntrinsic(
230 UnpackType, Intrinsic::spv_unpackhalf2x16, ArrayRef<Value *>{Op0});
231 return CGF.Builder.CreateExtractElement(Unpack, (uint64_t)0);
232 }
233
234 // a vector input - build a congruent output vector by iterating through
235 // the input vector calling unpackhalf2x16 for each element
236 Value *Result = PoisonValue::get(ResType);
237 for (uint64_t I = 0; I < NumElements; I++) {
238 Value *InVal = CGF.Builder.CreateExtractElement(Op0, I);
239 Value *Unpack = CGF.Builder.CreateIntrinsic(
240 UnpackType, Intrinsic::spv_unpackhalf2x16, ArrayRef<Value *>{InVal});
241 Value *Res = CGF.Builder.CreateExtractElement(Unpack, (uint64_t)0);
242 Result = CGF.Builder.CreateInsertElement(Result, Res, I);
243 }
244 return Result;
245 }
246
247 llvm_unreachable("Intrinsic F16ToF32 not supported by target architecture");
248}
249
251 const CallExpr *E) {
252 Value *Op0 = CGF.EmitScalarExpr(E->getArg(0));
253 QualType Op0Ty = E->getArg(0)->getType();
254 llvm::Type *ResType = CGF.IntTy;
255 uint64_t NumElements = 0;
256 if (Op0->getType()->isVectorTy()) {
257 NumElements =
258 E->getArg(0)->getType()->castAs<clang::VectorType>()->getNumElements();
259 ResType =
260 llvm::VectorType::get(ResType, ElementCount::getFixed(NumElements));
261 }
262 if (!Op0Ty->hasFloatingRepresentation())
263 llvm_unreachable("f32tof16 operand must have a float representation");
264
265 if (CGF.CGM.getTriple().isDXIL())
266 return CGF.Builder.CreateIntrinsic(ResType, Intrinsic::dx_legacyf32tof16,
267 ArrayRef<Value *>{Op0}, nullptr,
268 "hlsl.f32tof16");
269
270 if (CGF.CGM.getTriple().isSPIRV()) {
271 // We use the SPIRV PackHalf2x16 operation to avoid the need for the
272 // Int16 and Float16 capabilities
273 auto *PackType =
274 llvm::VectorType::get(CGF.FloatTy, ElementCount::getFixed(2));
275
276 if (NumElements == 0) {
277 // a scalar input - simply insert the scalar in the first element
278 // of the 2 element float vector
279 Value *Float2 = Constant::getNullValue(PackType);
280 Float2 = CGF.Builder.CreateInsertElement(Float2, Op0, (uint64_t)0);
281 Value *Result = CGF.Builder.CreateIntrinsic(
282 ResType, Intrinsic::spv_packhalf2x16, ArrayRef<Value *>{Float2});
283 return Result;
284 }
285
286 // a vector input - build a congruent output vector by iterating through
287 // the input vector calling packhalf2x16 for each element
288 Value *Result = PoisonValue::get(ResType);
289 for (uint64_t I = 0; I < NumElements; I++) {
290 Value *Float2 = Constant::getNullValue(PackType);
291 Value *InVal = CGF.Builder.CreateExtractElement(Op0, I);
292 Float2 = CGF.Builder.CreateInsertElement(Float2, InVal, (uint64_t)0);
293 Value *Res = CGF.Builder.CreateIntrinsic(
294 CGF.IntTy, Intrinsic::spv_packhalf2x16, ArrayRef<Value *>{Float2});
295 Result = CGF.Builder.CreateInsertElement(Result, Res, I);
296 }
297 return Result;
298 }
299
300 llvm_unreachable("Intrinsic F32ToF16 not supported by target architecture");
301}
302
303static Value *emitBufferStride(CodeGenFunction *CGF, const Expr *HandleExpr,
304 LValue &Stride) {
305 // Figure out the stride of the buffer elements from the handle type.
306 auto *HandleTy =
308 QualType ElementTy = HandleTy->getContainedType();
309 Value *StrideValue = CGF->getTypeSize(ElementTy);
310 return CGF->Builder.CreateStore(StrideValue, Stride.getAddress());
311}
312
313// Return dot product intrinsic that corresponds to the QT scalar type
314static Intrinsic::ID getDotProductIntrinsic(CGHLSLRuntime &RT, QualType QT) {
315 if (QT->isFloatingType())
316 return RT.getFDotIntrinsic();
317 if (QT->isSignedIntegerType())
318 return RT.getSDotIntrinsic();
319 assert(QT->isUnsignedIntegerType());
320 return RT.getUDotIntrinsic();
321}
322
323static Intrinsic::ID getFirstBitHighIntrinsic(CGHLSLRuntime &RT, QualType QT) {
325 return RT.getFirstBitSHighIntrinsic();
326 }
327
329 return RT.getFirstBitUHighIntrinsic();
330}
331
332// Return wave active sum that corresponds to the QT scalar type
333static Intrinsic::ID getWaveActiveSumIntrinsic(llvm::Triple::ArchType Arch,
334 CGHLSLRuntime &RT, QualType QT) {
335 switch (Arch) {
336 case llvm::Triple::spirv:
337 return Intrinsic::spv_wave_reduce_sum;
338 case llvm::Triple::dxil: {
339 if (QT->isUnsignedIntegerType())
340 return Intrinsic::dx_wave_reduce_usum;
341 return Intrinsic::dx_wave_reduce_sum;
342 }
343 default:
344 llvm_unreachable("Intrinsic WaveActiveSum"
345 " not supported by target architecture");
346 }
347}
348
349// Return wave active max that corresponds to the QT scalar type
350static Intrinsic::ID getWaveActiveMaxIntrinsic(llvm::Triple::ArchType Arch,
351 CGHLSLRuntime &RT, QualType QT) {
352 switch (Arch) {
353 case llvm::Triple::spirv:
354 if (QT->isUnsignedIntegerType())
355 return Intrinsic::spv_wave_reduce_umax;
356 return Intrinsic::spv_wave_reduce_max;
357 case llvm::Triple::dxil: {
358 if (QT->isUnsignedIntegerType())
359 return Intrinsic::dx_wave_reduce_umax;
360 return Intrinsic::dx_wave_reduce_max;
361 }
362 default:
363 llvm_unreachable("Intrinsic WaveActiveMax"
364 " not supported by target architecture");
365 }
366}
367
368// Return wave active min that corresponds to the QT scalar type
369static Intrinsic::ID getWaveActiveMinIntrinsic(llvm::Triple::ArchType Arch,
370 CGHLSLRuntime &RT, QualType QT) {
371 switch (Arch) {
372 case llvm::Triple::spirv:
373 if (QT->isUnsignedIntegerType())
374 return Intrinsic::spv_wave_reduce_umin;
375 return Intrinsic::spv_wave_reduce_min;
376 case llvm::Triple::dxil: {
377 if (QT->isUnsignedIntegerType())
378 return Intrinsic::dx_wave_reduce_umin;
379 return Intrinsic::dx_wave_reduce_min;
380 }
381 default:
382 llvm_unreachable("Intrinsic WaveActiveMin"
383 " not supported by target architecture");
384 }
385}
386
387static Intrinsic::ID getPrefixCountBitsIntrinsic(llvm::Triple::ArchType Arch) {
388 switch (Arch) {
389 case llvm::Triple::spirv:
390 return Intrinsic::spv_subgroup_prefix_bit_count;
391 case llvm::Triple::dxil: {
392 return Intrinsic::dx_wave_prefix_bit_count;
393 }
394 default:
395 llvm_unreachable(
396 "WavePrefixOp instruction not supported by target architecture");
397 }
398}
399
400// Returns the mangled name for a builtin function that the SPIR-V backend
401// will expand into a spec Constant.
402static std::string getSpecConstantFunctionName(clang::QualType SpecConstantType,
403 ASTContext &Context) {
404 // The parameter types for our conceptual intrinsic function.
405 QualType ClangParamTypes[] = {Context.IntTy, SpecConstantType};
406
407 // Create a temporary FunctionDecl for the builtin fuction. It won't be
408 // added to the AST.
410 QualType FnType =
411 Context.getFunctionType(SpecConstantType, ClangParamTypes, EPI);
412 DeclarationName FuncName = &Context.Idents.get("__spirv_SpecConstant");
413 FunctionDecl *FnDeclForMangling = FunctionDecl::Create(
414 Context, Context.getTranslationUnitDecl(), SourceLocation(),
415 SourceLocation(), FuncName, FnType, /*TSI=*/nullptr, SC_Extern);
416
417 // Attach the created parameter declarations to the function declaration.
419 for (QualType ParamType : ClangParamTypes) {
421 Context, FnDeclForMangling, SourceLocation(), SourceLocation(),
422 /*IdentifierInfo*/ nullptr, ParamType, /*TSI*/ nullptr, SC_None,
423 /*DefaultArg*/ nullptr);
424 ParamDecls.push_back(PD);
425 }
426 FnDeclForMangling->setParams(ParamDecls);
427
428 // Get the mangled name.
429 std::string Name;
430 llvm::raw_string_ostream MangledNameStream(Name);
431 std::unique_ptr<MangleContext> Mangler(Context.createMangleContext());
432 Mangler->mangleName(FnDeclForMangling, MangledNameStream);
433 MangledNameStream.flush();
434
435 return Name;
436}
437
439 const CallExpr *E,
441 if (!getLangOpts().HLSL)
442 return nullptr;
443
444 switch (BuiltinID) {
445 case Builtin::BI__builtin_hlsl_adduint64: {
446 Value *OpA = EmitScalarExpr(E->getArg(0));
447 Value *OpB = EmitScalarExpr(E->getArg(1));
448 QualType Arg0Ty = E->getArg(0)->getType();
449 uint64_t NumElements = Arg0Ty->castAs<VectorType>()->getNumElements();
450 assert(Arg0Ty == E->getArg(1)->getType() &&
451 "AddUint64 operand types must match");
452 assert(Arg0Ty->hasIntegerRepresentation() &&
453 "AddUint64 operands must have an integer representation");
454 assert((NumElements == 2 || NumElements == 4) &&
455 "AddUint64 operands must have 2 or 4 elements");
456
457 llvm::Value *LowA;
458 llvm::Value *HighA;
459 llvm::Value *LowB;
460 llvm::Value *HighB;
461
462 // Obtain low and high words of inputs A and B
463 if (NumElements == 2) {
464 LowA = Builder.CreateExtractElement(OpA, (uint64_t)0, "LowA");
465 HighA = Builder.CreateExtractElement(OpA, (uint64_t)1, "HighA");
466 LowB = Builder.CreateExtractElement(OpB, (uint64_t)0, "LowB");
467 HighB = Builder.CreateExtractElement(OpB, (uint64_t)1, "HighB");
468 } else {
469 LowA = Builder.CreateShuffleVector(OpA, {0, 2}, "LowA");
470 HighA = Builder.CreateShuffleVector(OpA, {1, 3}, "HighA");
471 LowB = Builder.CreateShuffleVector(OpB, {0, 2}, "LowB");
472 HighB = Builder.CreateShuffleVector(OpB, {1, 3}, "HighB");
473 }
474
475 // Use an uadd_with_overflow to compute the sum of low words and obtain a
476 // carry value
477 llvm::Value *Carry;
478 llvm::Value *LowSum = EmitOverflowIntrinsic(
479 *this, Intrinsic::uadd_with_overflow, LowA, LowB, Carry);
480 llvm::Value *ZExtCarry =
481 Builder.CreateZExt(Carry, HighA->getType(), "CarryZExt");
482
483 // Sum the high words and the carry
484 llvm::Value *HighSum = Builder.CreateAdd(HighA, HighB, "HighSum");
485 llvm::Value *HighSumPlusCarry =
486 Builder.CreateAdd(HighSum, ZExtCarry, "HighSumPlusCarry");
487
488 if (NumElements == 4) {
489 return Builder.CreateShuffleVector(LowSum, HighSumPlusCarry, {0, 2, 1, 3},
490 "hlsl.AddUint64");
491 }
492
493 llvm::Value *Result = PoisonValue::get(OpA->getType());
494 Result = Builder.CreateInsertElement(Result, LowSum, (uint64_t)0,
495 "hlsl.AddUint64.upto0");
496 Result = Builder.CreateInsertElement(Result, HighSumPlusCarry, (uint64_t)1,
497 "hlsl.AddUint64");
498 return Result;
499 }
500 case Builtin::BI__builtin_hlsl_resource_getpointer: {
501 Value *HandleOp = EmitScalarExpr(E->getArg(0));
502 Value *IndexOp = EmitScalarExpr(E->getArg(1));
503
504 llvm::Type *RetTy = ConvertType(E->getType());
505 return Builder.CreateIntrinsic(
506 RetTy, CGM.getHLSLRuntime().getCreateResourceGetPointerIntrinsic(),
507 ArrayRef<Value *>{HandleOp, IndexOp});
508 }
509 case Builtin::BI__builtin_hlsl_resource_sample: {
510 Value *HandleOp = EmitScalarExpr(E->getArg(0));
511 Value *SamplerOp = EmitScalarExpr(E->getArg(1));
512 Value *CoordOp = EmitScalarExpr(E->getArg(2));
513
515 Args.push_back(HandleOp);
516 Args.push_back(SamplerOp);
517 Args.push_back(CoordOp);
518 if (E->getNumArgs() > 3) {
519 Args.push_back(EmitScalarExpr(E->getArg(3)));
520 } else {
521 // Default offset is 0.
522 // We need to know the type of the offset. It should be a vector of i32
523 // with the same number of elements as the coordinate, or scalar i32.
524 llvm::Type *CoordTy = CoordOp->getType();
525 llvm::Type *Int32Ty = Builder.getInt32Ty();
526 llvm::Type *OffsetTy = Int32Ty;
527 if (auto *VT = dyn_cast<llvm::FixedVectorType>(CoordTy))
528 OffsetTy = llvm::FixedVectorType::get(Int32Ty, VT->getNumElements());
529 Args.push_back(llvm::Constant::getNullValue(OffsetTy));
530 }
531
532 llvm::Type *RetTy = ConvertType(E->getType());
533 if (E->getNumArgs() <= 4) {
534 return Builder.CreateIntrinsic(
535 RetTy, CGM.getHLSLRuntime().getSampleIntrinsic(), Args);
536 }
537
538 llvm::Value *Clamp = EmitScalarExpr(E->getArg(4));
539 // The builtin is defined with variadic arguments, so the clamp parameter
540 // might have been promoted to double. The intrinsic requires a 32-bit
541 // float.
542 if (Clamp->getType() != Builder.getFloatTy())
543 Clamp = Builder.CreateFPCast(Clamp, Builder.getFloatTy());
544 Args.push_back(Clamp);
545 return Builder.CreateIntrinsic(
546 RetTy, CGM.getHLSLRuntime().getSampleClampIntrinsic(), Args);
547 }
548 case Builtin::BI__builtin_hlsl_resource_load_with_status: {
549 Value *HandleOp = EmitScalarExpr(E->getArg(0));
550 Value *IndexOp = EmitScalarExpr(E->getArg(1));
551
552 // Get the *address* of the status argument to write to it by reference
553 LValue StatusLVal = EmitLValue(E->getArg(2));
554 Address StatusAddr = StatusLVal.getAddress();
555
556 QualType HandleTy = E->getArg(0)->getType();
557 const HLSLAttributedResourceType *RT =
558 HandleTy->getAs<HLSLAttributedResourceType>();
559 assert(CGM.getTarget().getTriple().getArch() == llvm::Triple::dxil &&
560 "Only DXIL currently implements load with status");
561
562 Intrinsic::ID IntrID = RT->getAttrs().RawBuffer
563 ? llvm::Intrinsic::dx_resource_load_rawbuffer
564 : llvm::Intrinsic::dx_resource_load_typedbuffer;
565
566 llvm::Type *DataTy = ConvertType(E->getType());
567 llvm::Type *RetTy = llvm::StructType::get(Builder.getContext(),
568 {DataTy, Builder.getInt1Ty()});
569
571 Args.push_back(HandleOp);
572 Args.push_back(IndexOp);
573
574 if (RT->getAttrs().RawBuffer) {
575 Value *Offset = Builder.getInt32(0);
576 Args.push_back(Offset);
577 }
578
579 // The load intrinsics give us a (T value, i1 status) pair -
580 // shepherd these into the return value and out reference respectively.
581 Value *ResRet =
582 Builder.CreateIntrinsic(RetTy, IntrID, Args, {}, "ld.struct");
583 Value *LoadedValue = Builder.CreateExtractValue(ResRet, {0}, "ld.value");
584 Value *StatusBit = Builder.CreateExtractValue(ResRet, {1}, "ld.status");
585 Value *ExtendedStatus =
586 Builder.CreateZExt(StatusBit, Builder.getInt32Ty(), "ld.status.ext");
587 Builder.CreateStore(ExtendedStatus, StatusAddr);
588
589 return LoadedValue;
590 }
591 case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: {
592 llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
593 return llvm::PoisonValue::get(HandleTy);
594 }
595 case Builtin::BI__builtin_hlsl_resource_handlefrombinding: {
596 llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
597 Value *RegisterOp = EmitScalarExpr(E->getArg(1));
598 Value *SpaceOp = EmitScalarExpr(E->getArg(2));
599 Value *RangeOp = EmitScalarExpr(E->getArg(3));
600 Value *IndexOp = EmitScalarExpr(E->getArg(4));
601 Value *Name = EmitScalarExpr(E->getArg(5));
602 llvm::Intrinsic::ID IntrinsicID =
603 CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic();
604 SmallVector<Value *> Args{SpaceOp, RegisterOp, RangeOp, IndexOp, Name};
605 return Builder.CreateIntrinsic(HandleTy, IntrinsicID, Args);
606 }
607 case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: {
608 llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
609 Value *OrderID = EmitScalarExpr(E->getArg(1));
610 Value *SpaceOp = EmitScalarExpr(E->getArg(2));
611 Value *RangeOp = EmitScalarExpr(E->getArg(3));
612 Value *IndexOp = EmitScalarExpr(E->getArg(4));
613 Value *Name = EmitScalarExpr(E->getArg(5));
614 llvm::Intrinsic::ID IntrinsicID =
615 CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();
616 SmallVector<Value *> Args{OrderID, SpaceOp, RangeOp, IndexOp, Name};
617 return Builder.CreateIntrinsic(HandleTy, IntrinsicID, Args);
618 }
619 case Builtin::BI__builtin_hlsl_resource_counterhandlefromimplicitbinding: {
620 Value *MainHandle = EmitScalarExpr(E->getArg(0));
621 if (!CGM.getTriple().isSPIRV())
622 return MainHandle;
623
624 llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
625 Value *OrderID = EmitScalarExpr(E->getArg(1));
626 Value *SpaceOp = EmitScalarExpr(E->getArg(2));
627 llvm::Intrinsic::ID IntrinsicID =
628 llvm::Intrinsic::spv_resource_counterhandlefromimplicitbinding;
629 SmallVector<Value *> Args{MainHandle, OrderID, SpaceOp};
630 return Builder.CreateIntrinsic(HandleTy, IntrinsicID, Args);
631 }
632 case Builtin::BI__builtin_hlsl_resource_nonuniformindex: {
633 Value *IndexOp = EmitScalarExpr(E->getArg(0));
634 llvm::Type *RetTy = ConvertType(E->getType());
635 return Builder.CreateIntrinsic(
636 RetTy, CGM.getHLSLRuntime().getNonUniformResourceIndexIntrinsic(),
637 ArrayRef<Value *>{IndexOp});
638 }
639 case Builtin::BI__builtin_hlsl_resource_getdimensions_x: {
640 Value *Handle = EmitScalarExpr(E->getArg(0));
641 LValue Dim = EmitLValue(E->getArg(1));
642 llvm::Type *RetTy = llvm::Type::getInt32Ty(getLLVMContext());
643 Value *DimValue = Builder.CreateIntrinsic(
644 RetTy, CGM.getHLSLRuntime().getGetDimensionsXIntrinsic(),
645 ArrayRef<Value *>{Handle});
646 return Builder.CreateStore(DimValue, Dim.getAddress());
647 }
648 case Builtin::BI__builtin_hlsl_resource_getstride: {
649 LValue Stride = EmitLValue(E->getArg(1));
650 return emitBufferStride(this, E->getArg(0), Stride);
651 }
652 case Builtin::BI__builtin_hlsl_all: {
653 Value *Op0 = EmitScalarExpr(E->getArg(0));
654 return Builder.CreateIntrinsic(
655 /*ReturnType=*/llvm::Type::getInt1Ty(getLLVMContext()),
656 CGM.getHLSLRuntime().getAllIntrinsic(), ArrayRef<Value *>{Op0}, nullptr,
657 "hlsl.all");
658 }
659 case Builtin::BI__builtin_hlsl_and: {
660 Value *Op0 = EmitScalarExpr(E->getArg(0));
661 Value *Op1 = EmitScalarExpr(E->getArg(1));
662 return Builder.CreateAnd(Op0, Op1, "hlsl.and");
663 }
664 case Builtin::BI__builtin_hlsl_or: {
665 Value *Op0 = EmitScalarExpr(E->getArg(0));
666 Value *Op1 = EmitScalarExpr(E->getArg(1));
667 return Builder.CreateOr(Op0, Op1, "hlsl.or");
668 }
669 case Builtin::BI__builtin_hlsl_any: {
670 Value *Op0 = EmitScalarExpr(E->getArg(0));
671 return Builder.CreateIntrinsic(
672 /*ReturnType=*/llvm::Type::getInt1Ty(getLLVMContext()),
673 CGM.getHLSLRuntime().getAnyIntrinsic(), ArrayRef<Value *>{Op0}, nullptr,
674 "hlsl.any");
675 }
676 case Builtin::BI__builtin_hlsl_asdouble:
677 return handleAsDoubleBuiltin(*this, E);
678 case Builtin::BI__builtin_hlsl_elementwise_clamp: {
679 Value *OpX = EmitScalarExpr(E->getArg(0));
680 Value *OpMin = EmitScalarExpr(E->getArg(1));
681 Value *OpMax = EmitScalarExpr(E->getArg(2));
682
683 QualType Ty = E->getArg(0)->getType();
684 if (auto *VecTy = Ty->getAs<VectorType>())
685 Ty = VecTy->getElementType();
686
687 Intrinsic::ID Intr;
688 if (Ty->isFloatingType()) {
689 Intr = CGM.getHLSLRuntime().getNClampIntrinsic();
690 } else if (Ty->isUnsignedIntegerType()) {
691 Intr = CGM.getHLSLRuntime().getUClampIntrinsic();
692 } else {
693 assert(Ty->isSignedIntegerType());
694 Intr = CGM.getHLSLRuntime().getSClampIntrinsic();
695 }
696 return Builder.CreateIntrinsic(
697 /*ReturnType=*/OpX->getType(), Intr,
698 ArrayRef<Value *>{OpX, OpMin, OpMax}, nullptr, "hlsl.clamp");
699 }
700 case Builtin::BI__builtin_hlsl_crossf16:
701 case Builtin::BI__builtin_hlsl_crossf32: {
702 Value *Op0 = EmitScalarExpr(E->getArg(0));
703 Value *Op1 = EmitScalarExpr(E->getArg(1));
704 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
706 "cross operands must have a float representation");
707 // make sure each vector has exactly 3 elements
708 assert(
709 E->getArg(0)->getType()->castAs<VectorType>()->getNumElements() == 3 &&
710 E->getArg(1)->getType()->castAs<VectorType>()->getNumElements() == 3 &&
711 "input vectors must have 3 elements each");
712 return Builder.CreateIntrinsic(
713 /*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getCrossIntrinsic(),
714 ArrayRef<Value *>{Op0, Op1}, nullptr, "hlsl.cross");
715 }
716 case Builtin::BI__builtin_hlsl_dot: {
717 Value *Op0 = EmitScalarExpr(E->getArg(0));
718 Value *Op1 = EmitScalarExpr(E->getArg(1));
719 llvm::Type *T0 = Op0->getType();
720 llvm::Type *T1 = Op1->getType();
721
722 // If the arguments are scalars, just emit a multiply
723 if (!T0->isVectorTy() && !T1->isVectorTy()) {
724 if (T0->isFloatingPointTy())
725 return Builder.CreateFMul(Op0, Op1, "hlsl.dot");
726
727 if (T0->isIntegerTy())
728 return Builder.CreateMul(Op0, Op1, "hlsl.dot");
729
730 llvm_unreachable(
731 "Scalar dot product is only supported on ints and floats.");
732 }
733 // For vectors, validate types and emit the appropriate intrinsic
734 assert(CGM.getContext().hasSameUnqualifiedType(E->getArg(0)->getType(),
735 E->getArg(1)->getType()) &&
736 "Dot product operands must have the same type.");
737
738 auto *VecTy0 = E->getArg(0)->getType()->castAs<VectorType>();
739 assert(VecTy0 && "Dot product argument must be a vector.");
740
741 return Builder.CreateIntrinsic(
742 /*ReturnType=*/T0->getScalarType(),
743 getDotProductIntrinsic(CGM.getHLSLRuntime(), VecTy0->getElementType()),
744 ArrayRef<Value *>{Op0, Op1}, nullptr, "hlsl.dot");
745 }
746 case Builtin::BI__builtin_hlsl_dot4add_i8packed: {
747 Value *X = EmitScalarExpr(E->getArg(0));
748 Value *Y = EmitScalarExpr(E->getArg(1));
749 Value *Acc = EmitScalarExpr(E->getArg(2));
750
751 Intrinsic::ID ID = CGM.getHLSLRuntime().getDot4AddI8PackedIntrinsic();
752 // Note that the argument order disagrees between the builtin and the
753 // intrinsic here.
754 return Builder.CreateIntrinsic(
755 /*ReturnType=*/Acc->getType(), ID, ArrayRef<Value *>{Acc, X, Y},
756 nullptr, "hlsl.dot4add.i8packed");
757 }
758 case Builtin::BI__builtin_hlsl_dot4add_u8packed: {
759 Value *X = EmitScalarExpr(E->getArg(0));
760 Value *Y = EmitScalarExpr(E->getArg(1));
761 Value *Acc = EmitScalarExpr(E->getArg(2));
762
763 Intrinsic::ID ID = CGM.getHLSLRuntime().getDot4AddU8PackedIntrinsic();
764 // Note that the argument order disagrees between the builtin and the
765 // intrinsic here.
766 return Builder.CreateIntrinsic(
767 /*ReturnType=*/Acc->getType(), ID, ArrayRef<Value *>{Acc, X, Y},
768 nullptr, "hlsl.dot4add.u8packed");
769 }
770 case Builtin::BI__builtin_hlsl_elementwise_firstbithigh: {
771 Value *X = EmitScalarExpr(E->getArg(0));
772
773 return Builder.CreateIntrinsic(
774 /*ReturnType=*/ConvertType(E->getType()),
775 getFirstBitHighIntrinsic(CGM.getHLSLRuntime(), E->getArg(0)->getType()),
776 ArrayRef<Value *>{X}, nullptr, "hlsl.firstbithigh");
777 }
778 case Builtin::BI__builtin_hlsl_elementwise_firstbitlow: {
779 Value *X = EmitScalarExpr(E->getArg(0));
780
781 return Builder.CreateIntrinsic(
782 /*ReturnType=*/ConvertType(E->getType()),
783 CGM.getHLSLRuntime().getFirstBitLowIntrinsic(), ArrayRef<Value *>{X},
784 nullptr, "hlsl.firstbitlow");
785 }
786 case Builtin::BI__builtin_hlsl_lerp: {
787 Value *X = EmitScalarExpr(E->getArg(0));
788 Value *Y = EmitScalarExpr(E->getArg(1));
789 Value *S = EmitScalarExpr(E->getArg(2));
791 llvm_unreachable("lerp operand must have a float representation");
792 return Builder.CreateIntrinsic(
793 /*ReturnType=*/X->getType(), CGM.getHLSLRuntime().getLerpIntrinsic(),
794 ArrayRef<Value *>{X, Y, S}, nullptr, "hlsl.lerp");
795 }
796 case Builtin::BI__builtin_hlsl_normalize: {
797 Value *X = EmitScalarExpr(E->getArg(0));
798
799 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
800 "normalize operand must have a float representation");
801
802 return Builder.CreateIntrinsic(
803 /*ReturnType=*/X->getType(),
804 CGM.getHLSLRuntime().getNormalizeIntrinsic(), ArrayRef<Value *>{X},
805 nullptr, "hlsl.normalize");
806 }
807 case Builtin::BI__builtin_hlsl_elementwise_degrees: {
808 Value *X = EmitScalarExpr(E->getArg(0));
809
810 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
811 "degree operand must have a float representation");
812
813 return Builder.CreateIntrinsic(
814 /*ReturnType=*/X->getType(), CGM.getHLSLRuntime().getDegreesIntrinsic(),
815 ArrayRef<Value *>{X}, nullptr, "hlsl.degrees");
816 }
817 case Builtin::BI__builtin_hlsl_elementwise_f16tof32: {
818 return handleElementwiseF16ToF32(*this, E);
819 }
820 case Builtin::BI__builtin_hlsl_elementwise_f32tof16: {
821 return handleElementwiseF32ToF16(*this, E);
822 }
823 case Builtin::BI__builtin_hlsl_elementwise_frac: {
824 Value *Op0 = EmitScalarExpr(E->getArg(0));
826 llvm_unreachable("frac operand must have a float representation");
827 return Builder.CreateIntrinsic(
828 /*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getFracIntrinsic(),
829 ArrayRef<Value *>{Op0}, nullptr, "hlsl.frac");
830 }
831 case Builtin::BI__builtin_hlsl_elementwise_isinf: {
832 Value *Op0 = EmitScalarExpr(E->getArg(0));
833 llvm::Type *Xty = Op0->getType();
834 llvm::Type *retType = llvm::Type::getInt1Ty(this->getLLVMContext());
835 if (Xty->isVectorTy()) {
836 auto *XVecTy = E->getArg(0)->getType()->castAs<VectorType>();
837 retType = llvm::VectorType::get(
838 retType, ElementCount::getFixed(XVecTy->getNumElements()));
839 }
841 llvm_unreachable("isinf operand must have a float representation");
842 return Builder.CreateIntrinsic(
843 retType, CGM.getHLSLRuntime().getIsInfIntrinsic(),
844 ArrayRef<Value *>{Op0}, nullptr, "hlsl.isinf");
845 }
846 case Builtin::BI__builtin_hlsl_elementwise_isnan: {
847 Value *Op0 = EmitScalarExpr(E->getArg(0));
848 llvm::Type *Xty = Op0->getType();
849 llvm::Type *retType = llvm::Type::getInt1Ty(this->getLLVMContext());
850 if (Xty->isVectorTy()) {
851 auto *XVecTy = E->getArg(0)->getType()->castAs<VectorType>();
852 retType = llvm::VectorType::get(
853 retType, ElementCount::getFixed(XVecTy->getNumElements()));
854 }
856 llvm_unreachable("isnan operand must have a float representation");
857 return Builder.CreateIntrinsic(
858 retType, CGM.getHLSLRuntime().getIsNaNIntrinsic(),
859 ArrayRef<Value *>{Op0}, nullptr, "hlsl.isnan");
860 }
861 case Builtin::BI__builtin_hlsl_mad: {
862 Value *M = EmitScalarExpr(E->getArg(0));
863 Value *A = EmitScalarExpr(E->getArg(1));
864 Value *B = EmitScalarExpr(E->getArg(2));
866 return Builder.CreateIntrinsic(
867 /*ReturnType*/ M->getType(), Intrinsic::fmuladd,
868 ArrayRef<Value *>{M, A, B}, nullptr, "hlsl.fmad");
869
871 if (CGM.getTarget().getTriple().getArch() == llvm::Triple::dxil)
872 return Builder.CreateIntrinsic(
873 /*ReturnType*/ M->getType(), Intrinsic::dx_imad,
874 ArrayRef<Value *>{M, A, B}, nullptr, "dx.imad");
875
876 Value *Mul = Builder.CreateNSWMul(M, A);
877 return Builder.CreateNSWAdd(Mul, B);
878 }
880 if (CGM.getTarget().getTriple().getArch() == llvm::Triple::dxil)
881 return Builder.CreateIntrinsic(
882 /*ReturnType=*/M->getType(), Intrinsic::dx_umad,
883 ArrayRef<Value *>{M, A, B}, nullptr, "dx.umad");
884
885 Value *Mul = Builder.CreateNUWMul(M, A);
886 return Builder.CreateNUWAdd(Mul, B);
887 }
888 case Builtin::BI__builtin_hlsl_elementwise_rcp: {
889 Value *Op0 = EmitScalarExpr(E->getArg(0));
891 llvm_unreachable("rcp operand must have a float representation");
892 llvm::Type *Ty = Op0->getType();
893 llvm::Type *EltTy = Ty->getScalarType();
894 Constant *One = Ty->isVectorTy()
895 ? ConstantVector::getSplat(
896 ElementCount::getFixed(
897 cast<FixedVectorType>(Ty)->getNumElements()),
898 ConstantFP::get(EltTy, 1.0))
899 : ConstantFP::get(EltTy, 1.0);
900 return Builder.CreateFDiv(One, Op0, "hlsl.rcp");
901 }
902 case Builtin::BI__builtin_hlsl_elementwise_rsqrt: {
903 Value *Op0 = EmitScalarExpr(E->getArg(0));
905 llvm_unreachable("rsqrt operand must have a float representation");
906 return Builder.CreateIntrinsic(
907 /*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getRsqrtIntrinsic(),
908 ArrayRef<Value *>{Op0}, nullptr, "hlsl.rsqrt");
909 }
910 case Builtin::BI__builtin_hlsl_elementwise_saturate: {
911 Value *Op0 = EmitScalarExpr(E->getArg(0));
912 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
913 "saturate operand must have a float representation");
914 return Builder.CreateIntrinsic(
915 /*ReturnType=*/Op0->getType(),
916 CGM.getHLSLRuntime().getSaturateIntrinsic(), ArrayRef<Value *>{Op0},
917 nullptr, "hlsl.saturate");
918 }
919 case Builtin::BI__builtin_hlsl_wave_prefix_count_bits: {
920 Value *Op = EmitScalarExpr(E->getArg(0));
921 assert(Op->getType()->isIntegerTy(1) &&
922 "WavePrefixBitCount operand must be a boolean type");
923
924 Intrinsic::ID IID =
925 getPrefixCountBitsIntrinsic(getTarget().getTriple().getArch());
926
927 return EmitRuntimeCall(
928 Intrinsic::getOrInsertDeclaration(&CGM.getModule(), IID), ArrayRef{Op},
929 "hlsl.wave.prefix.bit.count");
930 }
931 case Builtin::BI__builtin_hlsl_select: {
932 Value *OpCond = EmitScalarExpr(E->getArg(0));
933 RValue RValTrue = EmitAnyExpr(E->getArg(1));
934 Value *OpTrue =
935 RValTrue.isScalar()
936 ? RValTrue.getScalarVal()
937 : Builder.CreateLoad(RValTrue.getAggregateAddress(), "true_val");
938 RValue RValFalse = EmitAnyExpr(E->getArg(2));
939 Value *OpFalse =
940 RValFalse.isScalar()
941 ? RValFalse.getScalarVal()
942 : Builder.CreateLoad(RValFalse.getAggregateAddress(), "false_val");
943 if (auto *VTy = E->getType()->getAs<VectorType>()) {
944 if (!OpTrue->getType()->isVectorTy())
945 OpTrue =
946 Builder.CreateVectorSplat(VTy->getNumElements(), OpTrue, "splat");
947 if (!OpFalse->getType()->isVectorTy())
948 OpFalse =
949 Builder.CreateVectorSplat(VTy->getNumElements(), OpFalse, "splat");
950 }
951
952 Value *SelectVal =
953 Builder.CreateSelect(OpCond, OpTrue, OpFalse, "hlsl.select");
954 if (!RValTrue.isScalar())
955 Builder.CreateStore(SelectVal, ReturnValue.getAddress(),
956 ReturnValue.isVolatile());
957
958 return SelectVal;
959 }
960 case Builtin::BI__builtin_hlsl_step: {
961 Value *Op0 = EmitScalarExpr(E->getArg(0));
962 Value *Op1 = EmitScalarExpr(E->getArg(1));
963 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
965 "step operands must have a float representation");
966 return Builder.CreateIntrinsic(
967 /*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getStepIntrinsic(),
968 ArrayRef<Value *>{Op0, Op1}, nullptr, "hlsl.step");
969 }
970 case Builtin::BI__builtin_hlsl_wave_active_all_true: {
971 Value *Op = EmitScalarExpr(E->getArg(0));
972 assert(Op->getType()->isIntegerTy(1) &&
973 "Intrinsic WaveActiveAllTrue operand must be a bool");
974
975 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveActiveAllTrueIntrinsic();
976 return EmitRuntimeCall(
977 Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID), {Op});
978 }
979 case Builtin::BI__builtin_hlsl_wave_active_any_true: {
980 Value *Op = EmitScalarExpr(E->getArg(0));
981 assert(Op->getType()->isIntegerTy(1) &&
982 "Intrinsic WaveActiveAnyTrue operand must be a bool");
983
984 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveActiveAnyTrueIntrinsic();
985 return EmitRuntimeCall(
986 Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID), {Op});
987 }
988 case Builtin::BI__builtin_hlsl_wave_active_ballot: {
989 [[maybe_unused]] Value *Op = EmitScalarExpr(E->getArg(0));
990 assert(Op->getType()->isIntegerTy(1) &&
991 "Intrinsic WaveActiveBallot operand must be a bool");
992
993 return handleHlslWaveActiveBallot(*this, E);
994 }
995 case Builtin::BI__builtin_hlsl_wave_active_count_bits: {
996 Value *OpExpr = EmitScalarExpr(E->getArg(0));
997 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveActiveCountBitsIntrinsic();
998 return EmitRuntimeCall(
999 Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID),
1000 ArrayRef{OpExpr});
1001 }
1002 case Builtin::BI__builtin_hlsl_wave_active_sum: {
1003 // Due to the use of variadic arguments, explicitly retreive argument
1004 Value *OpExpr = EmitScalarExpr(E->getArg(0));
1005 Intrinsic::ID IID = getWaveActiveSumIntrinsic(
1006 getTarget().getTriple().getArch(), CGM.getHLSLRuntime(),
1007 E->getArg(0)->getType());
1008
1009 return EmitRuntimeCall(Intrinsic::getOrInsertDeclaration(
1010 &CGM.getModule(), IID, {OpExpr->getType()}),
1011 ArrayRef{OpExpr}, "hlsl.wave.active.sum");
1012 }
1013 case Builtin::BI__builtin_hlsl_wave_active_max: {
1014 // Due to the use of variadic arguments, explicitly retreive argument
1015 Value *OpExpr = EmitScalarExpr(E->getArg(0));
1016 Intrinsic::ID IID = getWaveActiveMaxIntrinsic(
1017 getTarget().getTriple().getArch(), CGM.getHLSLRuntime(),
1018 E->getArg(0)->getType());
1019
1020 return EmitRuntimeCall(Intrinsic::getOrInsertDeclaration(
1021 &CGM.getModule(), IID, {OpExpr->getType()}),
1022 ArrayRef{OpExpr}, "hlsl.wave.active.max");
1023 }
1024 case Builtin::BI__builtin_hlsl_wave_active_min: {
1025 // Due to the use of variadic arguments, explicitly retreive argument
1026 Value *OpExpr = EmitScalarExpr(E->getArg(0));
1027 Intrinsic::ID IID = getWaveActiveMinIntrinsic(
1028 getTarget().getTriple().getArch(), CGM.getHLSLRuntime(),
1029 E->getArg(0)->getType());
1030
1031 return EmitRuntimeCall(Intrinsic::getOrInsertDeclaration(
1032 &CGM.getModule(), IID, {OpExpr->getType()}),
1033 ArrayRef{OpExpr}, "hlsl.wave.active.min");
1034 }
1035 case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
1036 // We don't define a SPIR-V intrinsic, instead it is a SPIR-V built-in
1037 // defined in SPIRVBuiltins.td. So instead we manually get the matching name
1038 // for the DirectX intrinsic and the demangled builtin name
1039 switch (CGM.getTarget().getTriple().getArch()) {
1040 case llvm::Triple::dxil:
1041 return EmitRuntimeCall(Intrinsic::getOrInsertDeclaration(
1042 &CGM.getModule(), Intrinsic::dx_wave_getlaneindex));
1043 case llvm::Triple::spirv:
1044 return EmitRuntimeCall(CGM.CreateRuntimeFunction(
1045 llvm::FunctionType::get(IntTy, {}, false),
1046 "__hlsl_wave_get_lane_index", {}, false, true));
1047 default:
1048 llvm_unreachable(
1049 "Intrinsic WaveGetLaneIndex not supported by target architecture");
1050 }
1051 }
1052 case Builtin::BI__builtin_hlsl_wave_is_first_lane: {
1053 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveIsFirstLaneIntrinsic();
1054 return EmitRuntimeCall(
1055 Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID));
1056 }
1057 case Builtin::BI__builtin_hlsl_wave_get_lane_count: {
1058 Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveGetLaneCountIntrinsic();
1059 return EmitRuntimeCall(
1060 Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID));
1061 }
1062 case Builtin::BI__builtin_hlsl_wave_read_lane_at: {
1063 // Due to the use of variadic arguments we must explicitly retreive them and
1064 // create our function type.
1065 Value *OpExpr = EmitScalarExpr(E->getArg(0));
1066 Value *OpIndex = EmitScalarExpr(E->getArg(1));
1067 return EmitRuntimeCall(
1068 Intrinsic::getOrInsertDeclaration(
1069 &CGM.getModule(), CGM.getHLSLRuntime().getWaveReadLaneAtIntrinsic(),
1070 {OpExpr->getType()}),
1071 ArrayRef{OpExpr, OpIndex}, "hlsl.wave.readlane");
1072 }
1073 case Builtin::BI__builtin_hlsl_elementwise_sign: {
1074 auto *Arg0 = E->getArg(0);
1075 Value *Op0 = EmitScalarExpr(Arg0);
1076 llvm::Type *Xty = Op0->getType();
1077 llvm::Type *retType = llvm::Type::getInt32Ty(this->getLLVMContext());
1078 if (Xty->isVectorTy()) {
1079 auto *XVecTy = Arg0->getType()->castAs<VectorType>();
1080 retType = llvm::VectorType::get(
1081 retType, ElementCount::getFixed(XVecTy->getNumElements()));
1082 }
1083 assert((Arg0->getType()->hasFloatingRepresentation() ||
1084 Arg0->getType()->hasIntegerRepresentation()) &&
1085 "sign operand must have a float or int representation");
1086
1087 if (Arg0->getType()->hasUnsignedIntegerRepresentation()) {
1088 Value *Cmp = Builder.CreateICmpEQ(Op0, ConstantInt::get(Xty, 0));
1089 return Builder.CreateSelect(Cmp, ConstantInt::get(retType, 0),
1090 ConstantInt::get(retType, 1), "hlsl.sign");
1091 }
1092
1093 return Builder.CreateIntrinsic(
1094 retType, CGM.getHLSLRuntime().getSignIntrinsic(),
1095 ArrayRef<Value *>{Op0}, nullptr, "hlsl.sign");
1096 }
1097 case Builtin::BI__builtin_hlsl_elementwise_radians: {
1098 Value *Op0 = EmitScalarExpr(E->getArg(0));
1099 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
1100 "radians operand must have a float representation");
1101 return Builder.CreateIntrinsic(
1102 /*ReturnType=*/Op0->getType(),
1103 CGM.getHLSLRuntime().getRadiansIntrinsic(), ArrayRef<Value *>{Op0},
1104 nullptr, "hlsl.radians");
1105 }
1106 case Builtin::BI__builtin_hlsl_buffer_update_counter: {
1107 Value *ResHandle = EmitScalarExpr(E->getArg(0));
1108 Value *Offset = EmitScalarExpr(E->getArg(1));
1109 Value *OffsetI8 = Builder.CreateIntCast(Offset, Int8Ty, true);
1110 return Builder.CreateIntrinsic(
1111 /*ReturnType=*/Offset->getType(),
1112 CGM.getHLSLRuntime().getBufferUpdateCounterIntrinsic(),
1113 ArrayRef<Value *>{ResHandle, OffsetI8}, nullptr);
1114 }
1115 case Builtin::BI__builtin_hlsl_elementwise_splitdouble: {
1116
1117 assert((E->getArg(0)->getType()->hasFloatingRepresentation() &&
1120 "asuint operands types mismatch");
1121 return handleHlslSplitdouble(E, this);
1122 }
1123 case Builtin::BI__builtin_hlsl_elementwise_clip:
1124 assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
1125 "clip operands types mismatch");
1126 return handleHlslClip(E, this);
1127 case Builtin::BI__builtin_hlsl_group_memory_barrier_with_group_sync: {
1128 Intrinsic::ID ID =
1129 CGM.getHLSLRuntime().getGroupMemoryBarrierWithGroupSyncIntrinsic();
1130 return EmitRuntimeCall(
1131 Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID));
1132 }
1133 case Builtin::BI__builtin_hlsl_elementwise_ddx_coarse: {
1134 Value *Op0 = EmitScalarExpr(E->getArg(0));
1135 if (!E->getArg(0)->getType()->hasFloatingRepresentation())
1136 llvm_unreachable("ddx_coarse operand must have a float representation");
1137 Intrinsic::ID ID = CGM.getHLSLRuntime().getDdxCoarseIntrinsic();
1138 return Builder.CreateIntrinsic(/*ReturnType=*/Op0->getType(), ID,
1139 ArrayRef<Value *>{Op0}, nullptr,
1140 "hlsl.ddx.coarse");
1141 }
1142 case Builtin::BI__builtin_hlsl_elementwise_ddy_coarse: {
1143 Value *Op0 = EmitScalarExpr(E->getArg(0));
1144 if (!E->getArg(0)->getType()->hasFloatingRepresentation())
1145 llvm_unreachable("ddy_coarse operand must have a float representation");
1146 Intrinsic::ID ID = CGM.getHLSLRuntime().getDdyCoarseIntrinsic();
1147 return Builder.CreateIntrinsic(/*ReturnType=*/Op0->getType(), ID,
1148 ArrayRef<Value *>{Op0}, nullptr,
1149 "hlsl.ddy.coarse");
1150 }
1151 case Builtin::BI__builtin_hlsl_elementwise_ddx_fine: {
1152 Value *Op0 = EmitScalarExpr(E->getArg(0));
1153 if (!E->getArg(0)->getType()->hasFloatingRepresentation())
1154 llvm_unreachable("ddx_fine operand must have a float representation");
1155 Intrinsic::ID ID = CGM.getHLSLRuntime().getDdxFineIntrinsic();
1156 return Builder.CreateIntrinsic(/*ReturnType=*/Op0->getType(), ID,
1157 ArrayRef<Value *>{Op0}, nullptr,
1158 "hlsl.ddx.fine");
1159 }
1160 case Builtin::BI__builtin_hlsl_elementwise_ddy_fine: {
1161 Value *Op0 = EmitScalarExpr(E->getArg(0));
1162 if (!E->getArg(0)->getType()->hasFloatingRepresentation())
1163 llvm_unreachable("ddy_fine operand must have a float representation");
1164 Intrinsic::ID ID = CGM.getHLSLRuntime().getDdyFineIntrinsic();
1165 return Builder.CreateIntrinsic(/*ReturnType=*/Op0->getType(), ID,
1166 ArrayRef<Value *>{Op0}, nullptr,
1167 "hlsl.ddy.fine");
1168 }
1169 case Builtin::BI__builtin_get_spirv_spec_constant_bool:
1170 case Builtin::BI__builtin_get_spirv_spec_constant_short:
1171 case Builtin::BI__builtin_get_spirv_spec_constant_ushort:
1172 case Builtin::BI__builtin_get_spirv_spec_constant_int:
1173 case Builtin::BI__builtin_get_spirv_spec_constant_uint:
1174 case Builtin::BI__builtin_get_spirv_spec_constant_longlong:
1175 case Builtin::BI__builtin_get_spirv_spec_constant_ulonglong:
1176 case Builtin::BI__builtin_get_spirv_spec_constant_half:
1177 case Builtin::BI__builtin_get_spirv_spec_constant_float:
1178 case Builtin::BI__builtin_get_spirv_spec_constant_double: {
1179 llvm::Function *SpecConstantFn = getSpecConstantFunction(E->getType());
1180 llvm::Value *SpecId = EmitScalarExpr(E->getArg(0));
1181 llvm::Value *DefaultVal = EmitScalarExpr(E->getArg(1));
1182 llvm::Value *Args[] = {SpecId, DefaultVal};
1183 return Builder.CreateCall(SpecConstantFn, Args);
1184 }
1185 }
1186 return nullptr;
1187}
1188
1190 const clang::QualType &SpecConstantType) {
1191
1192 // Find or create the declaration for the function.
1193 llvm::Module *M = &CGM.getModule();
1194 std::string MangledName =
1195 getSpecConstantFunctionName(SpecConstantType, getContext());
1196 llvm::Function *SpecConstantFn = M->getFunction(MangledName);
1197
1198 if (!SpecConstantFn) {
1199 llvm::Type *IntType = ConvertType(getContext().IntTy);
1200 llvm::Type *RetTy = ConvertType(SpecConstantType);
1201 llvm::Type *ArgTypes[] = {IntType, RetTy};
1202 llvm::FunctionType *FnTy = llvm::FunctionType::get(RetTy, ArgTypes, false);
1203 SpecConstantFn = llvm::Function::Create(
1204 FnTy, llvm::GlobalValue::ExternalLinkage, MangledName, M);
1205 }
1206 return SpecConstantFn;
1207}
llvm::Value * EmitOverflowIntrinsic(CodeGenFunction &CGF, const Intrinsic::ID IntrinsicID, llvm::Value *X, llvm::Value *Y, llvm::Value *&Carry)
Emit a call to llvm.
static Intrinsic::ID getWaveActiveSumIntrinsic(llvm::Triple::ArchType Arch, CGHLSLRuntime &RT, QualType QT)
static Intrinsic::ID getWaveActiveMaxIntrinsic(llvm::Triple::ArchType Arch, CGHLSLRuntime &RT, QualType QT)
static Intrinsic::ID getDotProductIntrinsic(CGHLSLRuntime &RT, QualType QT)
static Intrinsic::ID getPrefixCountBitsIntrinsic(llvm::Triple::ArchType Arch)
static std::string getSpecConstantFunctionName(clang::QualType SpecConstantType, ASTContext &Context)
static Intrinsic::ID getWaveActiveMinIntrinsic(llvm::Triple::ArchType Arch, CGHLSLRuntime &RT, QualType QT)
static Value * handleHlslSplitdouble(const CallExpr *E, CodeGenFunction *CGF)
static Value * emitBufferStride(CodeGenFunction *CGF, const Expr *HandleExpr, LValue &Stride)
static Intrinsic::ID getFirstBitHighIntrinsic(CGHLSLRuntime &RT, QualType QT)
static Value * handleElementwiseF16ToF32(CodeGenFunction &CGF, const CallExpr *E)
static Value * handleAsDoubleBuiltin(CodeGenFunction &CGF, const CallExpr *E)
static Value * handleHlslWaveActiveBallot(CodeGenFunction &CGF, const CallExpr *E)
static Value * handleElementwiseF32ToF16(CodeGenFunction &CGF, const CallExpr *E)
static Value * handleHlslClip(const CallExpr *E, CodeGenFunction *CGF)
#define X(type, name)
Definition Value.h:97
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:220
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition Expr.h:2943
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3147
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition Expr.h:3134
Like RawAddress, an abstract representation of an aligned address, but the pointer contained in this ...
Definition Address.h:128
llvm::StoreInst * CreateStore(llvm::Value *Val, Address Addr, bool IsVolatile=false)
Definition CGBuilder.h:140
CallArgList - Type for representing both the value and type of arguments in a call.
Definition CGCall.h:274
CodeGenFunction - This class organizes the per-function state that is used while generating LLVM code...
llvm::Type * ConvertType(QualType T)
llvm::BasicBlock * createBasicBlock(const Twine &name="", llvm::Function *parent=nullptr, llvm::BasicBlock *before=nullptr)
createBasicBlock - Create an LLVM basic block.
const LangOptions & getLangOpts() const
const TargetInfo & getTarget() const
llvm::Function * getSpecConstantFunction(const clang::QualType &SpecConstantType)
LValue EmitHLSLOutArgExpr(const HLSLOutArgExpr *E, CallArgList &Args, QualType Ty)
Definition CGExpr.cpp:6170
void EmitWritebacks(const CallArgList &Args)
EmitWriteback - Emit callbacks for function.
Definition CGCall.cpp:4883
llvm::Value * getTypeSize(QualType Ty)
Returns calculated size of the specified type.
RValue EmitAnyExpr(const Expr *E, AggValueSlot aggSlot=AggValueSlot::ignored(), bool ignoreResult=false)
EmitAnyExpr - Emit code to compute the specified expression which can have any type.
Definition CGExpr.cpp:267
llvm::CallInst * EmitRuntimeCall(llvm::FunctionCallee callee, const Twine &name="")
llvm::Value * EmitScalarExpr(const Expr *E, bool IgnoreResultAssign=false)
EmitScalarExpr - Emit the computation of the specified expression of LLVM scalar type,...
Address ReturnValue
ReturnValue - The temporary alloca to hold the return value.
LValue EmitLValue(const Expr *E, KnownNonNull_t IsKnownNonNull=NotKnownNonNull)
EmitLValue - Emit code to compute a designator that specifies the location of the expression.
Definition CGExpr.cpp:1692
llvm::LLVMContext & getLLVMContext()
llvm::Value * EmitHLSLBuiltinExpr(unsigned BuiltinID, const CallExpr *E, ReturnValueSlot ReturnValue)
CGHLSLRuntime & getHLSLRuntime()
Return a reference to the configured HLSL runtime.
const TargetInfo & getTarget() const
const llvm::Triple & getTriple() const
llvm::Function * getIntrinsic(unsigned IID, ArrayRef< llvm::Type * > Tys={})
LValue - This represents an lvalue references.
Definition CGValue.h:183
Address getAddress() const
Definition CGValue.h:373
RValue - This trivial value class is used to represent the result of an expression that is evaluated.
Definition CGValue.h:42
bool isScalar() const
Definition CGValue.h:64
Address getAggregateAddress() const
getAggregateAddr() - Return the Value* of the address of the aggregate.
Definition CGValue.h:84
llvm::Value * getScalarVal() const
getScalarVal() - Return the Value* of this scalar value.
Definition CGValue.h:72
ReturnValueSlot - Contains the address where the return value of a function can be stored,...
Definition CGCall.h:379
The name of a declaration.
This represents one expression.
Definition Expr.h:112
QualType getType() const
Definition Expr.h:144
Represents a function declaration or definition.
Definition Decl.h:2000
static FunctionDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation NLoc, DeclarationName N, QualType T, TypeSourceInfo *TInfo, StorageClass SC, bool UsesFPIntrin=false, bool isInlineSpecified=false, bool hasWrittenPrototype=true, ConstexprSpecKind ConstexprKind=ConstexprSpecKind::Unspecified, const AssociatedConstraint &TrailingRequiresClause={})
Definition Decl.h:2189
Represents a parameter to a function.
Definition Decl.h:1790
static ParmVarDecl * Create(ASTContext &C, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, const IdentifierInfo *Id, QualType T, TypeSourceInfo *TInfo, StorageClass S, Expr *DefArg)
Definition Decl.cpp:2957
A (possibly-)qualified type.
Definition TypeBase.h:937
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8302
Encodes a location in the source.
bool areArgsDestroyedLeftToRightInCallee() const
Are arguments to a call destroyed left to right in the callee?
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
TargetCXXABI getCXXABI() const
Get the C++ ABI currently in use.
bool isSignedIntegerType() const
Return true if this is an integer type that is signed, according to C99 6.2.5p4 [char,...
Definition Type.cpp:2206
bool hasIntegerRepresentation() const
Determine whether this type has an integer representation of some sort, e.g., it is an integer type o...
Definition Type.cpp:2067
const T * castAs() const
Member-template castAs<specific type>.
Definition TypeBase.h:9188
bool hasUnsignedIntegerRepresentation() const
Determine whether this type has an unsigned integer representation of some sort, e....
Definition Type.cpp:2292
bool hasSignedIntegerRepresentation() const
Determine whether this type has an signed integer representation of some sort, e.g....
Definition Type.cpp:2244
bool hasFloatingRepresentation() const
Determine whether this type has a floating-point representation of some sort, e.g....
Definition Type.cpp:2313
bool isVectorType() const
Definition TypeBase.h:8678
bool isFloatingType() const
Definition Type.cpp:2305
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
Definition Type.cpp:2254
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9121
QualType getType() const
Definition Value.cpp:237
Represents a GCC generic vector type.
Definition TypeBase.h:4176
unsigned getNumElements() const
Definition TypeBase.h:4191
The JSON file list parser is used to communicate input to InstallAPI.
@ SC_Extern
Definition Specifiers.h:251
@ SC_None
Definition Specifiers.h:250
Expr * Cond
};
@ Result
The result type of a method or function.
Definition TypeBase.h:905
U cast(CodeGen::Address addr)
Definition Address.h:327
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
llvm::IntegerType * Int8Ty
i8, i16, i32, and i64
Extra information about a function prototype.
Definition TypeBase.h:5354