LLVM 24.0.0git
DXILIntrinsicExpansion.cpp
Go to the documentation of this file.
1//===- DXILIntrinsicExpansion.cpp - Prepare LLVM Module for DXIL encoding--===//
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/// \file This file contains DXIL intrinsic expansions for those that don't have
10// opcodes in DirectX Intermediate Language (DXIL).
11//===----------------------------------------------------------------------===//
12
14#include "DirectX.h"
15#include "llvm/ADT/APInt.h"
16#include "llvm/ADT/STLExtras.h"
18#include "llvm/CodeGen/Passes.h"
19#include "llvm/IR/Constants.h"
20#include "llvm/IR/IRBuilder.h"
21#include "llvm/IR/InstrTypes.h"
22#include "llvm/IR/Instruction.h"
24#include "llvm/IR/Intrinsics.h"
25#include "llvm/IR/IntrinsicsDirectX.h"
27#include "llvm/IR/Module.h"
28#include "llvm/IR/PassManager.h"
29#include "llvm/IR/Type.h"
30#include "llvm/Pass.h"
34
35#define DEBUG_TYPE "dxil-intrinsic-expansion"
36
37using namespace llvm;
38
40
41public:
42 bool runOnModule(Module &M) override;
44
45 static char ID; // Pass identification.
46};
47
48static bool resourceAccessNeeds64BitExpansion(Module *M, Type *OverloadTy,
49 bool IsRaw) {
50 if (IsRaw && M->getTargetTriple().getDXILVersion() > VersionTuple(1, 2))
51 return false;
52
53 Type *ScalarTy = OverloadTy->getScalarType();
54 return ScalarTy->isDoubleTy() || ScalarTy->isIntegerTy(64);
55}
56
58 Module *M = Orig->getModule();
59 if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
60 return nullptr;
61
62 Value *Val = Orig->getOperand(0);
63 Type *ValTy = Val->getType();
64 if (!ValTy->getScalarType()->isHalfTy())
65 return nullptr;
66
67 IRBuilder<> Builder(Orig);
68 Type *IType = Type::getInt16Ty(M->getContext());
69 Constant *PosInf =
70 ValTy->isVectorTy()
73 cast<FixedVectorType>(ValTy)->getNumElements()),
74 ConstantInt::get(IType, 0x7c00))
75 : ConstantInt::get(IType, 0x7c00);
76
77 Constant *NegInf =
78 ValTy->isVectorTy()
81 cast<FixedVectorType>(ValTy)->getNumElements()),
82 ConstantInt::get(IType, 0xfc00))
83 : ConstantInt::get(IType, 0xfc00);
84
85 Value *IVal = Builder.CreateBitCast(Val, PosInf->getType());
86 Value *B1 = Builder.CreateICmpEQ(IVal, PosInf);
87 Value *B2 = Builder.CreateICmpEQ(IVal, NegInf);
88 Value *B3 = Builder.CreateOr(B1, B2);
89 return B3;
90}
91
93 Module *M = Orig->getModule();
94 if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
95 return nullptr;
96
97 Value *Val = Orig->getOperand(0);
98 Type *ValTy = Val->getType();
99 if (!ValTy->getScalarType()->isHalfTy())
100 return nullptr;
101
102 IRBuilder<> Builder(Orig);
103 Type *IType = Type::getInt16Ty(M->getContext());
104
105 Constant *ExpBitMask =
106 ValTy->isVectorTy()
109 cast<FixedVectorType>(ValTy)->getNumElements()),
110 ConstantInt::get(IType, 0x7c00))
111 : ConstantInt::get(IType, 0x7c00);
112 Constant *SigBitMask =
113 ValTy->isVectorTy()
116 cast<FixedVectorType>(ValTy)->getNumElements()),
117 ConstantInt::get(IType, 0x3ff))
118 : ConstantInt::get(IType, 0x3ff);
119
120 Constant *Zero =
121 ValTy->isVectorTy()
124 cast<FixedVectorType>(ValTy)->getNumElements()),
125 ConstantInt::get(IType, 0))
126 : ConstantInt::get(IType, 0);
127
128 Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
129 Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
130 Value *B1 = Builder.CreateICmpEQ(Exp, ExpBitMask);
131
132 Value *Sig = Builder.CreateAnd(IVal, SigBitMask);
133 Value *B2 = Builder.CreateICmpNE(Sig, Zero);
134 Value *B3 = Builder.CreateAnd(B1, B2);
135 return B3;
136}
137
139 Module *M = Orig->getModule();
140 if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
141 return nullptr;
142
143 Value *Val = Orig->getOperand(0);
144 Type *ValTy = Val->getType();
145 if (!ValTy->getScalarType()->isHalfTy())
146 return nullptr;
147
148 IRBuilder<> Builder(Orig);
149 Type *IType = Type::getInt16Ty(M->getContext());
150
151 Constant *ExpBitMask =
152 ValTy->isVectorTy()
155 cast<FixedVectorType>(ValTy)->getNumElements()),
156 ConstantInt::get(IType, 0x7c00))
157 : ConstantInt::get(IType, 0x7c00);
158
159 Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
160 Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
161 Value *B1 = Builder.CreateICmpNE(Exp, ExpBitMask);
162 return B1;
163}
164
166 Module *M = Orig->getModule();
167 if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
168 return nullptr;
169
170 Value *Val = Orig->getOperand(0);
171 Type *ValTy = Val->getType();
172 if (!ValTy->getScalarType()->isHalfTy())
173 return nullptr;
174
175 IRBuilder<> Builder(Orig);
176 Type *IType = Type::getInt16Ty(M->getContext());
177
178 Constant *ExpBitMask =
179 ValTy->isVectorTy()
182 cast<FixedVectorType>(ValTy)->getNumElements()),
183 ConstantInt::get(IType, 0x7c00))
184 : ConstantInt::get(IType, 0x7c00);
185 Constant *Zero =
186 ValTy->isVectorTy()
189 cast<FixedVectorType>(ValTy)->getNumElements()),
190 ConstantInt::get(IType, 0))
191 : ConstantInt::get(IType, 0);
192
193 Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
194 Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
195 Value *NotAllZeroes = Builder.CreateICmpNE(Exp, Zero);
196 Value *NotAllOnes = Builder.CreateICmpNE(Exp, ExpBitMask);
197 Value *B1 = Builder.CreateAnd(NotAllZeroes, NotAllOnes);
198 return B1;
199}
200
202 switch (F.getIntrinsicID()) {
203 case Intrinsic::assume:
204 case Intrinsic::abs:
205 case Intrinsic::atan2:
206 case Intrinsic::fshl:
207 case Intrinsic::fshr:
208 case Intrinsic::exp:
209 case Intrinsic::is_fpclass:
210 case Intrinsic::log:
211 case Intrinsic::log10:
212 case Intrinsic::pow:
213 case Intrinsic::powi:
214 case Intrinsic::dx_all:
215 case Intrinsic::dx_any:
216 case Intrinsic::dx_cross:
217 case Intrinsic::dx_uclamp:
218 case Intrinsic::dx_sclamp:
219 case Intrinsic::dx_nclamp:
220 case Intrinsic::dx_degrees:
221 case Intrinsic::dx_isinf:
222 case Intrinsic::dx_isnan:
223 case Intrinsic::dx_lerp:
224 case Intrinsic::dx_normalize:
225 case Intrinsic::dx_fdot:
226 case Intrinsic::dx_sdot:
227 case Intrinsic::dx_udot:
228 case Intrinsic::dx_sign:
229 case Intrinsic::dx_step:
230 case Intrinsic::dx_radians:
231 case Intrinsic::usub_sat:
232 case Intrinsic::vector_reduce_add:
233 case Intrinsic::vector_reduce_fadd:
234 case Intrinsic::matrix_multiply:
235 case Intrinsic::matrix_transpose:
236 case Intrinsic::umul_with_overflow:
237 case Intrinsic::smul_with_overflow:
238 return true;
239 case Intrinsic::dx_resource_load_rawbuffer:
241 F.getParent(), F.getReturnType()->getStructElementType(0),
242 /*IsRaw*/ true);
243 case Intrinsic::dx_resource_load_typedbuffer:
245 F.getParent(), F.getReturnType()->getStructElementType(0),
246 /*IsRaw*/ false);
247 case Intrinsic::dx_resource_store_rawbuffer:
249 F.getParent(), F.getFunctionType()->getParamType(3), /*IsRaw*/ true);
250 case Intrinsic::dx_resource_store_typedbuffer:
252 F.getParent(), F.getFunctionType()->getParamType(2), /*IsRaw*/ false);
253 }
254 return false;
255}
256
258 Value *A = Orig->getArgOperand(0);
259 Value *B = Orig->getArgOperand(1);
260 Type *Ty = A->getType();
261
262 IRBuilder<> Builder(Orig);
263
264 Value *Cmp = Builder.CreateICmpULT(A, B, "usub.cmp");
265 Value *Sub = Builder.CreateSub(A, B, "usub.sub");
266 Value *Zero = ConstantInt::get(Ty, 0);
267 return Builder.CreateSelect(Cmp, Zero, Sub, "usub.sat");
268}
269
270// Compute the high N bits of the 2N-bit unsigned product of two N-bit values
271// using only N-bit arithmetic, so we don't introduce a wider integer type that
272// may be unsupported in DXIL.
274 Type *Ty, unsigned BW) {
275 assert(BW % 2 == 0 && "high-half split needs symmetric halves");
276 unsigned Half = BW / 2;
277 Value *HalfShift = ConstantInt::get(Ty, Half);
278 Value *LoMask = ConstantInt::get(Ty, APInt::getLowBitsSet(BW, Half));
279
280 Value *U0 = Builder.CreateAnd(A, LoMask);
281 Value *U1 = Builder.CreateLShr(A, HalfShift);
282 Value *V0 = Builder.CreateAnd(B, LoMask);
283 Value *V1 = Builder.CreateLShr(B, HalfShift);
284
285 Value *W0 = Builder.CreateMul(U0, V0);
286 Value *T = Builder.CreateAdd(Builder.CreateMul(U1, V0),
287 Builder.CreateLShr(W0, HalfShift));
288 Value *W1 = Builder.CreateAnd(T, LoMask);
289 Value *W2 = Builder.CreateLShr(T, HalfShift);
290 W1 = Builder.CreateAdd(Builder.CreateMul(U0, V1), W1);
291 return Builder.CreateAdd(Builder.CreateAdd(Builder.CreateMul(U1, V1), W2),
292 Builder.CreateLShr(W1, HalfShift));
293}
294
295// Expand a {u,s}mul.with.overflow intrinsic. The low half of the result is a
296// plain multiply; overflow is derived from the high half of the double-width
297// product.
299 IRBuilder<> Builder(Orig);
300 Value *A = Orig->getArgOperand(0);
301 Value *B = Orig->getArgOperand(1);
302 Type *Ty = A->getType();
303 unsigned BW = Ty->getScalarSizeInBits();
304
305 Value *Lo;
306 Value *Ov;
307
308 // A plain double-width multiply is simplest, but we avoid it once it would
309 // introduce a 64-bit (or wider) integer, which DXIL does not always support.
310 // For i32 we use the native DXIL IMul/UMul ops, which return the full product
311 // as two i32s; wider types fall back to a same-width high-half computation.
312 if (2 * BW <= 32) {
313 Lo = Builder.CreateMul(A, B);
314 Type *WideTy = Ty->getWithNewBitWidth(2 * BW);
315 Value *WideA =
316 Signed ? Builder.CreateSExt(A, WideTy) : Builder.CreateZExt(A, WideTy);
317 Value *WideB =
318 Signed ? Builder.CreateSExt(B, WideTy) : Builder.CreateZExt(B, WideTy);
319 Value *Wide = Builder.CreateMul(WideA, WideB);
320 if (Signed) {
321 // Overflow when the full product doesn't fit back into BW signed bits.
322 Ov = Builder.CreateICmpNE(Wide, Builder.CreateSExt(Lo, WideTy));
323 } else {
324 Value *Hi = Builder.CreateLShr(Wide, ConstantInt::get(WideTy, BW));
325 Ov = Builder.CreateICmpNE(Hi, ConstantInt::get(WideTy, 0));
326 }
327 } else if (BW == 32) {
328 // IMul/UMul return {high, low}; index 0 is the high 32 bits.
329 Type *ResTy = StructType::get(Ty, Ty);
330 Intrinsic::ID IntrinsicID =
331 Signed ? Intrinsic::dx_imul : Intrinsic::dx_umul;
332 Value *Mul = Builder.CreateIntrinsic(ResTy, IntrinsicID, {A, B});
333 Value *Hi = Builder.CreateExtractValue(Mul, 0);
334 Lo = Builder.CreateExtractValue(Mul, 1);
335 if (Signed)
336 Ov = Builder.CreateICmpNE(
337 Hi, Builder.CreateAShr(Lo, ConstantInt::get(Ty, BW - 1)));
338 else
339 Ov = Builder.CreateICmpNE(Hi, ConstantInt::get(Ty, 0));
340 } else {
341 Lo = Builder.CreateMul(A, B);
342 Value *Hi = createMulHighUnsigned(Builder, A, B, Ty, BW);
343 if (Signed) {
344 // Turn the unsigned high half into the signed one, then overflow means it
345 // isn't the sign extension of the low half.
346 Value *SignShift = ConstantInt::get(Ty, BW - 1);
347 Value *ASign = Builder.CreateAShr(A, SignShift);
348 Value *BSign = Builder.CreateAShr(B, SignShift);
349 Hi = Builder.CreateSub(Hi, Builder.CreateAnd(ASign, B));
350 Hi = Builder.CreateSub(Hi, Builder.CreateAnd(BSign, A));
351 Ov = Builder.CreateICmpNE(Hi, Builder.CreateAShr(Lo, SignShift));
352 } else {
353 Ov = Builder.CreateICmpNE(Hi, ConstantInt::get(Ty, 0));
354 }
355 }
356
357 Value *Agg = PoisonValue::get(Orig->getType());
358 Agg = Builder.CreateInsertValue(Agg, Lo, 0);
359 return Builder.CreateInsertValue(Agg, Ov, 1);
360}
361
362static Value *expandVecReduceAdd(CallInst *Orig, Intrinsic::ID IntrinsicId) {
363 assert(IntrinsicId == Intrinsic::vector_reduce_add ||
364 IntrinsicId == Intrinsic::vector_reduce_fadd);
365
366 IRBuilder<> Builder(Orig);
367 bool IsFAdd = (IntrinsicId == Intrinsic::vector_reduce_fadd);
368
369 Value *X = Orig->getOperand(IsFAdd ? 1 : 0);
370 Type *Ty = X->getType();
371 auto *XVec = dyn_cast<FixedVectorType>(Ty);
372 unsigned XVecSize = XVec->getNumElements();
373 Value *Sum = Builder.CreateExtractElement(X, static_cast<uint64_t>(0));
374
375 // Handle the initial start value for floating-point addition.
376 if (IsFAdd) {
377 Constant *StartValue = dyn_cast<Constant>(Orig->getOperand(0));
378 if (StartValue && !StartValue->isNullValue())
379 Sum = Builder.CreateFAdd(Sum, StartValue);
380 }
381
382 // Accumulate the remaining vector elements.
383 for (unsigned I = 1; I < XVecSize; I++) {
384 Value *Elt = Builder.CreateExtractElement(X, I);
385 if (IsFAdd)
386 Sum = Builder.CreateFAdd(Sum, Elt);
387 else
388 Sum = Builder.CreateAdd(Sum, Elt);
389 }
390
391 return Sum;
392}
393
394static Value *expandAbs(CallInst *Orig) {
395 Value *X = Orig->getOperand(0);
396 IRBuilder<> Builder(Orig);
397 Type *Ty = X->getType();
398 Type *EltTy = Ty->getScalarType();
399 Constant *Zero = Ty->isVectorTy()
402 cast<FixedVectorType>(Ty)->getNumElements()),
403 ConstantInt::get(EltTy, 0))
404 : ConstantInt::get(EltTy, 0);
405 auto *V = Builder.CreateSub(Zero, X);
406 return Builder.CreateIntrinsic(Ty, Intrinsic::smax, {X, V}, nullptr,
407 "dx.max");
408}
409
411
412 VectorType *VT = cast<VectorType>(Orig->getType());
413 if (cast<FixedVectorType>(VT)->getNumElements() != 3)
414 reportFatalUsageError("return vector must have exactly 3 elements");
415
416 Value *op0 = Orig->getOperand(0);
417 Value *op1 = Orig->getOperand(1);
418 IRBuilder<> Builder(Orig);
419
420 Value *op0_x = Builder.CreateExtractElement(op0, (uint64_t)0, "x0");
421 Value *op0_y = Builder.CreateExtractElement(op0, 1, "x1");
422 Value *op0_z = Builder.CreateExtractElement(op0, 2, "x2");
423
424 Value *op1_x = Builder.CreateExtractElement(op1, (uint64_t)0, "y0");
425 Value *op1_y = Builder.CreateExtractElement(op1, 1, "y1");
426 Value *op1_z = Builder.CreateExtractElement(op1, 2, "y2");
427
428 auto MulSub = [&](Value *x0, Value *y0, Value *x1, Value *y1) -> Value * {
429 Value *xy = Builder.CreateFMul(x0, y1);
430 Value *yx = Builder.CreateFMul(y0, x1);
431 return Builder.CreateFSub(xy, yx, Orig->getName());
432 };
433
434 Value *yz_zy = MulSub(op0_y, op0_z, op1_y, op1_z);
435 Value *zx_xz = MulSub(op0_z, op0_x, op1_z, op1_x);
436 Value *xy_yx = MulSub(op0_x, op0_y, op1_x, op1_y);
437
438 Value *cross = PoisonValue::get(VT);
439 cross = Builder.CreateInsertElement(cross, yz_zy, (uint64_t)0);
440 cross = Builder.CreateInsertElement(cross, zx_xz, 1);
441 cross = Builder.CreateInsertElement(cross, xy_yx, 2);
442 return cross;
443}
444
445// Create appropriate DXIL float dot intrinsic for the given A and B operands
446// The appropriate opcode will be determined by the size of the operands
447// The dot product is placed in the position indicated by Orig
449 Type *ATy = A->getType();
450 [[maybe_unused]] Type *BTy = B->getType();
451 assert(ATy->isVectorTy() && BTy->isVectorTy());
452
453 IRBuilder<> Builder(Orig);
454
455 auto *AVec = dyn_cast<FixedVectorType>(ATy);
456
458
459 Intrinsic::ID DotIntrinsic = Intrinsic::dx_dot4;
460 int NumElts = AVec->getNumElements();
461 switch (NumElts) {
462 case 2:
463 DotIntrinsic = Intrinsic::dx_dot2;
464 break;
465 case 3:
466 DotIntrinsic = Intrinsic::dx_dot3;
467 break;
468 case 4:
469 DotIntrinsic = Intrinsic::dx_dot4;
470 break;
471 default:
473 "Invalid dot product input vector: length is outside 2-4");
474 return nullptr;
475 }
476
478 for (int I = 0; I < NumElts; ++I)
479 Args.push_back(Builder.CreateExtractElement(A, Builder.getInt32(I)));
480 for (int I = 0; I < NumElts; ++I)
481 Args.push_back(Builder.CreateExtractElement(B, Builder.getInt32(I)));
482 return Builder.CreateIntrinsic(ATy->getScalarType(), DotIntrinsic, Args,
483 nullptr, "dot");
484}
485
486// Create the appropriate DXIL float dot intrinsic for the operands of Orig
487// The appropriate opcode will be determined by the size of the operands
488// The dot product is placed in the position indicated by Orig
490 return expandFloatDotIntrinsic(Orig, Orig->getOperand(0),
491 Orig->getOperand(1));
492}
493
494// Expand integer dot product to multiply and add ops
496 Intrinsic::ID DotIntrinsic) {
497 assert(DotIntrinsic == Intrinsic::dx_sdot ||
498 DotIntrinsic == Intrinsic::dx_udot);
499 Value *A = Orig->getOperand(0);
500 Value *B = Orig->getOperand(1);
501 Type *ATy = A->getType();
502 [[maybe_unused]] Type *BTy = B->getType();
503 assert(ATy->isVectorTy() && BTy->isVectorTy());
504
505 IRBuilder<> Builder(Orig);
506
507 auto *AVec = dyn_cast<FixedVectorType>(ATy);
508
510
511 Value *Result;
512 Intrinsic::ID MadIntrinsic = DotIntrinsic == Intrinsic::dx_sdot
513 ? Intrinsic::dx_imad
514 : Intrinsic::dx_umad;
515 Value *Elt0 = Builder.CreateExtractElement(A, (uint64_t)0);
516 Value *Elt1 = Builder.CreateExtractElement(B, (uint64_t)0);
517 Result = Builder.CreateMul(Elt0, Elt1);
518 for (unsigned I = 1; I < AVec->getNumElements(); I++) {
519 Elt0 = Builder.CreateExtractElement(A, I);
520 Elt1 = Builder.CreateExtractElement(B, I);
521 Result = Builder.CreateIntrinsic(Result->getType(), MadIntrinsic,
522 ArrayRef<Value *>{Elt0, Elt1, Result},
523 nullptr, "dx.mad");
524 }
525 return Result;
526}
527
529 Value *X = Orig->getOperand(0);
530 IRBuilder<> Builder(Orig);
531 Type *Ty = X->getType();
532 Type *EltTy = Ty->getScalarType();
533 Constant *Log2eConst =
534 Ty->isVectorTy() ? ConstantVector::getSplat(
536 cast<FixedVectorType>(Ty)->getNumElements()),
537 ConstantFP::get(EltTy, numbers::log2ef))
538 : ConstantFP::get(EltTy, numbers::log2ef);
539 Value *NewX = Builder.CreateFMul(Log2eConst, X);
540 CallInst *Exp2Call = Builder.CreateIntrinsicWithoutFolding(
541 Ty, Intrinsic::exp2, {NewX}, nullptr, "dx.exp2");
542 Exp2Call->setTailCall(Orig->isTailCall());
543 Exp2Call->setAttributes(Orig->getAttributes());
544 return Exp2Call;
545}
546
548 Value *T = Orig->getArgOperand(1);
549 auto *TCI = dyn_cast<ConstantInt>(T);
550
551 // These FPClassTest cases have DXIL opcodes, so they will be handled in
552 // DXIL Op Lowering instead for all non f16 cases.
553 switch (TCI->getZExtValue()) {
555 return expand16BitIsInf(Orig);
557 return expand16BitIsNaN(Orig);
559 return expand16BitIsNormal(Orig);
561 return expand16BitIsFinite(Orig);
562 }
563
564 IRBuilder<> Builder(Orig);
565
566 Value *F = Orig->getArgOperand(0);
567 Type *FTy = F->getType();
568 unsigned FNumElem = 0; // 0 => F is not a vector
569
570 unsigned BitWidth; // Bit width of F or the ElemTy of F
571 Type *BitCastTy; // An IntNTy of the same bitwidth as F or ElemTy of F
572
573 if (auto *FVecTy = dyn_cast<FixedVectorType>(FTy)) {
574 Type *ElemTy = FVecTy->getElementType();
575 FNumElem = FVecTy->getNumElements();
576 BitWidth = ElemTy->getPrimitiveSizeInBits();
577 BitCastTy = FixedVectorType::get(Builder.getIntNTy(BitWidth), FNumElem);
578 } else {
580 BitCastTy = Builder.getIntNTy(BitWidth);
581 }
582
583 Value *FBitCast = Builder.CreateBitCast(F, BitCastTy);
584 switch (TCI->getZExtValue()) {
586 Value *NegZero =
587 ConstantInt::get(Builder.getIntNTy(BitWidth), 1 << (BitWidth - 1),
588 /*IsSigned=*/true);
589 Value *RetVal;
590 if (FNumElem) {
591 Value *NegZeroSplat = Builder.CreateVectorSplat(FNumElem, NegZero);
592 RetVal =
593 Builder.CreateICmpEQ(FBitCast, NegZeroSplat, "is.fpclass.negzero");
594 } else
595 RetVal = Builder.CreateICmpEQ(FBitCast, NegZero, "is.fpclass.negzero");
596 return RetVal;
597 }
598 default:
599 reportFatalUsageError("Unsupported FPClassTest");
600 }
601}
602
604 Intrinsic::ID IntrinsicId) {
605 Value *X = Orig->getOperand(0);
606 IRBuilder<> Builder(Orig);
607 Type *Ty = X->getType();
608 Type *EltTy = Ty->getScalarType();
609
610 auto ApplyOp = [&Builder](Intrinsic::ID IntrinsicId, Value *Result,
611 Value *Elt) {
612 if (IntrinsicId == Intrinsic::dx_any)
613 return Builder.CreateOr(Result, Elt);
614 assert(IntrinsicId == Intrinsic::dx_all);
615 return Builder.CreateAnd(Result, Elt);
616 };
617
618 Value *Result = nullptr;
619 if (!Ty->isVectorTy()) {
620 Result = EltTy->isFloatingPointTy()
621 ? Builder.CreateFCmpUNE(X, ConstantFP::get(EltTy, 0))
622 : Builder.CreateICmpNE(X, ConstantInt::get(EltTy, 0));
623 } else {
624 auto *XVec = dyn_cast<FixedVectorType>(Ty);
625 Value *Cond =
626 EltTy->isFloatingPointTy()
627 ? Builder.CreateFCmpUNE(
629 ElementCount::getFixed(XVec->getNumElements()),
630 ConstantFP::get(EltTy, 0)))
631 : Builder.CreateICmpNE(
633 ElementCount::getFixed(XVec->getNumElements()),
634 ConstantInt::get(EltTy, 0)));
635 Result = Builder.CreateExtractElement(Cond, (uint64_t)0);
636 for (unsigned I = 1; I < XVec->getNumElements(); I++) {
637 Value *Elt = Builder.CreateExtractElement(Cond, I);
638 Result = ApplyOp(IntrinsicId, Result, Elt);
639 }
640 }
641 return Result;
642}
643
645 Value *X = Orig->getOperand(0);
646 Value *Y = Orig->getOperand(1);
647 Value *S = Orig->getOperand(2);
648 IRBuilder<> Builder(Orig);
649 auto *V = Builder.CreateFSub(Y, X);
650 V = Builder.CreateFMul(S, V);
651 return Builder.CreateFAdd(X, V, "dx.lerp");
652}
653
655 float LogConstVal = numbers::ln2f) {
656 Value *X = Orig->getOperand(0);
657 IRBuilder<> Builder(Orig);
658 Type *Ty = X->getType();
659 Type *EltTy = Ty->getScalarType();
660 Constant *Ln2Const =
661 Ty->isVectorTy() ? ConstantVector::getSplat(
663 cast<FixedVectorType>(Ty)->getNumElements()),
664 ConstantFP::get(EltTy, LogConstVal))
665 : ConstantFP::get(EltTy, LogConstVal);
666 CallInst *Log2Call = Builder.CreateIntrinsicWithoutFolding(
667 Ty, Intrinsic::log2, {X}, nullptr, "elt.log2");
668 Log2Call->setTailCall(Orig->isTailCall());
669 Log2Call->setAttributes(Orig->getAttributes());
670 return Builder.CreateFMul(Ln2Const, Log2Call);
671}
675
676// Use dot product of vector operand with itself to calculate the length.
677// Divide the vector by that length to normalize it.
679 Value *X = Orig->getOperand(0);
680 Type *Ty = Orig->getType();
681 Type *EltTy = Ty->getScalarType();
682 IRBuilder<> Builder(Orig);
683
684 auto *XVec = dyn_cast<FixedVectorType>(Ty);
685 if (!XVec) {
686 if (auto *constantFP = dyn_cast<ConstantFP>(X)) {
687 const APFloat &fpVal = constantFP->getValueAPF();
688 if (fpVal.isZero())
689 reportFatalUsageError("Invalid input scalar: length is zero");
690 }
691 return Builder.CreateFDiv(X, X);
692 }
693
694 Value *DotProduct = expandFloatDotIntrinsic(Orig, X, X);
695
696 // verify that the length is non-zero
697 // (if the dot product is non-zero, then the length is non-zero)
698 if (auto *constantFP = dyn_cast<ConstantFP>(DotProduct)) {
699 const APFloat &fpVal = constantFP->getValueAPF();
700 if (fpVal.isZero())
701 reportFatalUsageError("Invalid input vector: length is zero");
702 }
703
704 Value *Multiplicand = Builder.CreateIntrinsic(EltTy, Intrinsic::dx_rsqrt,
705 ArrayRef<Value *>{DotProduct},
706 nullptr, "dx.rsqrt");
707
708 Value *MultiplicandVec =
709 Builder.CreateVectorSplat(XVec->getNumElements(), Multiplicand);
710 return Builder.CreateFMul(X, MultiplicandVec);
711}
712
714 Value *Y = Orig->getOperand(0);
715 Value *X = Orig->getOperand(1);
716 Type *Ty = X->getType();
717 IRBuilder<> Builder(Orig);
718 Builder.setFastMathFlags(Orig->getFastMathFlags());
719
720 Value *Tan = Builder.CreateFDiv(Y, X);
721
722 CallInst *Atan = Builder.CreateIntrinsicWithoutFolding(
723 Ty, Intrinsic::atan, {Tan}, nullptr, "Elt.Atan");
724 Atan->setTailCall(Orig->isTailCall());
725 Atan->setAttributes(Orig->getAttributes());
726
727 // Modify atan result based on https://en.wikipedia.org/wiki/Atan2.
728 Constant *Pi = ConstantFP::get(Ty, llvm::numbers::pi);
729 Constant *HalfPi = ConstantFP::get(Ty, llvm::numbers::pi / 2);
730 Constant *NegHalfPi = ConstantFP::get(Ty, -llvm::numbers::pi / 2);
731 Constant *Zero = ConstantFP::get(Ty, 0);
732 Value *AtanAddPi = Builder.CreateFAdd(Atan, Pi);
733 Value *AtanSubPi = Builder.CreateFSub(Atan, Pi);
734
735 // x > 0 -> atan.
736 Value *Result = Atan;
737 Value *XLt0 = Builder.CreateFCmpOLT(X, Zero);
738 Value *XEq0 = Builder.CreateFCmpOEQ(X, Zero);
739 Value *YGe0 = Builder.CreateFCmpOGE(Y, Zero);
740 Value *YLt0 = Builder.CreateFCmpOLT(Y, Zero);
741
742 // x < 0, y >= 0 -> atan + pi.
743 Value *XLt0AndYGe0 = Builder.CreateAnd(XLt0, YGe0);
744 Result = Builder.CreateSelect(XLt0AndYGe0, AtanAddPi, Result);
745
746 // x < 0, y < 0 -> atan - pi.
747 Value *XLt0AndYLt0 = Builder.CreateAnd(XLt0, YLt0);
748 Result = Builder.CreateSelect(XLt0AndYLt0, AtanSubPi, Result);
749
750 // x == 0, y < 0 -> -pi/2
751 Value *XEq0AndYLt0 = Builder.CreateAnd(XEq0, YLt0);
752 Result = Builder.CreateSelect(XEq0AndYLt0, NegHalfPi, Result);
753
754 // x == 0, y > 0 -> pi/2
755 Value *XEq0AndYGe0 = Builder.CreateAnd(XEq0, YGe0);
756 Result = Builder.CreateSelect(XEq0AndYGe0, HalfPi, Result);
757
758 return Result;
759}
760
761template <bool LeftFunnel>
763 Type *Ty = Orig->getType();
764 Value *A = Orig->getOperand(0);
765 Value *B = Orig->getOperand(1);
766 Value *Shift = Orig->getOperand(2);
767
768 IRBuilder<> Builder(Orig);
769
770 unsigned BitWidth = Ty->getScalarSizeInBits();
772 "Can't use Mask to compute modulo and inverse");
773
774 // Note: if (Shift % BitWidth) == 0 then (BitWidth - Shift) == BitWidth,
775 // shifting by the bitwidth for shl/lshr returns a poisoned result. As such,
776 // we implement the same formula as LegalizerHelper::lowerFunnelShiftAsShifts.
777 //
778 // The funnel shift is expanded like so:
779 // fshl
780 // -> msb_extract((concat(A, B) << (Shift % BitWidth)), BitWidth)
781 // -> A << (Shift % BitWidth) | B >> 1 >> (BitWidth - 1 - (Shift % BitWidth))
782 // fshr
783 // -> lsb_extract((concat(A, B) >> (Shift % BitWidth), BitWidth))
784 // -> A << 1 << (BitWidth - 1 - (Shift % BitWidth)) | B >> (Shift % BitWidth)
785
786 // (BitWidth - 1) -> Mask
787 Constant *Mask = ConstantInt::get(Ty, Ty->getScalarSizeInBits() - 1);
788
789 // Shift % BitWidth
790 // -> Shift & (BitWidth - 1)
791 // -> Shift & Mask
792 Value *MaskedShift = Builder.CreateAnd(Shift, Mask);
793
794 // (BitWidth - 1) - (Shift % BitWidth)
795 // -> ~Shift & (BitWidth - 1)
796 // -> ~Shift & Mask
797 Value *NotShift = Builder.CreateNot(Shift);
798 Value *InverseShift = Builder.CreateAnd(NotShift, Mask);
799
800 Constant *One = ConstantInt::get(Ty, 1);
801 Value *ShiftedA;
802 Value *ShiftedB;
803
804 if (LeftFunnel) {
805 ShiftedA = Builder.CreateShl(A, MaskedShift);
806 Value *ShiftB1 = Builder.CreateLShr(B, One);
807 ShiftedB = Builder.CreateLShr(ShiftB1, InverseShift);
808 } else {
809 Value *ShiftA1 = Builder.CreateShl(A, One);
810 ShiftedA = Builder.CreateShl(ShiftA1, InverseShift);
811 ShiftedB = Builder.CreateLShr(B, MaskedShift);
812 }
813
814 Value *Result = Builder.CreateOr(ShiftedA, ShiftedB);
815 return Result;
816}
817
818static Value *expandPowIntrinsic(CallInst *Orig, Intrinsic::ID IntrinsicId) {
819
820 Value *X = Orig->getOperand(0);
821 Value *Y = Orig->getOperand(1);
822 Type *Ty = X->getType();
823 IRBuilder<> Builder(Orig);
824
825 if (IntrinsicId == Intrinsic::powi)
826 Y = Builder.CreateSIToFP(Y, Ty);
827
828 Value *Log2Call =
829 Builder.CreateIntrinsic(Ty, Intrinsic::log2, {X}, nullptr, "elt.log2");
830 auto *Mul = Builder.CreateFMul(Log2Call, Y);
831 CallInst *Exp2Call = Builder.CreateIntrinsicWithoutFolding(
832 Ty, Intrinsic::exp2, {Mul}, nullptr, "elt.exp2");
833 Exp2Call->setTailCall(Orig->isTailCall());
834 Exp2Call->setAttributes(Orig->getAttributes());
835 return Exp2Call;
836}
837
839
840 Value *X = Orig->getOperand(0);
841 Value *Y = Orig->getOperand(1);
842 Type *Ty = X->getType();
843 IRBuilder<> Builder(Orig);
844
845 Constant *One = ConstantFP::get(Ty->getScalarType(), 1.0);
846 Constant *Zero = ConstantFP::get(Ty->getScalarType(), 0.0);
847 Value *Cond = Builder.CreateFCmpOLT(Y, X);
848
849 if (Ty != Ty->getScalarType()) {
850 auto *XVec = dyn_cast<FixedVectorType>(Ty);
852 ElementCount::getFixed(XVec->getNumElements()), One);
854 ElementCount::getFixed(XVec->getNumElements()), Zero);
855 }
856
857 return Builder.CreateSelect(Cond, Zero, One);
858}
859
861 Value *X = Orig->getOperand(0);
862 Type *Ty = X->getType();
863 IRBuilder<> Builder(Orig);
864 Value *PiOver180 = ConstantFP::get(Ty, llvm::numbers::pi / 180.0);
865 return Builder.CreateFMul(X, PiOver180);
866}
867
868static bool expandBufferLoadIntrinsic(CallInst *Orig, bool IsRaw) {
869 IRBuilder<> Builder(Orig);
870
871 Type *BufferTy = Orig->getType()->getStructElementType(0);
872 Type *ScalarTy = BufferTy->getScalarType();
873 bool IsDouble = ScalarTy->isDoubleTy();
874 assert(IsDouble || ScalarTy->isIntegerTy(64) &&
875 "Only expand double or int64 scalars or vectors");
876 bool IsVector = false;
877 unsigned ExtractNum = 2;
878 if (auto *VT = dyn_cast<FixedVectorType>(BufferTy)) {
879 ExtractNum = 2 * VT->getNumElements();
880 IsVector = true;
881 assert(IsRaw || ExtractNum == 4 && "TypedBufferLoad vector must be size 2");
882 }
883
885 Value *Result = PoisonValue::get(BufferTy);
886 unsigned Base = 0;
887 // If we need to extract more than 4 i32; we need to break it up into
888 // more than one load. LoadNum tells us how many i32s we are loading in
889 // each load
890 while (ExtractNum > 0) {
891 unsigned LoadNum = std::min(ExtractNum, 4u);
892 Type *Ty = VectorType::get(Builder.getInt32Ty(), LoadNum, false);
893
894 Type *LoadType = StructType::get(Ty, Builder.getInt1Ty());
895 Intrinsic::ID LoadIntrinsic = Intrinsic::dx_resource_load_typedbuffer;
896 SmallVector<Value *, 3> Args = {Orig->getOperand(0), Orig->getOperand(1)};
897 if (IsRaw) {
898 LoadIntrinsic = Intrinsic::dx_resource_load_rawbuffer;
899 Value *Tmp = Builder.getInt32(4 * Base * 2);
900 Args.push_back(Builder.CreateAdd(Orig->getOperand(2), Tmp));
901 }
902
903 Value *Load = Builder.CreateIntrinsic(LoadType, LoadIntrinsic, Args);
904 Loads.push_back(Load);
905
906 // extract the buffer load's result
907 Value *Extract = Builder.CreateExtractValue(Load, {0});
908
909 SmallVector<Value *> ExtractElements;
910 for (unsigned I = 0; I < LoadNum; ++I)
911 ExtractElements.push_back(
912 Builder.CreateExtractElement(Extract, Builder.getInt32(I)));
913
914 // combine into double(s) or int64(s)
915 for (unsigned I = 0; I < LoadNum; I += 2) {
916 Value *Combined = nullptr;
917 if (IsDouble)
918 // For doubles, use dx_asdouble intrinsic
919 Combined = Builder.CreateIntrinsic(
920 Builder.getDoubleTy(), Intrinsic::dx_asdouble,
921 {ExtractElements[I], ExtractElements[I + 1]});
922 else {
923 // For int64, manually combine two int32s
924 // First, zero-extend both values to i64
925 Value *Lo =
926 Builder.CreateZExt(ExtractElements[I], Builder.getInt64Ty());
927 Value *Hi =
928 Builder.CreateZExt(ExtractElements[I + 1], Builder.getInt64Ty());
929 // Shift the high bits left by 32 bits
930 Value *ShiftedHi = Builder.CreateShl(Hi, Builder.getInt64(32));
931 // OR the high and low bits together
932 Combined = Builder.CreateOr(Lo, ShiftedHi);
933 }
934
935 if (IsVector)
936 Result = Builder.CreateInsertElement(Result, Combined,
937 Builder.getInt32((I / 2) + Base));
938 else
939 Result = Combined;
940 }
941
942 ExtractNum -= LoadNum;
943 Base += LoadNum / 2;
944 }
945
946 Value *CheckBit = nullptr;
947 for (User *U : make_early_inc_range(Orig->users())) {
948 // If it's not a ExtractValueInst, we don't know how to
949 // handle it
950 auto *EVI = dyn_cast<ExtractValueInst>(U);
951 if (!EVI)
952 llvm_unreachable("Unexpected user of typedbufferload");
953
954 ArrayRef<unsigned> Indices = EVI->getIndices();
955 assert(Indices.size() == 1);
956
957 if (Indices[0] == 0) {
958 // Use of the value(s)
959 EVI->replaceAllUsesWith(Result);
960 } else {
961 // Use of the check bit
962 assert(Indices[0] == 1 && "Unexpected type for typedbufferload");
963 // Note: This does not always match the historical behaviour of DXC.
964 // See https://github.com/microsoft/DirectXShaderCompiler/issues/7622
965 if (!CheckBit) {
966 SmallVector<Value *, 2> CheckBits;
967 for (Value *L : Loads)
968 CheckBits.push_back(Builder.CreateExtractValue(L, {1}));
969 CheckBit = Builder.CreateAnd(CheckBits);
970 }
971 EVI->replaceAllUsesWith(CheckBit);
972 }
973 EVI->eraseFromParent();
974 }
975 Orig->eraseFromParent();
976 return true;
977}
978
979static bool expandBufferStoreIntrinsic(CallInst *Orig, bool IsRaw) {
980 IRBuilder<> Builder(Orig);
981
982 unsigned ValIndex = IsRaw ? 3 : 2;
983 Type *BufferTy = Orig->getFunctionType()->getParamType(ValIndex);
984 Type *ScalarTy = BufferTy->getScalarType();
985 bool IsDouble = ScalarTy->isDoubleTy();
986 assert((IsDouble || ScalarTy->isIntegerTy(64)) &&
987 "Only expand double or int64 scalars or vectors");
988
989 // Determine if we're dealing with a vector or scalar
990 bool IsVector = false;
991 unsigned ExtractNum = 2;
992 unsigned VecLen = 0;
993 if (auto *VT = dyn_cast<FixedVectorType>(BufferTy)) {
994 VecLen = VT->getNumElements();
995 assert(IsRaw || VecLen == 2 && "TypedBufferStore vector must be size 2");
996 ExtractNum = VecLen * 2;
997 IsVector = true;
998 }
999
1000 // Create the appropriate vector type for the result
1001 Type *Int32Ty = Builder.getInt32Ty();
1002 Type *ResultTy = VectorType::get(Int32Ty, ExtractNum, false);
1003 Value *Val = PoisonValue::get(ResultTy);
1004
1005 Type *SplitElementTy = Int32Ty;
1006 if (IsVector)
1007 SplitElementTy = VectorType::get(SplitElementTy, VecLen, false);
1008
1009 Value *LowBits = nullptr;
1010 Value *HighBits = nullptr;
1011 // Split the 64-bit values into 32-bit components
1012 if (IsDouble) {
1013 auto *SplitTy = llvm::StructType::get(SplitElementTy, SplitElementTy);
1014 Value *Split = Builder.CreateIntrinsic(SplitTy, Intrinsic::dx_splitdouble,
1015 {Orig->getOperand(ValIndex)});
1016 LowBits = Builder.CreateExtractValue(Split, 0);
1017 HighBits = Builder.CreateExtractValue(Split, 1);
1018 } else {
1019 // Handle int64 type(s)
1020 Value *InputVal = Orig->getOperand(ValIndex);
1021 Constant *ShiftAmt = Builder.getInt64(32);
1022 if (IsVector)
1023 ShiftAmt =
1025
1026 // Split into low and high 32-bit parts
1027 LowBits = Builder.CreateTrunc(InputVal, SplitElementTy);
1028 Value *ShiftedVal = Builder.CreateLShr(InputVal, ShiftAmt);
1029 HighBits = Builder.CreateTrunc(ShiftedVal, SplitElementTy);
1030 }
1031
1032 if (IsVector) {
1034 for (unsigned I = 0; I < VecLen; ++I) {
1035 Mask.push_back(I);
1036 Mask.push_back(I + VecLen);
1037 }
1038 Val = Builder.CreateShuffleVector(LowBits, HighBits, Mask);
1039 } else {
1040 Val = Builder.CreateInsertElement(Val, LowBits, Builder.getInt32(0));
1041 Val = Builder.CreateInsertElement(Val, HighBits, Builder.getInt32(1));
1042 }
1043
1044 // If we need to extract more than 4 i32; we need to break it up into
1045 // more than one store. StoreNum tells us how many i32s we are storing in
1046 // each store
1047 unsigned Base = 0;
1048 while (ExtractNum > 0) {
1049 unsigned StoreNum = std::min(ExtractNum, 4u);
1050
1051 Intrinsic::ID StoreIntrinsic = Intrinsic::dx_resource_store_typedbuffer;
1052 SmallVector<Value *, 4> Args = {Orig->getOperand(0), Orig->getOperand(1)};
1053 if (IsRaw) {
1054 StoreIntrinsic = Intrinsic::dx_resource_store_rawbuffer;
1055 Value *Tmp = Builder.getInt32(4 * Base);
1056 Args.push_back(Builder.CreateAdd(Orig->getOperand(2), Tmp));
1057 }
1058
1060 for (unsigned I = 0; I < StoreNum; ++I) {
1061 Mask.push_back(Base + I);
1062 }
1063
1064 Value *SubVal = Val;
1065 if (VecLen > 2)
1066 SubVal = Builder.CreateShuffleVector(Val, Mask);
1067
1068 Args.push_back(SubVal);
1069 // Create the final intrinsic call
1070 Builder.CreateIntrinsic(Builder.getVoidTy(), StoreIntrinsic, Args);
1071
1072 ExtractNum -= StoreNum;
1073 Base += StoreNum;
1074 }
1075 Orig->eraseFromParent();
1076 return true;
1077}
1078
1080 if (ClampIntrinsic == Intrinsic::dx_uclamp)
1081 return Intrinsic::umax;
1082 if (ClampIntrinsic == Intrinsic::dx_sclamp)
1083 return Intrinsic::smax;
1084 assert(ClampIntrinsic == Intrinsic::dx_nclamp);
1085 return Intrinsic::maxnum;
1086}
1087
1089 if (ClampIntrinsic == Intrinsic::dx_uclamp)
1090 return Intrinsic::umin;
1091 if (ClampIntrinsic == Intrinsic::dx_sclamp)
1092 return Intrinsic::smin;
1093 assert(ClampIntrinsic == Intrinsic::dx_nclamp);
1094 return Intrinsic::minnum;
1095}
1096
1098 Intrinsic::ID ClampIntrinsic) {
1099 Value *X = Orig->getOperand(0);
1100 Value *Min = Orig->getOperand(1);
1101 Value *Max = Orig->getOperand(2);
1102 Type *Ty = X->getType();
1103 IRBuilder<> Builder(Orig);
1104 auto *MaxCall = Builder.CreateIntrinsic(Ty, getMaxForClamp(ClampIntrinsic),
1105 {X, Min}, nullptr, "dx.max");
1106 return Builder.CreateIntrinsic(Ty, getMinForClamp(ClampIntrinsic),
1107 {MaxCall, Max}, nullptr, "dx.min");
1108}
1109
1111 Value *X = Orig->getOperand(0);
1112 Type *Ty = X->getType();
1113 IRBuilder<> Builder(Orig);
1114 Value *DegreesRatio = ConstantFP::get(Ty, 180.0 * llvm::numbers::inv_pi);
1115 return Builder.CreateFMul(X, DegreesRatio);
1116}
1117
1119 Value *X = Orig->getOperand(0);
1120 Type *Ty = X->getType();
1121 Type *ScalarTy = Ty->getScalarType();
1122 Type *RetTy = Orig->getType();
1123 Constant *Zero = Constant::getNullValue(Ty);
1124
1125 IRBuilder<> Builder(Orig);
1126
1127 Value *GT;
1128 Value *LT;
1129 if (ScalarTy->isFloatingPointTy()) {
1130 GT = Builder.CreateFCmpOLT(Zero, X);
1131 LT = Builder.CreateFCmpOLT(X, Zero);
1132 } else {
1133 assert(ScalarTy->isIntegerTy());
1134 GT = Builder.CreateICmpSLT(Zero, X);
1135 LT = Builder.CreateICmpSLT(X, Zero);
1136 }
1137
1138 Value *ZextGT = Builder.CreateZExt(GT, RetTy);
1139 Value *ZextLT = Builder.CreateZExt(LT, RetTy);
1140
1141 return Builder.CreateSub(ZextGT, ZextLT);
1142}
1143
1144// Expand llvm.matrix.multiply by extracting row/column vectors and computing
1145// dot products.
1146// Result[r,c] = dot(row_r(LHS), col_c(RHS))
1147// Element (r,c) is at index c*NumRows + r (column-major).
1149 Value *LHS = Orig->getArgOperand(0);
1150 Value *RHS = Orig->getArgOperand(1);
1151 unsigned LHSRows = cast<ConstantInt>(Orig->getArgOperand(2))->getZExtValue();
1152 unsigned LHSCols = cast<ConstantInt>(Orig->getArgOperand(3))->getZExtValue();
1153 unsigned RHSCols = cast<ConstantInt>(Orig->getArgOperand(4))->getZExtValue();
1154
1155 auto *RetTy = cast<FixedVectorType>(Orig->getType());
1156 Type *EltTy = RetTy->getElementType();
1157 bool IsFP = EltTy->isFloatingPointTy();
1158
1159 IRBuilder<> Builder(Orig);
1160
1161 // Column-major indexing:
1162 // LHS row R, element K: index = K * LHSRows + R
1163 // RHS col C, element K: index = C * LHSCols + K
1164 Value *Result = PoisonValue::get(RetTy);
1165
1166 // Extract all scalar elements from LHS and RHS once, then reuse them.
1167 unsigned LHSSize = LHSRows * LHSCols;
1168 unsigned RHSSize = LHSCols * RHSCols;
1169 SmallVector<Value *, 16> LHSElts(LHSSize);
1170 SmallVector<Value *, 16> RHSElts(RHSSize);
1171 for (unsigned I = 0; I < LHSSize; ++I)
1172 LHSElts[I] = Builder.CreateExtractElement(LHS, I);
1173 for (unsigned I = 0; I < RHSSize; ++I)
1174 RHSElts[I] = Builder.CreateExtractElement(RHS, I);
1175
1176 // Choose the appropriate scalar-arg dot intrinsic for floats.
1177 // K=1 and double types use scalar expansion instead.
1179 bool UseScalarFP = IsFP && (EltTy->isDoubleTy() || LHSCols == 1);
1180 if (IsFP && !UseScalarFP) {
1181 switch (LHSCols) {
1182 case 2:
1183 FloatDotID = Intrinsic::dx_dot2;
1184 break;
1185 case 3:
1186 FloatDotID = Intrinsic::dx_dot3;
1187 break;
1188 case 4:
1189 FloatDotID = Intrinsic::dx_dot4;
1190 break;
1191 default:
1193 "Invalid matrix inner dimension for dot product: must be 2-4");
1194 return nullptr;
1195 }
1196 }
1197
1198 for (unsigned C = 0; C < RHSCols; ++C) {
1199 for (unsigned R = 0; R < LHSRows; ++R) {
1200 // Gather row R from LHS and column C from RHS.
1201 SmallVector<Value *, 4> RowElts, ColElts;
1202 for (unsigned K = 0; K < LHSCols; ++K) {
1203 RowElts.push_back(LHSElts[K * LHSRows + R]);
1204 ColElts.push_back(RHSElts[C * LHSCols + K]);
1205 }
1206
1207 Value *Dot;
1208 if (UseScalarFP) {
1209 // Scalar fmul+fmuladd expansion for double types and K=1.
1210 Dot = Builder.CreateFMul(RowElts[0], ColElts[0]);
1211 for (unsigned K = 1; K < LHSCols; ++K)
1212 Dot = Builder.CreateIntrinsic(EltTy, Intrinsic::fmuladd,
1213 {RowElts[K], ColElts[K], Dot});
1214 } else if (IsFP) {
1215 // Emit scalar-arg DXIL dot directly (dx.dot2/dx.dot3/dx.dot4).
1217 Args.append(RowElts.begin(), RowElts.end());
1218 Args.append(ColElts.begin(), ColElts.end());
1219 Dot = Builder.CreateIntrinsic(EltTy, FloatDotID, Args);
1220 } else {
1221 // Integer: emit multiply + imad chain.
1222 Dot = Builder.CreateMul(RowElts[0], ColElts[0]);
1223 for (unsigned K = 1; K < LHSCols; ++K)
1224 Dot = Builder.CreateIntrinsic(EltTy, Intrinsic::dx_imad,
1225 {RowElts[K], ColElts[K], Dot});
1226 }
1227 unsigned ResIdx = C * LHSRows + R;
1228 Result = Builder.CreateInsertElement(Result, Dot, ResIdx);
1229 }
1230 }
1231 return Result;
1232}
1233
1234// Expand llvm.matrix.transpose as a shufflevector that permutes elements
1235// from column-major source to column-major transposed layout.
1236// Element (r,c) at index c*Rows + r moves to index r*Cols + c.
1238 Value *Mat = Orig->getArgOperand(0);
1239 unsigned Rows = cast<ConstantInt>(Orig->getArgOperand(1))->getZExtValue();
1240 unsigned Cols = cast<ConstantInt>(Orig->getArgOperand(2))->getZExtValue();
1241
1242 unsigned NumElts = Rows * Cols;
1243 SmallVector<int, 16> Mask(NumElts);
1244 for (unsigned I = 0; I < NumElts; ++I)
1245 Mask[I] = (I % Cols) * Rows + (I / Cols);
1246
1247 IRBuilder<> Builder(Orig);
1248 return Builder.CreateShuffleVector(Mat, Mask);
1249}
1250
1251static bool expandIntrinsic(Function &F, CallInst *Orig) {
1252 Value *Result = nullptr;
1253 Intrinsic::ID IntrinsicId = F.getIntrinsicID();
1254 switch (IntrinsicId) {
1255 case Intrinsic::abs:
1256 Result = expandAbs(Orig);
1257 break;
1258 case Intrinsic::assume:
1259 Orig->eraseFromParent();
1260 return true;
1261 case Intrinsic::atan2:
1262 Result = expandAtan2Intrinsic(Orig);
1263 break;
1264 case Intrinsic::fshl:
1265 Result = expandFunnelShiftIntrinsic<true>(Orig);
1266 break;
1267 case Intrinsic::fshr:
1268 Result = expandFunnelShiftIntrinsic<false>(Orig);
1269 break;
1270 case Intrinsic::exp:
1271 Result = expandExpIntrinsic(Orig);
1272 break;
1273 case Intrinsic::is_fpclass:
1274 Result = expandIsFPClass(Orig);
1275 break;
1276 case Intrinsic::log:
1277 Result = expandLogIntrinsic(Orig);
1278 break;
1279 case Intrinsic::log10:
1280 Result = expandLog10Intrinsic(Orig);
1281 break;
1282 case Intrinsic::pow:
1283 case Intrinsic::powi:
1284 Result = expandPowIntrinsic(Orig, IntrinsicId);
1285 break;
1286 case Intrinsic::dx_all:
1287 case Intrinsic::dx_any:
1288 Result = expandAnyOrAllIntrinsic(Orig, IntrinsicId);
1289 break;
1290 case Intrinsic::dx_cross:
1291 Result = expandCrossIntrinsic(Orig);
1292 break;
1293 case Intrinsic::dx_uclamp:
1294 case Intrinsic::dx_sclamp:
1295 case Intrinsic::dx_nclamp:
1296 Result = expandClampIntrinsic(Orig, IntrinsicId);
1297 break;
1298 case Intrinsic::dx_degrees:
1299 Result = expandDegreesIntrinsic(Orig);
1300 break;
1301 case Intrinsic::dx_isinf:
1302 Result = expand16BitIsInf(Orig);
1303 break;
1304 case Intrinsic::dx_isnan:
1305 Result = expand16BitIsNaN(Orig);
1306 break;
1307 case Intrinsic::dx_lerp:
1308 Result = expandLerpIntrinsic(Orig);
1309 break;
1310 case Intrinsic::dx_normalize:
1311 Result = expandNormalizeIntrinsic(Orig);
1312 break;
1313 case Intrinsic::dx_fdot:
1314 Result = expandFloatDotIntrinsic(Orig);
1315 break;
1316 case Intrinsic::dx_sdot:
1317 case Intrinsic::dx_udot:
1318 Result = expandIntegerDotIntrinsic(Orig, IntrinsicId);
1319 break;
1320 case Intrinsic::dx_sign:
1321 Result = expandSignIntrinsic(Orig);
1322 break;
1323 case Intrinsic::dx_step:
1324 Result = expandStepIntrinsic(Orig);
1325 break;
1326 case Intrinsic::dx_radians:
1327 Result = expandRadiansIntrinsic(Orig);
1328 break;
1329 case Intrinsic::dx_resource_load_rawbuffer:
1330 if (expandBufferLoadIntrinsic(Orig, /*IsRaw*/ true))
1331 return true;
1332 break;
1333 case Intrinsic::dx_resource_store_rawbuffer:
1334 if (expandBufferStoreIntrinsic(Orig, /*IsRaw*/ true))
1335 return true;
1336 break;
1337 case Intrinsic::dx_resource_load_typedbuffer:
1338 if (expandBufferLoadIntrinsic(Orig, /*IsRaw*/ false))
1339 return true;
1340 break;
1341 case Intrinsic::dx_resource_store_typedbuffer:
1342 if (expandBufferStoreIntrinsic(Orig, /*IsRaw*/ false))
1343 return true;
1344 break;
1345 case Intrinsic::usub_sat:
1346 Result = expandUsubSat(Orig);
1347 break;
1348 case Intrinsic::umul_with_overflow:
1349 case Intrinsic::smul_with_overflow:
1350 Result = expandMulWithOverflow(Orig, /*Signed=*/IntrinsicId ==
1351 Intrinsic::smul_with_overflow);
1352 break;
1353 case Intrinsic::vector_reduce_add:
1354 case Intrinsic::vector_reduce_fadd:
1355 Result = expandVecReduceAdd(Orig, IntrinsicId);
1356 break;
1357 case Intrinsic::matrix_multiply:
1358 Result = expandMatrixMultiply(Orig);
1359 break;
1360 case Intrinsic::matrix_transpose:
1361 Result = expandMatrixTranspose(Orig);
1362 break;
1363 }
1364 if (Result) {
1365 Orig->replaceAllUsesWith(Result);
1366 Orig->eraseFromParent();
1367 return true;
1368 }
1369 return false;
1370}
1371
1373 for (auto &F : make_early_inc_range(M.functions())) {
1374 if (!isIntrinsicExpansion(F))
1375 continue;
1376 bool IntrinsicExpanded = false;
1377 for (User *U : make_early_inc_range(F.users())) {
1378 auto *IntrinsicCall = dyn_cast<CallInst>(U);
1379 if (!IntrinsicCall)
1380 continue;
1381 IntrinsicExpanded = expandIntrinsic(F, IntrinsicCall);
1382 }
1383 if (F.user_empty() && IntrinsicExpanded)
1384 F.eraseFromParent();
1385 }
1386 return true;
1387}
1388
1395
1399
1401
1403 "DXIL Intrinsic Expansion", false, false)
1405 "DXIL Intrinsic Expansion", false, false)
1406
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file implements a class to represent arbitrary precision integral constant values and operations...
#define X(NUM, ENUM, NAME)
Definition ELF.h:856
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
static Value * expand16BitIsNormal(CallInst *Orig)
static Value * expandNormalizeIntrinsic(CallInst *Orig)
static Value * createMulHighUnsigned(IRBuilder<> &Builder, Value *A, Value *B, Type *Ty, unsigned BW)
static bool expandIntrinsic(Function &F, CallInst *Orig)
static Value * expandClampIntrinsic(CallInst *Orig, Intrinsic::ID ClampIntrinsic)
static Value * expand16BitIsInf(CallInst *Orig)
static bool expansionIntrinsics(Module &M)
static Value * expand16BitIsFinite(CallInst *Orig)
static Value * expandLerpIntrinsic(CallInst *Orig)
static Value * expandCrossIntrinsic(CallInst *Orig)
static Value * expandUsubSat(CallInst *Orig)
static Value * expandAnyOrAllIntrinsic(CallInst *Orig, Intrinsic::ID IntrinsicId)
static Value * expandMatrixTranspose(CallInst *Orig)
static Value * expandVecReduceAdd(CallInst *Orig, Intrinsic::ID IntrinsicId)
static Value * expandAtan2Intrinsic(CallInst *Orig)
static Value * expandLog10Intrinsic(CallInst *Orig)
static Intrinsic::ID getMinForClamp(Intrinsic::ID ClampIntrinsic)
static Value * expandStepIntrinsic(CallInst *Orig)
static Value * expandIntegerDotIntrinsic(CallInst *Orig, Intrinsic::ID DotIntrinsic)
static bool expandBufferStoreIntrinsic(CallInst *Orig, bool IsRaw)
static Value * expandLogIntrinsic(CallInst *Orig, float LogConstVal=numbers::ln2f)
static Value * expandDegreesIntrinsic(CallInst *Orig)
static Value * expandMulWithOverflow(CallInst *Orig, bool Signed)
static Value * expandPowIntrinsic(CallInst *Orig, Intrinsic::ID IntrinsicId)
static bool resourceAccessNeeds64BitExpansion(Module *M, Type *OverloadTy, bool IsRaw)
static Value * expandExpIntrinsic(CallInst *Orig)
static Value * expand16BitIsNaN(CallInst *Orig)
static Value * expandSignIntrinsic(CallInst *Orig)
static Intrinsic::ID getMaxForClamp(Intrinsic::ID ClampIntrinsic)
static Value * expandAbs(CallInst *Orig)
static Value * expandFloatDotIntrinsic(CallInst *Orig, Value *A, Value *B)
static Value * expandRadiansIntrinsic(CallInst *Orig)
static bool isIntrinsicExpansion(Function &F)
static bool expandBufferLoadIntrinsic(CallInst *Orig, bool IsRaw)
static Value * expandMatrixMultiply(CallInst *Orig)
static Value * expandIsFPClass(CallInst *Orig)
static Value * expandFunnelShiftIntrinsic(CallInst *Orig)
#define DEBUG_TYPE
Module.h This file contains the declarations for the Module class.
This header defines various interfaces for pass management in LLVM.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
#define T
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
const SmallVectorImpl< MachineOperand > & Cond
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
static TableGen::Emitter::Opt Y("gen-skeleton-entry", EmitSkeleton, "Generate example skeleton entry")
Value * RHS
Value * LHS
BinaryOperator * Mul
bool runOnModule(Module &M) override
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
bool isZero() const
Definition APFloat.h:1561
static APInt getLowBitsSet(unsigned numBits, unsigned loBitsSet)
Constructs an APInt value that has the bottom loBitsSet bits set.
Definition APInt.h:307
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
void setAttributes(AttributeList A)
Set the attributes for this call.
Value * getArgOperand(unsigned i) const
FunctionType * getFunctionType() const
AttributeList getAttributes() const
Return the attributes for this call.
This class represents a function call, abstracting a target machine's calling convention.
bool isTailCall() const
void setTailCall(bool IsTc=true)
static LLVM_ABI Constant * getSplat(ElementCount EC, Constant *Elt)
Return a ConstantVector with the specified constant in each element.
This is an important base class in LLVM.
Definition Constant.h:43
bool isNullValue() const
Return true if this is the value that would be returned by getNullValue.
Definition Constant.h:64
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
PreservedAnalyses run(Module &M, ModuleAnalysisManager &)
static constexpr ElementCount getFixed(ScalarTy MinVal)
Definition TypeSize.h:309
static LLVM_ABI FixedVectorType * get(Type *ElementType, unsigned NumElts)
Definition Type.cpp:867
Type * getParamType(unsigned i) const
Parameter type accessors.
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2893
LLVM_ABI const Module * getModule() const
Return the module owning the function this instruction belongs to or nullptr it the function does not...
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
LLVM_ABI FastMathFlags getFastMathFlags() const LLVM_READONLY
Convenience function for getting all the fast-math flags, which must be an operator which supports th...
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
ModulePass(char &pid)
Definition Pass.h:257
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
static LLVM_ABI PoisonValue * get(Type *T)
Static factory methods - Return an 'poison' object of the specified type.
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:477
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
LLVM_ABI Type * getStructElementType(unsigned N) const
bool isVectorTy() const
True if this is an instance of VectorType.
Definition Type.h:288
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:309
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition Type.h:368
LLVM_ABI TypeSize getPrimitiveSizeInBits() const LLVM_READONLY
Return the basic size of this type if it is a primitive type.
Definition Type.cpp:197
LLVM_ABI Type * getWithNewBitWidth(unsigned NewBitWidth) const
Given an integer or vector type, change the lane bitwidth to NewBitwidth, whilst keeping the old numb...
static LLVM_ABI IntegerType * getInt16Ty(LLVMContext &C)
Definition Type.cpp:308
bool isHalfTy() const
Return true if this is 'half', a 16-bit IEEE fp type.
Definition Type.h:144
bool isDoubleTy() const
Return true if this is 'double', a 64-bit IEEE fp type.
Definition Type.h:158
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition Type.h:186
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition Type.h:257
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:313
Value * getOperand(unsigned i) const
Definition User.h:207
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:255
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
iterator_range< user_iterator > users()
Definition Value.h:426
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:319
static LLVM_ABI VectorType * get(Type *ElementType, ElementCount EC)
This static method is the primary way to construct an VectorType.
Represents a version number in the form major[.minor[.subminor[.build]]].
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
constexpr double inv_pi
constexpr float ln10f
Definition MathExtras.h:51
constexpr float log2ef
Definition MathExtras.h:52
constexpr double pi
constexpr float ln2f
Definition MathExtras.h:50
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
@ Load
The value being inserted comes from a load (InsertElement only).
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:633
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
ModulePass * createDXILIntrinsicExpansionLegacyPass()
Pass to expand intrinsic operations that lack DXIL opCodes.
@ Sub
Subtraction of integers.
constexpr unsigned BitWidth
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177