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
13#include "DirectX.h"
14#include "llvm/ADT/APInt.h"
15#include "llvm/ADT/STLExtras.h"
17#include "llvm/CodeGen/Passes.h"
18#include "llvm/IR/Constants.h"
19#include "llvm/IR/IRBuilder.h"
20#include "llvm/IR/InstrTypes.h"
21#include "llvm/IR/Instruction.h"
23#include "llvm/IR/Intrinsics.h"
24#include "llvm/IR/IntrinsicsDirectX.h"
26#include "llvm/IR/Module.h"
27#include "llvm/IR/PassManager.h"
28#include "llvm/IR/Type.h"
29#include "llvm/Pass.h"
33
34#define DEBUG_TYPE "dxil-intrinsic-expansion"
35
36using namespace llvm;
37
39
40public:
41 bool runOnModule(Module &M) override;
43
44 static char ID; // Pass identification.
45};
46
47static bool resourceAccessNeeds64BitExpansion(Module *M, Type *OverloadTy,
48 bool IsRaw) {
49 if (IsRaw && M->getTargetTriple().getDXILVersion() > VersionTuple(1, 2))
50 return false;
51
52 Type *ScalarTy = OverloadTy->getScalarType();
53 return ScalarTy->isDoubleTy() || ScalarTy->isIntegerTy(64);
54}
55
57 Module *M = Orig->getModule();
58 if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
59 return nullptr;
60
61 Value *Val = Orig->getOperand(0);
62 Type *ValTy = Val->getType();
63 if (!ValTy->getScalarType()->isHalfTy())
64 return nullptr;
65
66 IRBuilder<> Builder(Orig);
67 Type *IType = Type::getInt16Ty(M->getContext());
68 Constant *PosInf =
69 ValTy->isVectorTy()
72 cast<FixedVectorType>(ValTy)->getNumElements()),
73 ConstantInt::get(IType, 0x7c00))
74 : ConstantInt::get(IType, 0x7c00);
75
76 Constant *NegInf =
77 ValTy->isVectorTy()
80 cast<FixedVectorType>(ValTy)->getNumElements()),
81 ConstantInt::get(IType, 0xfc00))
82 : ConstantInt::get(IType, 0xfc00);
83
84 Value *IVal = Builder.CreateBitCast(Val, PosInf->getType());
85 Value *B1 = Builder.CreateICmpEQ(IVal, PosInf);
86 Value *B2 = Builder.CreateICmpEQ(IVal, NegInf);
87 Value *B3 = Builder.CreateOr(B1, B2);
88 return B3;
89}
90
92 Module *M = Orig->getModule();
93 if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
94 return nullptr;
95
96 Value *Val = Orig->getOperand(0);
97 Type *ValTy = Val->getType();
98 if (!ValTy->getScalarType()->isHalfTy())
99 return nullptr;
100
101 IRBuilder<> Builder(Orig);
102 Type *IType = Type::getInt16Ty(M->getContext());
103
104 Constant *ExpBitMask =
105 ValTy->isVectorTy()
108 cast<FixedVectorType>(ValTy)->getNumElements()),
109 ConstantInt::get(IType, 0x7c00))
110 : ConstantInt::get(IType, 0x7c00);
111 Constant *SigBitMask =
112 ValTy->isVectorTy()
115 cast<FixedVectorType>(ValTy)->getNumElements()),
116 ConstantInt::get(IType, 0x3ff))
117 : ConstantInt::get(IType, 0x3ff);
118
119 Constant *Zero =
120 ValTy->isVectorTy()
123 cast<FixedVectorType>(ValTy)->getNumElements()),
124 ConstantInt::get(IType, 0))
125 : ConstantInt::get(IType, 0);
126
127 Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
128 Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
129 Value *B1 = Builder.CreateICmpEQ(Exp, ExpBitMask);
130
131 Value *Sig = Builder.CreateAnd(IVal, SigBitMask);
132 Value *B2 = Builder.CreateICmpNE(Sig, Zero);
133 Value *B3 = Builder.CreateAnd(B1, B2);
134 return B3;
135}
136
138 Module *M = Orig->getModule();
139 if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
140 return nullptr;
141
142 Value *Val = Orig->getOperand(0);
143 Type *ValTy = Val->getType();
144 if (!ValTy->getScalarType()->isHalfTy())
145 return nullptr;
146
147 IRBuilder<> Builder(Orig);
148 Type *IType = Type::getInt16Ty(M->getContext());
149
150 Constant *ExpBitMask =
151 ValTy->isVectorTy()
154 cast<FixedVectorType>(ValTy)->getNumElements()),
155 ConstantInt::get(IType, 0x7c00))
156 : ConstantInt::get(IType, 0x7c00);
157
158 Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
159 Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
160 Value *B1 = Builder.CreateICmpNE(Exp, ExpBitMask);
161 return B1;
162}
163
165 Module *M = Orig->getModule();
166 if (M->getTargetTriple().getDXILVersion() >= VersionTuple(1, 9))
167 return nullptr;
168
169 Value *Val = Orig->getOperand(0);
170 Type *ValTy = Val->getType();
171 if (!ValTy->getScalarType()->isHalfTy())
172 return nullptr;
173
174 IRBuilder<> Builder(Orig);
175 Type *IType = Type::getInt16Ty(M->getContext());
176
177 Constant *ExpBitMask =
178 ValTy->isVectorTy()
181 cast<FixedVectorType>(ValTy)->getNumElements()),
182 ConstantInt::get(IType, 0x7c00))
183 : ConstantInt::get(IType, 0x7c00);
184 Constant *Zero =
185 ValTy->isVectorTy()
188 cast<FixedVectorType>(ValTy)->getNumElements()),
189 ConstantInt::get(IType, 0))
190 : ConstantInt::get(IType, 0);
191
192 Value *IVal = Builder.CreateBitCast(Val, ExpBitMask->getType());
193 Value *Exp = Builder.CreateAnd(IVal, ExpBitMask);
194 Value *NotAllZeroes = Builder.CreateICmpNE(Exp, Zero);
195 Value *NotAllOnes = Builder.CreateICmpNE(Exp, ExpBitMask);
196 Value *B1 = Builder.CreateAnd(NotAllZeroes, NotAllOnes);
197 return B1;
198}
199
201 assert(F.getIntrinsicID() == Intrinsic::dx_fdot &&
202 "Function is not a dx.fdot intrinsic");
203 auto *ParamTy = cast<FixedVectorType>(F.getFunctionType()->getParamType(0));
204 return ParamTy->getNumElements() <= 4 ||
205 F.getParent()->getTargetTriple().getOSVersion() < VersionTuple(6, 9);
206}
207
209 switch (F.getIntrinsicID()) {
210 case Intrinsic::assume:
211 case Intrinsic::abs:
212 case Intrinsic::atan2:
213 case Intrinsic::copysign:
214 case Intrinsic::fshl:
215 case Intrinsic::fshr:
216 case Intrinsic::exp:
217 case Intrinsic::is_fpclass:
218 case Intrinsic::log:
219 case Intrinsic::log10:
220 case Intrinsic::pow:
221 case Intrinsic::powi:
222 case Intrinsic::dx_uclamp:
223 case Intrinsic::dx_sclamp:
224 case Intrinsic::dx_nclamp:
225 case Intrinsic::dx_isfinite:
226 case Intrinsic::dx_isinf:
227 case Intrinsic::dx_isnan:
228 case Intrinsic::dx_sdot:
229 case Intrinsic::dx_udot:
230 case Intrinsic::dx_sign:
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 case Intrinsic::dx_load_input:
239 case Intrinsic::dx_store_output:
240 return true;
241 case Intrinsic::vector_reduce_and:
242 case Intrinsic::vector_reduce_or:
243 return F.getParent()->getTargetTriple().getDXILVersion() <
244 VersionTuple(1, 9);
245 case Intrinsic::dx_fdot:
247 case Intrinsic::dx_resource_load_rawbuffer:
249 F.getParent(), F.getReturnType()->getStructElementType(0),
250 /*IsRaw*/ true);
251 case Intrinsic::dx_resource_load_typedbuffer:
253 F.getParent(), F.getReturnType()->getStructElementType(0),
254 /*IsRaw*/ false);
255 case Intrinsic::dx_resource_store_rawbuffer:
257 F.getParent(), F.getFunctionType()->getParamType(3), /*IsRaw*/ true);
258 case Intrinsic::dx_resource_store_typedbuffer:
260 F.getParent(), F.getFunctionType()->getParamType(2), /*IsRaw*/ false);
261 }
262 return false;
263}
264
266 Value *A = Orig->getArgOperand(0);
267 Value *B = Orig->getArgOperand(1);
268 Type *Ty = A->getType();
269
270 IRBuilder<> Builder(Orig);
271
272 Value *Cmp = Builder.CreateICmpULT(A, B, "usub.cmp");
273 Value *Sub = Builder.CreateSub(A, B, "usub.sub");
274 Value *Zero = ConstantInt::get(Ty, 0);
275 return Builder.CreateSelect(Cmp, Zero, Sub, "usub.sat");
276}
277
278// Compute the high N bits of the 2N-bit unsigned product of two N-bit values
279// using only N-bit arithmetic, so we don't introduce a wider integer type that
280// may be unsupported in DXIL.
282 Type *Ty, unsigned BW) {
283 assert(BW % 2 == 0 && "high-half split needs symmetric halves");
284 unsigned Half = BW / 2;
285 Value *HalfShift = ConstantInt::get(Ty, Half);
286 Value *LoMask = ConstantInt::get(Ty, APInt::getLowBitsSet(BW, Half));
287
288 Value *U0 = Builder.CreateAnd(A, LoMask);
289 Value *U1 = Builder.CreateLShr(A, HalfShift);
290 Value *V0 = Builder.CreateAnd(B, LoMask);
291 Value *V1 = Builder.CreateLShr(B, HalfShift);
292
293 Value *W0 = Builder.CreateMul(U0, V0);
294 Value *T = Builder.CreateAdd(Builder.CreateMul(U1, V0),
295 Builder.CreateLShr(W0, HalfShift));
296 Value *W1 = Builder.CreateAnd(T, LoMask);
297 Value *W2 = Builder.CreateLShr(T, HalfShift);
298 W1 = Builder.CreateAdd(Builder.CreateMul(U0, V1), W1);
299 return Builder.CreateAdd(Builder.CreateAdd(Builder.CreateMul(U1, V1), W2),
300 Builder.CreateLShr(W1, HalfShift));
301}
302
303// Expand a {u,s}mul.with.overflow intrinsic. The low half of the result is a
304// plain multiply; overflow is derived from the high half of the double-width
305// product.
307 IRBuilder<> Builder(Orig);
308 Value *A = Orig->getArgOperand(0);
309 Value *B = Orig->getArgOperand(1);
310 Type *Ty = A->getType();
311 unsigned BW = Ty->getScalarSizeInBits();
312
313 Value *Lo;
314 Value *Ov;
315
316 // A plain double-width multiply is simplest, but we avoid it once it would
317 // introduce a 64-bit (or wider) integer, which DXIL does not always support.
318 // For i32 we use the native DXIL IMul/UMul ops, which return the full product
319 // as two i32s; wider types fall back to a same-width high-half computation.
320 if (2 * BW <= 32) {
321 Lo = Builder.CreateMul(A, B);
322 Type *WideTy = Ty->getWithNewBitWidth(2 * BW);
323 Value *WideA =
324 Signed ? Builder.CreateSExt(A, WideTy) : Builder.CreateZExt(A, WideTy);
325 Value *WideB =
326 Signed ? Builder.CreateSExt(B, WideTy) : Builder.CreateZExt(B, WideTy);
327 Value *Wide = Builder.CreateMul(WideA, WideB);
328 if (Signed) {
329 // Overflow when the full product doesn't fit back into BW signed bits.
330 Ov = Builder.CreateICmpNE(Wide, Builder.CreateSExt(Lo, WideTy));
331 } else {
332 Value *Hi = Builder.CreateLShr(Wide, ConstantInt::get(WideTy, BW));
333 Ov = Builder.CreateICmpNE(Hi, ConstantInt::get(WideTy, 0));
334 }
335 } else if (BW == 32) {
336 // IMul/UMul return {high, low}; index 0 is the high 32 bits.
337 Type *ResTy = StructType::get(Ty, Ty);
338 Intrinsic::ID IntrinsicID =
339 Signed ? Intrinsic::dx_imul : Intrinsic::dx_umul;
340 Value *Mul = Builder.CreateIntrinsic(ResTy, IntrinsicID, {A, B});
341 Value *Hi = Builder.CreateExtractValue(Mul, 0);
342 Lo = Builder.CreateExtractValue(Mul, 1);
343 if (Signed)
344 Ov = Builder.CreateICmpNE(
345 Hi, Builder.CreateAShr(Lo, ConstantInt::get(Ty, BW - 1)));
346 else
347 Ov = Builder.CreateICmpNE(Hi, ConstantInt::get(Ty, 0));
348 } else {
349 Lo = Builder.CreateMul(A, B);
350 Value *Hi = createMulHighUnsigned(Builder, A, B, Ty, BW);
351 if (Signed) {
352 // Turn the unsigned high half into the signed one, then overflow means it
353 // isn't the sign extension of the low half.
354 Value *SignShift = ConstantInt::get(Ty, BW - 1);
355 Value *ASign = Builder.CreateAShr(A, SignShift);
356 Value *BSign = Builder.CreateAShr(B, SignShift);
357 Hi = Builder.CreateSub(Hi, Builder.CreateAnd(ASign, B));
358 Hi = Builder.CreateSub(Hi, Builder.CreateAnd(BSign, A));
359 Ov = Builder.CreateICmpNE(Hi, Builder.CreateAShr(Lo, SignShift));
360 } else {
361 Ov = Builder.CreateICmpNE(Hi, ConstantInt::get(Ty, 0));
362 }
363 }
364
365 Value *Agg = PoisonValue::get(Orig->getType());
366 Agg = Builder.CreateInsertValue(Agg, Lo, 0);
367 return Builder.CreateInsertValue(Agg, Ov, 1);
368}
369
370static Value *expandVecReduceAdd(CallInst *Orig, Intrinsic::ID IntrinsicId) {
371 assert(IntrinsicId == Intrinsic::vector_reduce_add ||
372 IntrinsicId == Intrinsic::vector_reduce_fadd);
373
374 IRBuilder<> Builder(Orig);
375 bool IsFAdd = (IntrinsicId == Intrinsic::vector_reduce_fadd);
376
377 Value *X = Orig->getOperand(IsFAdd ? 1 : 0);
378 Type *Ty = X->getType();
379 auto *XVec = dyn_cast<FixedVectorType>(Ty);
380 unsigned XVecSize = XVec->getNumElements();
381 Value *Sum = Builder.CreateExtractElement(X, static_cast<uint64_t>(0));
382
383 // Handle the initial start value for floating-point addition.
384 if (IsFAdd) {
385 Constant *StartValue = dyn_cast<Constant>(Orig->getOperand(0));
386 if (StartValue && !StartValue->isNullValue())
387 Sum = Builder.CreateFAdd(Sum, StartValue);
388 }
389
390 // Accumulate the remaining vector elements.
391 for (unsigned I = 1; I < XVecSize; I++) {
392 Value *Elt = Builder.CreateExtractElement(X, I);
393 if (IsFAdd)
394 Sum = Builder.CreateFAdd(Sum, Elt);
395 else
396 Sum = Builder.CreateAdd(Sum, Elt);
397 }
398
399 return Sum;
400}
401
403 assert(IntrinsicId == Intrinsic::vector_reduce_and ||
404 IntrinsicId == Intrinsic::vector_reduce_or);
405
406 IRBuilder<> Builder(Orig);
407 Value *X = Orig->getArgOperand(0);
408 auto *XVec = cast<FixedVectorType>(X->getType());
409 Value *Result = Builder.CreateExtractElement(X, static_cast<uint64_t>(0));
410 for (unsigned I = 1; I < XVec->getNumElements(); ++I) {
411 Value *Elt = Builder.CreateExtractElement(X, I);
412 Result = IntrinsicId == Intrinsic::vector_reduce_or
413 ? Builder.CreateOr(Result, Elt)
414 : Builder.CreateAnd(Result, Elt);
415 }
416 return Result;
417}
418
419static Value *expandAbs(CallInst *Orig) {
420 Value *X = Orig->getOperand(0);
421 IRBuilder<> Builder(Orig);
422 Type *Ty = X->getType();
423 Type *EltTy = Ty->getScalarType();
424 Constant *Zero = Ty->isVectorTy()
427 cast<FixedVectorType>(Ty)->getNumElements()),
428 ConstantInt::get(EltTy, 0))
429 : ConstantInt::get(EltTy, 0);
430 auto *V = Builder.CreateSub(Zero, X);
431 return Builder.CreateIntrinsic(Ty, Intrinsic::smax, {X, V}, nullptr,
432 "dx.max");
433}
434
435// Create a DXIL dot2, dot3, or dot4 for the given operands.
437 Type *ATy = A->getType();
438 [[maybe_unused]] Type *BTy = B->getType();
439 assert(ATy->isVectorTy() && BTy->isVectorTy());
440
441 IRBuilder<> Builder(Orig);
442
443 auto *AVec = dyn_cast<FixedVectorType>(ATy);
444
446
447 unsigned NumElts = AVec->getNumElements();
448 Intrinsic::ID DotIntrinsic;
449 switch (NumElts) {
450 case 2:
451 DotIntrinsic = Intrinsic::dx_dot2;
452 break;
453 case 3:
454 DotIntrinsic = Intrinsic::dx_dot3;
455 break;
456 case 4:
457 DotIntrinsic = Intrinsic::dx_dot4;
458 break;
459 default:
461 "Invalid dot product input vector: length is outside 2-4");
462 }
463
465 for (unsigned I = 0; I < NumElts; ++I)
466 Args.push_back(Builder.CreateExtractElement(A, Builder.getInt32(I)));
467 for (unsigned I = 0; I < NumElts; ++I)
468 Args.push_back(Builder.CreateExtractElement(B, Builder.getInt32(I)));
469 return Builder.CreateIntrinsic(ATy->getScalarType(), DotIntrinsic, Args,
470 nullptr, "dot");
471}
472
473// Expand an arbitrary-width float dot into the minimum number of legal DXIL
474// dot2, dot3, and dot4 operations.
476 Value *A = Orig->getOperand(0);
477 Value *B = Orig->getOperand(1);
478 unsigned NumElts = cast<FixedVectorType>(A->getType())->getNumElements();
479
480 // We return early here to avoid constructing unnecessary identity shuffles.
481 if (NumElts <= 4)
482 return expandFloatDotChunk(Orig, A, B);
483
485 VersionTuple(6, 9) &&
486 "long fdot must not be expanded for shader model 6.9 or later");
487
488 IRBuilder<> Builder(Orig);
489 Value *Result = nullptr;
490 for (unsigned Offset = 0; Offset < NumElts;) {
491 unsigned Remaining = NumElts - Offset;
492 // Taking four is optimal unless it would leave an illegal one-element
493 // tail. In that case, take three and finish with dot2.
494 unsigned ChunkSize = Remaining == 5 ? 3 : std::min(Remaining, 4u);
496 for (unsigned I = 0; I < ChunkSize; ++I)
497 Mask.push_back(Offset + I);
498 Value *AChunk = Builder.CreateShuffleVector(A, Mask);
499 Value *BChunk = Builder.CreateShuffleVector(B, Mask);
500 Value *Chunk = expandFloatDotChunk(Orig, AChunk, BChunk);
501 Result = Result ? Builder.CreateFAdd(Result, Chunk, "dot.add") : Chunk;
502 Offset += ChunkSize;
503 }
504 return Result;
505}
506
507// Expand integer dot product to multiply and add ops
509 Intrinsic::ID DotIntrinsic) {
510 assert(DotIntrinsic == Intrinsic::dx_sdot ||
511 DotIntrinsic == Intrinsic::dx_udot);
512 Value *A = Orig->getOperand(0);
513 Value *B = Orig->getOperand(1);
514 Type *ATy = A->getType();
515 [[maybe_unused]] Type *BTy = B->getType();
516 assert(ATy->isVectorTy() && BTy->isVectorTy());
517
518 IRBuilder<> Builder(Orig);
519
520 auto *AVec = dyn_cast<FixedVectorType>(ATy);
521
523
524 Value *Result;
525 Intrinsic::ID MadIntrinsic = DotIntrinsic == Intrinsic::dx_sdot
526 ? Intrinsic::dx_imad
527 : Intrinsic::dx_umad;
528 Value *Elt0 = Builder.CreateExtractElement(A, (uint64_t)0);
529 Value *Elt1 = Builder.CreateExtractElement(B, (uint64_t)0);
530 Result = Builder.CreateMul(Elt0, Elt1);
531 for (unsigned I = 1; I < AVec->getNumElements(); I++) {
532 Elt0 = Builder.CreateExtractElement(A, I);
533 Elt1 = Builder.CreateExtractElement(B, I);
534 Result = Builder.CreateIntrinsic(Result->getType(), MadIntrinsic,
535 ArrayRef<Value *>{Elt0, Elt1, Result},
536 nullptr, "dx.mad");
537 }
538 return Result;
539}
540
542 Value *X = Orig->getOperand(0);
543 IRBuilder<> Builder(Orig);
544 Type *Ty = X->getType();
545 Type *EltTy = Ty->getScalarType();
546 Constant *Log2eConst =
547 Ty->isVectorTy() ? ConstantVector::getSplat(
549 cast<FixedVectorType>(Ty)->getNumElements()),
550 ConstantFP::get(EltTy, numbers::log2ef))
551 : ConstantFP::get(EltTy, numbers::log2ef);
552 Value *NewX = Builder.CreateFMul(Log2eConst, X);
553 CallInst *Exp2Call = Builder.CreateIntrinsicWithoutFolding(
554 Ty, Intrinsic::exp2, {NewX}, nullptr, "dx.exp2");
555 Exp2Call->setTailCall(Orig->isTailCall());
556 Exp2Call->setAttributes(Orig->getAttributes());
557 return Exp2Call;
558}
559
561 Value *T = Orig->getArgOperand(1);
562 auto *TCI = dyn_cast<ConstantInt>(T);
563
564 // These FPClassTest cases have DXIL opcodes, so they will be handled in
565 // DXIL Op Lowering instead for all non f16 cases.
566 switch (TCI->getZExtValue()) {
568 return expand16BitIsInf(Orig);
570 return expand16BitIsNaN(Orig);
572 return expand16BitIsNormal(Orig);
574 return expand16BitIsFinite(Orig);
575 }
576
577 IRBuilder<> Builder(Orig);
578
579 Value *F = Orig->getArgOperand(0);
580 Type *FTy = F->getType();
581 unsigned FNumElem = 0; // 0 => F is not a vector
582
583 unsigned BitWidth; // Bit width of F or the ElemTy of F
584 Type *BitCastTy; // An IntNTy of the same bitwidth as F or ElemTy of F
585
586 if (auto *FVecTy = dyn_cast<FixedVectorType>(FTy)) {
587 Type *ElemTy = FVecTy->getElementType();
588 FNumElem = FVecTy->getNumElements();
589 BitWidth = ElemTy->getPrimitiveSizeInBits();
590 BitCastTy = FixedVectorType::get(Builder.getIntNTy(BitWidth), FNumElem);
591 } else {
593 BitCastTy = Builder.getIntNTy(BitWidth);
594 }
595
596 Value *FBitCast = Builder.CreateBitCast(F, BitCastTy);
597 switch (TCI->getZExtValue()) {
599 Value *NegZero = ConstantInt::get(Builder.getIntNTy(BitWidth),
601 Value *RetVal;
602 if (FNumElem) {
603 Value *NegZeroSplat = Builder.CreateVectorSplat(FNumElem, NegZero);
604 RetVal =
605 Builder.CreateICmpEQ(FBitCast, NegZeroSplat, "is.fpclass.negzero");
606 } else
607 RetVal = Builder.CreateICmpEQ(FBitCast, NegZero, "is.fpclass.negzero");
608 return RetVal;
609 }
610 default:
611 reportFatalUsageError("Unsupported FPClassTest");
612 }
613}
614
616 float LogConstVal = numbers::ln2f) {
617 Value *X = Orig->getOperand(0);
618 IRBuilder<> Builder(Orig);
619 Type *Ty = X->getType();
620 Type *EltTy = Ty->getScalarType();
621 Constant *Ln2Const =
622 Ty->isVectorTy() ? ConstantVector::getSplat(
624 cast<FixedVectorType>(Ty)->getNumElements()),
625 ConstantFP::get(EltTy, LogConstVal))
626 : ConstantFP::get(EltTy, LogConstVal);
627 CallInst *Log2Call = Builder.CreateIntrinsicWithoutFolding(
628 Ty, Intrinsic::log2, {X}, nullptr, "elt.log2");
629 Log2Call->setTailCall(Orig->isTailCall());
630 Log2Call->setAttributes(Orig->getAttributes());
631 return Builder.CreateFMul(Ln2Const, Log2Call);
632}
636
638 Value *Y = Orig->getOperand(0);
639 Value *X = Orig->getOperand(1);
640 Type *Ty = X->getType();
641 IRBuilder<> Builder(Orig);
642 Builder.setFastMathFlags(Orig->getFastMathFlags());
643
644 Value *Tan = Builder.CreateFDiv(Y, X);
645
646 CallInst *Atan = Builder.CreateIntrinsicWithoutFolding(
647 Ty, Intrinsic::atan, {Tan}, nullptr, "Elt.Atan");
648 Atan->setTailCall(Orig->isTailCall());
649 Atan->setAttributes(Orig->getAttributes());
650
651 // Modify atan result based on https://en.wikipedia.org/wiki/Atan2.
652 Constant *Pi = ConstantFP::get(Ty, llvm::numbers::pi);
653 Constant *HalfPi = ConstantFP::get(Ty, llvm::numbers::pi / 2);
654 Constant *NegHalfPi = ConstantFP::get(Ty, -llvm::numbers::pi / 2);
655 Constant *Zero = ConstantFP::get(Ty, 0);
656 Value *AtanAddPi = Builder.CreateFAdd(Atan, Pi);
657 Value *AtanSubPi = Builder.CreateFSub(Atan, Pi);
658
659 // x > 0 -> atan.
660 Value *Result = Atan;
661 Value *XLt0 = Builder.CreateFCmpOLT(X, Zero);
662 Value *XEq0 = Builder.CreateFCmpOEQ(X, Zero);
663 Value *YGe0 = Builder.CreateFCmpOGE(Y, Zero);
664 Value *YLt0 = Builder.CreateFCmpOLT(Y, Zero);
665
666 // x < 0, y >= 0 -> atan + pi.
667 Value *XLt0AndYGe0 = Builder.CreateAnd(XLt0, YGe0);
668 Result = Builder.CreateSelect(XLt0AndYGe0, AtanAddPi, Result);
669
670 // x < 0, y < 0 -> atan - pi.
671 Value *XLt0AndYLt0 = Builder.CreateAnd(XLt0, YLt0);
672 Result = Builder.CreateSelect(XLt0AndYLt0, AtanSubPi, Result);
673
674 // x == 0, y < 0 -> -pi/2
675 Value *XEq0AndYLt0 = Builder.CreateAnd(XEq0, YLt0);
676 Result = Builder.CreateSelect(XEq0AndYLt0, NegHalfPi, Result);
677
678 // x == 0, y > 0 -> pi/2
679 Value *XEq0AndYGe0 = Builder.CreateAnd(XEq0, YGe0);
680 Result = Builder.CreateSelect(XEq0AndYGe0, HalfPi, Result);
681
682 return Result;
683}
684
685template <bool LeftFunnel>
687 Type *Ty = Orig->getType();
688 Value *A = Orig->getOperand(0);
689 Value *B = Orig->getOperand(1);
690 Value *Shift = Orig->getOperand(2);
691
692 IRBuilder<> Builder(Orig);
693
694 assert(llvm::isPowerOf2_32(Ty->getScalarSizeInBits()) &&
695 "Can't use Mask to compute modulo and inverse");
696
697 // Note: if (Shift % BitWidth) == 0 then (BitWidth - Shift) == BitWidth,
698 // shifting by the bitwidth for shl/lshr returns a poisoned result. As such,
699 // we implement the same formula as LegalizerHelper::lowerFunnelShiftAsShifts.
700 //
701 // The funnel shift is expanded like so:
702 // fshl
703 // -> msb_extract((concat(A, B) << (Shift % BitWidth)), BitWidth)
704 // -> A << (Shift % BitWidth) | B >> 1 >> (BitWidth - 1 - (Shift % BitWidth))
705 // fshr
706 // -> lsb_extract((concat(A, B) >> (Shift % BitWidth), BitWidth))
707 // -> A << 1 << (BitWidth - 1 - (Shift % BitWidth)) | B >> (Shift % BitWidth)
708
709 // (BitWidth - 1) -> Mask
710 Constant *Mask = ConstantInt::get(Ty, Ty->getScalarSizeInBits() - 1);
711
712 // Shift % BitWidth
713 // -> Shift & (BitWidth - 1)
714 // -> Shift & Mask
715 Value *MaskedShift = Builder.CreateAnd(Shift, Mask);
716
717 // (BitWidth - 1) - (Shift % BitWidth)
718 // -> ~Shift & (BitWidth - 1)
719 // -> ~Shift & Mask
720 Value *NotShift = Builder.CreateNot(Shift);
721 Value *InverseShift = Builder.CreateAnd(NotShift, Mask);
722
723 Constant *One = ConstantInt::get(Ty, 1);
724 Value *ShiftedA;
725 Value *ShiftedB;
726
727 if (LeftFunnel) {
728 ShiftedA = Builder.CreateShl(A, MaskedShift);
729 Value *ShiftB1 = Builder.CreateLShr(B, One);
730 ShiftedB = Builder.CreateLShr(ShiftB1, InverseShift);
731 } else {
732 Value *ShiftA1 = Builder.CreateShl(A, One);
733 ShiftedA = Builder.CreateShl(ShiftA1, InverseShift);
734 ShiftedB = Builder.CreateLShr(B, MaskedShift);
735 }
736
737 Value *Result = Builder.CreateOr(ShiftedA, ShiftedB);
738 return Result;
739}
740
741static Value *expandPowIntrinsic(CallInst *Orig, Intrinsic::ID IntrinsicId) {
742
743 Value *X = Orig->getOperand(0);
744 Value *Y = Orig->getOperand(1);
745 Type *Ty = X->getType();
746 IRBuilder<> Builder(Orig);
747
748 if (IntrinsicId == Intrinsic::powi)
749 Y = Builder.CreateSIToFP(Y, Ty);
750
751 Value *Log2Call =
752 Builder.CreateIntrinsic(Ty, Intrinsic::log2, {X}, nullptr, "elt.log2");
753 auto *Mul = Builder.CreateFMul(Log2Call, Y);
754 CallInst *Exp2Call = Builder.CreateIntrinsicWithoutFolding(
755 Ty, Intrinsic::exp2, {Mul}, nullptr, "elt.exp2");
756 Exp2Call->setTailCall(Orig->isTailCall());
757 Exp2Call->setAttributes(Orig->getAttributes());
758 return Exp2Call;
759}
760
761static bool expandBufferLoadIntrinsic(CallInst *Orig, bool IsRaw) {
762 IRBuilder<> Builder(Orig);
763
764 Type *BufferTy = Orig->getType()->getStructElementType(0);
765 Type *ScalarTy = BufferTy->getScalarType();
766 bool IsDouble = ScalarTy->isDoubleTy();
767 assert(IsDouble || ScalarTy->isIntegerTy(64) &&
768 "Only expand double or int64 scalars or vectors");
769 bool IsVector = false;
770 unsigned ExtractNum = 2;
771 if (auto *VT = dyn_cast<FixedVectorType>(BufferTy)) {
772 ExtractNum = 2 * VT->getNumElements();
773 IsVector = true;
774 assert(IsRaw || ExtractNum == 4 && "TypedBufferLoad vector must be size 2");
775 }
776
778 Value *Result = PoisonValue::get(BufferTy);
779 unsigned Base = 0;
780 // If we need to extract more than 4 i32; we need to break it up into
781 // more than one load. LoadNum tells us how many i32s we are loading in
782 // each load
783 while (ExtractNum > 0) {
784 unsigned LoadNum = std::min(ExtractNum, 4u);
785 Type *Ty = VectorType::get(Builder.getInt32Ty(), LoadNum, false);
786
787 Type *LoadType = StructType::get(Ty, Builder.getInt1Ty());
788 Intrinsic::ID LoadIntrinsic = Intrinsic::dx_resource_load_typedbuffer;
789 SmallVector<Value *, 3> Args = {Orig->getOperand(0), Orig->getOperand(1)};
790 if (IsRaw) {
791 LoadIntrinsic = Intrinsic::dx_resource_load_rawbuffer;
792 Value *Tmp = Builder.getInt32(4 * Base * 2);
793 Value *Offset = Orig->getOperand(2);
794 Args.push_back(Offset);
795 unsigned AddressArg = isa<PoisonValue>(Offset) ? 1 : 2;
796 if (Base != 0)
797 Args[AddressArg] = Builder.CreateAdd(Args[AddressArg], Tmp);
798 }
799
800 Value *Load = Builder.CreateIntrinsic(LoadType, LoadIntrinsic, Args);
801 Loads.push_back(Load);
802
803 // extract the buffer load's result
804 Value *Extract = Builder.CreateExtractValue(Load, {0});
805
806 SmallVector<Value *> ExtractElements;
807 for (unsigned I = 0; I < LoadNum; ++I)
808 ExtractElements.push_back(
809 Builder.CreateExtractElement(Extract, Builder.getInt32(I)));
810
811 // combine into double(s) or int64(s)
812 for (unsigned I = 0; I < LoadNum; I += 2) {
813 Value *Combined = nullptr;
814 if (IsDouble)
815 // For doubles, use dx_asdouble intrinsic
816 Combined = Builder.CreateIntrinsic(
817 Builder.getDoubleTy(), Intrinsic::dx_asdouble,
818 {ExtractElements[I], ExtractElements[I + 1]});
819 else {
820 // For int64, manually combine two int32s
821 // First, zero-extend both values to i64
822 Value *Lo =
823 Builder.CreateZExt(ExtractElements[I], Builder.getInt64Ty());
824 Value *Hi =
825 Builder.CreateZExt(ExtractElements[I + 1], Builder.getInt64Ty());
826 // Shift the high bits left by 32 bits
827 Value *ShiftedHi = Builder.CreateShl(Hi, Builder.getInt64(32));
828 // OR the high and low bits together
829 Combined = Builder.CreateOr(Lo, ShiftedHi);
830 }
831
832 if (IsVector)
833 Result = Builder.CreateInsertElement(Result, Combined,
834 Builder.getInt32((I / 2) + Base));
835 else
836 Result = Combined;
837 }
838
839 ExtractNum -= LoadNum;
840 Base += LoadNum / 2;
841 }
842
843 Value *CheckBit = nullptr;
844 for (User *U : make_early_inc_range(Orig->users())) {
845 // If it's not a ExtractValueInst, we don't know how to
846 // handle it
847 auto *EVI = dyn_cast<ExtractValueInst>(U);
848 if (!EVI)
849 llvm_unreachable("Unexpected user of typedbufferload");
850
851 ArrayRef<unsigned> Indices = EVI->getIndices();
852 assert(Indices.size() == 1);
853
854 if (Indices[0] == 0) {
855 // Use of the value(s)
856 EVI->replaceAllUsesWith(Result);
857 } else {
858 // Use of the check bit
859 assert(Indices[0] == 1 && "Unexpected type for typedbufferload");
860 // Note: This does not always match the historical behaviour of DXC.
861 // See https://github.com/microsoft/DirectXShaderCompiler/issues/7622
862 if (!CheckBit) {
863 SmallVector<Value *, 2> CheckBits;
864 for (Value *L : Loads)
865 CheckBits.push_back(Builder.CreateExtractValue(L, {1}));
866 CheckBit = Builder.CreateAnd(CheckBits);
867 }
868 EVI->replaceAllUsesWith(CheckBit);
869 }
870 EVI->eraseFromParent();
871 }
872 Orig->eraseFromParent();
873 return true;
874}
875
876static bool expandBufferStoreIntrinsic(CallInst *Orig, bool IsRaw) {
877 IRBuilder<> Builder(Orig);
878
879 unsigned ValIndex = IsRaw ? 3 : 2;
880 Type *BufferTy = Orig->getFunctionType()->getParamType(ValIndex);
881 Type *ScalarTy = BufferTy->getScalarType();
882 bool IsDouble = ScalarTy->isDoubleTy();
883 assert((IsDouble || ScalarTy->isIntegerTy(64)) &&
884 "Only expand double or int64 scalars or vectors");
885
886 // Determine if we're dealing with a vector or scalar
887 bool IsVector = false;
888 unsigned ExtractNum = 2;
889 unsigned VecLen = 0;
890 if (auto *VT = dyn_cast<FixedVectorType>(BufferTy)) {
891 VecLen = VT->getNumElements();
892 assert(IsRaw || VecLen == 2 && "TypedBufferStore vector must be size 2");
893 ExtractNum = VecLen * 2;
894 IsVector = true;
895 }
896
897 // Create the appropriate vector type for the result
898 Type *Int32Ty = Builder.getInt32Ty();
899 Type *ResultTy = VectorType::get(Int32Ty, ExtractNum, false);
900 Value *Val = PoisonValue::get(ResultTy);
901
902 Type *SplitElementTy = Int32Ty;
903 if (IsVector)
904 SplitElementTy = VectorType::get(SplitElementTy, VecLen, false);
905
906 Value *LowBits = nullptr;
907 Value *HighBits = nullptr;
908 // Split the 64-bit values into 32-bit components
909 if (IsDouble) {
910 auto *SplitTy = llvm::StructType::get(SplitElementTy, SplitElementTy);
911 Value *Split = Builder.CreateIntrinsic(SplitTy, Intrinsic::dx_splitdouble,
912 {Orig->getOperand(ValIndex)});
913 LowBits = Builder.CreateExtractValue(Split, 0);
914 HighBits = Builder.CreateExtractValue(Split, 1);
915 } else {
916 // Handle int64 type(s)
917 Value *InputVal = Orig->getOperand(ValIndex);
918 Constant *ShiftAmt = Builder.getInt64(32);
919 if (IsVector)
920 ShiftAmt =
922
923 // Split into low and high 32-bit parts
924 LowBits = Builder.CreateTrunc(InputVal, SplitElementTy);
925 Value *ShiftedVal = Builder.CreateLShr(InputVal, ShiftAmt);
926 HighBits = Builder.CreateTrunc(ShiftedVal, SplitElementTy);
927 }
928
929 if (IsVector) {
931 for (unsigned I = 0; I < VecLen; ++I) {
932 Mask.push_back(I);
933 Mask.push_back(I + VecLen);
934 }
935 Val = Builder.CreateShuffleVector(LowBits, HighBits, Mask);
936 } else {
937 Val = Builder.CreateInsertElement(Val, LowBits, Builder.getInt32(0));
938 Val = Builder.CreateInsertElement(Val, HighBits, Builder.getInt32(1));
939 }
940
941 // If we need to extract more than 4 i32; we need to break it up into
942 // more than one store. StoreNum tells us how many i32s we are storing in
943 // each store
944 unsigned Base = 0;
945 while (ExtractNum > 0) {
946 unsigned StoreNum = std::min(ExtractNum, 4u);
947
948 Intrinsic::ID StoreIntrinsic = Intrinsic::dx_resource_store_typedbuffer;
949 SmallVector<Value *, 4> Args = {Orig->getOperand(0), Orig->getOperand(1)};
950 if (IsRaw) {
951 StoreIntrinsic = Intrinsic::dx_resource_store_rawbuffer;
952 Value *Tmp = Builder.getInt32(4 * Base);
953 Value *Offset = Orig->getOperand(2);
954 Args.push_back(Offset);
955 unsigned AddressArg = isa<PoisonValue>(Offset) ? 1 : 2;
956 if (Base != 0)
957 Args[AddressArg] = Builder.CreateAdd(Args[AddressArg], Tmp);
958 }
959
961 for (unsigned I = 0; I < StoreNum; ++I) {
962 Mask.push_back(Base + I);
963 }
964
965 Value *SubVal = Val;
966 if (VecLen > 2)
967 SubVal = Builder.CreateShuffleVector(Val, Mask);
968
969 Args.push_back(SubVal);
970 // Create the final intrinsic call
971 Builder.CreateIntrinsic(Builder.getVoidTy(), StoreIntrinsic, Args);
972
973 ExtractNum -= StoreNum;
974 Base += StoreNum;
975 }
976 Orig->eraseFromParent();
977 return true;
978}
979
981 if (ClampIntrinsic == Intrinsic::dx_uclamp)
982 return Intrinsic::umax;
983 if (ClampIntrinsic == Intrinsic::dx_sclamp)
984 return Intrinsic::smax;
985 assert(ClampIntrinsic == Intrinsic::dx_nclamp);
986 return Intrinsic::maxnum;
987}
988
990 if (ClampIntrinsic == Intrinsic::dx_uclamp)
991 return Intrinsic::umin;
992 if (ClampIntrinsic == Intrinsic::dx_sclamp)
993 return Intrinsic::smin;
994 assert(ClampIntrinsic == Intrinsic::dx_nclamp);
995 return Intrinsic::minnum;
996}
997
999 Intrinsic::ID ClampIntrinsic) {
1000 Value *X = Orig->getOperand(0);
1001 Value *Min = Orig->getOperand(1);
1002 Value *Max = Orig->getOperand(2);
1003 Type *Ty = X->getType();
1004 IRBuilder<> Builder(Orig);
1005 auto *MaxCall = Builder.CreateIntrinsic(Ty, getMaxForClamp(ClampIntrinsic),
1006 {X, Min}, nullptr, "dx.max");
1007 return Builder.CreateIntrinsic(Ty, getMinForClamp(ClampIntrinsic),
1008 {MaxCall, Max}, nullptr, "dx.min");
1009}
1010
1012 Value *X = Orig->getOperand(0);
1013 Type *Ty = X->getType();
1014 Type *ScalarTy = Ty->getScalarType();
1015 Type *RetTy = Orig->getType();
1017
1018 IRBuilder<> Builder(Orig);
1019
1020 Value *GT;
1021 Value *LT;
1022 if (ScalarTy->isFloatingPointTy()) {
1023 GT = Builder.CreateFCmpOLT(Zero, X);
1024 LT = Builder.CreateFCmpOLT(X, Zero);
1025 } else {
1026 assert(ScalarTy->isIntegerTy());
1027 GT = Builder.CreateICmpSLT(Zero, X);
1028 LT = Builder.CreateICmpSLT(X, Zero);
1029 }
1030
1031 Value *ZextGT = Builder.CreateZExt(GT, RetTy);
1032 Value *ZextLT = Builder.CreateZExt(LT, RetTy);
1033
1034 return Builder.CreateSub(ZextGT, ZextLT);
1035}
1036
1037// Expand llvm.copysign by combining the sign bit with the magnitude bits using
1038// bitwise operations.
1040 Value *Magnitude = Orig->getOperand(0);
1041 Value *Sign = Orig->getOperand(1);
1042 Type *Ty = Orig->getType();
1043
1044 IRBuilder<> Builder(Orig);
1045
1046 bool IsDouble = Ty->getScalarType()->isDoubleTy();
1047 unsigned BitWidth = IsDouble ? 32 : Ty->getScalarSizeInBits();
1048 Type *IntTy = Ty->getWithNewType(Builder.getIntNTy(BitWidth));
1049
1050 auto CopySignBit = [&](Value *MagnitudeInt, Value *SignInt) {
1051 APInt SignMaskVal = APInt::getSignMask(BitWidth);
1052 // `ConstantInt::get` broadcasts to a splat when `IntTy` is a vector.
1053 Constant *SignMask = ConstantInt::get(IntTy, SignMaskVal);
1054 Constant *NotSignMask = ConstantInt::get(IntTy, ~SignMaskVal);
1055
1056 Value *MagnitudeBits = Builder.CreateAnd(MagnitudeInt, NotSignMask);
1057 Value *SignBits = Builder.CreateAnd(SignInt, SignMask);
1058 return Builder.CreateOr(MagnitudeBits, SignBits);
1059 };
1060
1061 // Avoid i64 bitwise ops, which require the Int64Ops shader feature.
1062 if (IsDouble) {
1063 auto *SplitTy = StructType::get(IntTy, IntTy);
1064 Value *MagnitudeHalves = Builder.CreateIntrinsic(
1065 SplitTy, Intrinsic::dx_splitdouble, {Magnitude});
1066 Value *SignHalves =
1067 Builder.CreateIntrinsic(SplitTy, Intrinsic::dx_splitdouble, {Sign});
1068 Value *MagnitudeLow = Builder.CreateExtractValue(MagnitudeHalves, 0);
1069 Value *MagnitudeHigh = Builder.CreateExtractValue(MagnitudeHalves, 1);
1070 Value *SignHigh = Builder.CreateExtractValue(SignHalves, 1);
1071
1072 Value *CombinedHigh = CopySignBit(MagnitudeHigh, SignHigh);
1073 return Builder.CreateIntrinsic(Ty, Intrinsic::dx_asdouble,
1074 {MagnitudeLow, CombinedHigh});
1075 }
1076
1077 Value *MagnitudeInt = Builder.CreateBitCast(Magnitude, IntTy);
1078 Value *SignInt = Builder.CreateBitCast(Sign, IntTy);
1079 Value *CombinedInt = CopySignBit(MagnitudeInt, SignInt);
1080 return Builder.CreateBitCast(CombinedInt, Ty);
1081}
1082
1083// Expand llvm.matrix.multiply by extracting row/column vectors and computing
1084// dot products.
1085// Result[r,c] = dot(row_r(LHS), col_c(RHS))
1086// Element (r,c) is at index c*NumRows + r (column-major).
1088 Value *LHS = Orig->getArgOperand(0);
1089 Value *RHS = Orig->getArgOperand(1);
1090 unsigned LHSRows = cast<ConstantInt>(Orig->getArgOperand(2))->getZExtValue();
1091 unsigned LHSCols = cast<ConstantInt>(Orig->getArgOperand(3))->getZExtValue();
1092 unsigned RHSCols = cast<ConstantInt>(Orig->getArgOperand(4))->getZExtValue();
1093
1094 auto *RetTy = cast<FixedVectorType>(Orig->getType());
1095 Type *EltTy = RetTy->getElementType();
1096 bool IsFP = EltTy->isFloatingPointTy();
1097
1098 IRBuilder<> Builder(Orig);
1099
1100 // Column-major indexing:
1101 // LHS row R, element K: index = K * LHSRows + R
1102 // RHS col C, element K: index = C * LHSCols + K
1103 Value *Result = PoisonValue::get(RetTy);
1104
1105 // Extract all scalar elements from LHS and RHS once, then reuse them.
1106 unsigned LHSSize = LHSRows * LHSCols;
1107 unsigned RHSSize = LHSCols * RHSCols;
1108 SmallVector<Value *, 16> LHSElts(LHSSize);
1109 SmallVector<Value *, 16> RHSElts(RHSSize);
1110 for (unsigned I = 0; I < LHSSize; ++I)
1111 LHSElts[I] = Builder.CreateExtractElement(LHS, I);
1112 for (unsigned I = 0; I < RHSSize; ++I)
1113 RHSElts[I] = Builder.CreateExtractElement(RHS, I);
1114
1115 // Choose the appropriate scalar-arg dot intrinsic for floats.
1116 // K=1 and double types use scalar expansion instead.
1118 bool UseScalarFP = IsFP && (EltTy->isDoubleTy() || LHSCols == 1);
1119 if (IsFP && !UseScalarFP) {
1120 switch (LHSCols) {
1121 case 2:
1122 FloatDotID = Intrinsic::dx_dot2;
1123 break;
1124 case 3:
1125 FloatDotID = Intrinsic::dx_dot3;
1126 break;
1127 case 4:
1128 FloatDotID = Intrinsic::dx_dot4;
1129 break;
1130 default:
1132 "Invalid matrix inner dimension for dot product: must be 2-4");
1133 return nullptr;
1134 }
1135 }
1136
1137 for (unsigned C = 0; C < RHSCols; ++C) {
1138 for (unsigned R = 0; R < LHSRows; ++R) {
1139 // Gather row R from LHS and column C from RHS.
1140 SmallVector<Value *, 4> RowElts, ColElts;
1141 for (unsigned K = 0; K < LHSCols; ++K) {
1142 RowElts.push_back(LHSElts[K * LHSRows + R]);
1143 ColElts.push_back(RHSElts[C * LHSCols + K]);
1144 }
1145
1146 Value *Dot;
1147 if (UseScalarFP) {
1148 // Scalar fmul+fmuladd expansion for double types and K=1.
1149 Dot = Builder.CreateFMul(RowElts[0], ColElts[0]);
1150 for (unsigned K = 1; K < LHSCols; ++K)
1151 Dot = Builder.CreateIntrinsic(EltTy, Intrinsic::fmuladd,
1152 {RowElts[K], ColElts[K], Dot});
1153 } else if (IsFP) {
1154 // Emit scalar-arg DXIL dot directly (dx.dot2/dx.dot3/dx.dot4).
1156 Args.append(RowElts.begin(), RowElts.end());
1157 Args.append(ColElts.begin(), ColElts.end());
1158 Dot = Builder.CreateIntrinsic(EltTy, FloatDotID, Args);
1159 } else {
1160 // Integer: emit multiply + imad chain.
1161 Dot = Builder.CreateMul(RowElts[0], ColElts[0]);
1162 for (unsigned K = 1; K < LHSCols; ++K)
1163 Dot = Builder.CreateIntrinsic(EltTy, Intrinsic::dx_imad,
1164 {RowElts[K], ColElts[K], Dot});
1165 }
1166 unsigned ResIdx = C * LHSRows + R;
1167 Result = Builder.CreateInsertElement(Result, Dot, ResIdx);
1168 }
1169 }
1170 return Result;
1171}
1172
1173// Expand llvm.matrix.transpose as a shufflevector that permutes elements
1174// from column-major source to column-major transposed layout.
1175// Element (r,c) at index c*Rows + r moves to index r*Cols + c.
1177 Value *Mat = Orig->getArgOperand(0);
1178 unsigned Rows = cast<ConstantInt>(Orig->getArgOperand(1))->getZExtValue();
1179 unsigned Cols = cast<ConstantInt>(Orig->getArgOperand(2))->getZExtValue();
1180
1181 unsigned NumElts = Rows * Cols;
1182 SmallVector<int, 16> Mask(NumElts);
1183 for (unsigned I = 0; I < NumElts; ++I)
1184 Mask[I] = (I % Cols) * Rows + (I / Cols);
1185
1186 IRBuilder<> Builder(Orig);
1187 return Builder.CreateShuffleVector(Mat, Mask);
1188}
1189
1190// Scalarize a vector int_dx_store_output call into per-component scalar calls.
1191// The DXIL StoreOutput op is per-component; vector intrinsics are split here
1192// so that DXILOpLowering sees only scalar variants.
1193static bool expandStoreOutput(CallInst *Orig) {
1194 auto *VT = dyn_cast<FixedVectorType>(Orig->getArgOperand(3)->getType());
1195 if (!VT)
1196 return false; // already scalar, nothing to expand
1197
1198 IRBuilder<> Builder(Orig);
1199 Module *M = Orig->getModule();
1200 Type *Int8Ty = Builder.getInt8Ty();
1201 Type *Int32Ty = Builder.getInt32Ty();
1202 Type *ScalarTy = VT->getElementType();
1203 unsigned NumElems = VT->getNumElements();
1204
1205 Value *SigElementId = Orig->getArgOperand(0);
1206 Value *RowIndex = Orig->getArgOperand(1);
1207 Value *StartCol = Orig->getArgOperand(2); // i8
1208 Value *Data = Orig->getArgOperand(3);
1209 Value *StartColI32 = Builder.CreateZExt(StartCol, Int32Ty);
1210
1212 M, Intrinsic::dx_store_output, {ScalarTy});
1213
1214 for (unsigned I = 0; I < NumElems; ++I) {
1215 Value *Scalar =
1216 Builder.CreateExtractElement(Data, ConstantInt::get(Int32Ty, I));
1217 Value *ColIdx =
1218 Builder.CreateAdd(StartColI32, ConstantInt::get(Int32Ty, I));
1219 Value *ColI8 = Builder.CreateTrunc(ColIdx, Int8Ty);
1220 Builder.CreateCall(ScalarFn, {SigElementId, RowIndex, ColI8, Scalar});
1221 }
1222
1223 Orig->eraseFromParent();
1224 return true;
1225}
1226
1227// Scalarize a vector int_dx_load_input call into per-component scalar calls
1228// and reassemble the vector. The DXIL LoadInput op is per-component.
1230 auto *VT = dyn_cast<FixedVectorType>(Orig->getType());
1231 if (!VT)
1232 return nullptr; // already scalar, nothing to expand
1233
1234 IRBuilder<> Builder(Orig);
1235 Module *M = Orig->getModule();
1236 Type *Int8Ty = Builder.getInt8Ty();
1237 Type *Int32Ty = Builder.getInt32Ty();
1238 Type *ScalarTy = VT->getElementType();
1239 unsigned NumElems = VT->getNumElements();
1240
1241 Value *SigElementId = Orig->getArgOperand(0);
1242 Value *RowIndex = Orig->getArgOperand(1);
1243 Value *StartCol = Orig->getArgOperand(2); // i8
1244 Value *GsVertexOrPrimIndex = Orig->getArgOperand(3);
1245 Value *StartColI32 = Builder.CreateZExt(StartCol, Int32Ty);
1246
1248 M, Intrinsic::dx_load_input, {ScalarTy});
1249
1250 Value *Vec = PoisonValue::get(VT);
1251 for (unsigned I = 0; I < NumElems; ++I) {
1252 Value *ColIdx =
1253 Builder.CreateAdd(StartColI32, ConstantInt::get(Int32Ty, I));
1254 Value *ColI8 = Builder.CreateTrunc(ColIdx, Int8Ty);
1255 Value *Scalar = Builder.CreateCall(
1256 ScalarFn, {SigElementId, RowIndex, ColI8, GsVertexOrPrimIndex});
1257 Vec =
1258 Builder.CreateInsertElement(Vec, Scalar, ConstantInt::get(Int32Ty, I));
1259 }
1260
1261 return Vec;
1262}
1263
1264static bool expandIntrinsic(Function &F, CallInst *Orig) {
1265 Value *Result = nullptr;
1266 Intrinsic::ID IntrinsicId = F.getIntrinsicID();
1267 switch (IntrinsicId) {
1268 case Intrinsic::abs:
1269 Result = expandAbs(Orig);
1270 break;
1271 case Intrinsic::assume:
1272 Orig->eraseFromParent();
1273 return true;
1274 case Intrinsic::atan2:
1275 Result = expandAtan2Intrinsic(Orig);
1276 break;
1277 case Intrinsic::copysign:
1278 Result = expandCopySignIntrinsic(Orig);
1279 break;
1280 case Intrinsic::fshl:
1281 Result = expandFunnelShiftIntrinsic<true>(Orig);
1282 break;
1283 case Intrinsic::fshr:
1284 Result = expandFunnelShiftIntrinsic<false>(Orig);
1285 break;
1286 case Intrinsic::exp:
1287 Result = expandExpIntrinsic(Orig);
1288 break;
1289 case Intrinsic::is_fpclass:
1290 Result = expandIsFPClass(Orig);
1291 break;
1292 case Intrinsic::log:
1293 Result = expandLogIntrinsic(Orig);
1294 break;
1295 case Intrinsic::log10:
1296 Result = expandLog10Intrinsic(Orig);
1297 break;
1298 case Intrinsic::pow:
1299 case Intrinsic::powi:
1300 Result = expandPowIntrinsic(Orig, IntrinsicId);
1301 break;
1302 case Intrinsic::vector_reduce_and:
1303 case Intrinsic::vector_reduce_or:
1304 Result = expandVecReduceAndOr(Orig, IntrinsicId);
1305 break;
1306 case Intrinsic::dx_uclamp:
1307 case Intrinsic::dx_sclamp:
1308 case Intrinsic::dx_nclamp:
1309 Result = expandClampIntrinsic(Orig, IntrinsicId);
1310 break;
1311 case Intrinsic::dx_isfinite:
1312 Result = expand16BitIsFinite(Orig);
1313 break;
1314 case Intrinsic::dx_isinf:
1315 Result = expand16BitIsInf(Orig);
1316 break;
1317 case Intrinsic::dx_isnan:
1318 Result = expand16BitIsNaN(Orig);
1319 break;
1320 case Intrinsic::dx_fdot:
1321 Result = expandFloatDotIntrinsic(Orig);
1322 break;
1323 case Intrinsic::dx_sdot:
1324 case Intrinsic::dx_udot:
1325 Result = expandIntegerDotIntrinsic(Orig, IntrinsicId);
1326 break;
1327 case Intrinsic::dx_sign:
1328 Result = expandSignIntrinsic(Orig);
1329 break;
1330 case Intrinsic::dx_load_input:
1331 Result = expandLoadInput(Orig);
1332 break;
1333 case Intrinsic::dx_store_output:
1334 if (expandStoreOutput(Orig))
1335 return true;
1336 break;
1337 case Intrinsic::dx_resource_load_rawbuffer:
1338 if (expandBufferLoadIntrinsic(Orig, /*IsRaw*/ true))
1339 return true;
1340 break;
1341 case Intrinsic::dx_resource_store_rawbuffer:
1342 if (expandBufferStoreIntrinsic(Orig, /*IsRaw*/ true))
1343 return true;
1344 break;
1345 case Intrinsic::dx_resource_load_typedbuffer:
1346 if (expandBufferLoadIntrinsic(Orig, /*IsRaw*/ false))
1347 return true;
1348 break;
1349 case Intrinsic::dx_resource_store_typedbuffer:
1350 if (expandBufferStoreIntrinsic(Orig, /*IsRaw*/ false))
1351 return true;
1352 break;
1353 case Intrinsic::usub_sat:
1354 Result = expandUsubSat(Orig);
1355 break;
1356 case Intrinsic::umul_with_overflow:
1357 case Intrinsic::smul_with_overflow:
1358 Result = expandMulWithOverflow(Orig, /*Signed=*/IntrinsicId ==
1359 Intrinsic::smul_with_overflow);
1360 break;
1361 case Intrinsic::vector_reduce_add:
1362 case Intrinsic::vector_reduce_fadd:
1363 Result = expandVecReduceAdd(Orig, IntrinsicId);
1364 break;
1365 case Intrinsic::matrix_multiply:
1366 Result = expandMatrixMultiply(Orig);
1367 break;
1368 case Intrinsic::matrix_transpose:
1369 Result = expandMatrixTranspose(Orig);
1370 break;
1371 }
1372 if (Result) {
1373 Orig->replaceAllUsesWith(Result);
1374 Orig->eraseFromParent();
1375 return true;
1376 }
1377 return false;
1378}
1379
1381 for (auto &F : make_early_inc_range(M.functions())) {
1382 if (!isIntrinsicExpansion(F))
1383 continue;
1384 bool IntrinsicExpanded = false;
1385 for (User *U : make_early_inc_range(F.users())) {
1386 auto *IntrinsicCall = dyn_cast<CallInst>(U);
1387 if (!IntrinsicCall)
1388 continue;
1389 IntrinsicExpanded = expandIntrinsic(F, IntrinsicCall);
1390 }
1391 if (F.user_empty() && IntrinsicExpanded)
1392 F.eraseFromParent();
1393 }
1394 return true;
1395}
1396
1403
1407
1409
1411 "DXIL Intrinsic Expansion", false, false)
1413 "DXIL Intrinsic Expansion", false, false)
1414
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
This file implements a class to represent arbitrary precision integral constant values and operations...
#define X(NUM, ENUM, NAME)
Definition ELF.h:665
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
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 * 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 * expandCopySignIntrinsic(CallInst *Orig)
static Value * expand16BitIsFinite(CallInst *Orig)
static Value * expandLoadInput(CallInst *Orig)
static Value * expandUsubSat(CallInst *Orig)
static Value * expandFloatDotIntrinsic(CallInst *Orig)
static bool expandStoreOutput(CallInst *Orig)
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 * expandIntegerDotIntrinsic(CallInst *Orig, Intrinsic::ID DotIntrinsic)
static Value * expandVecReduceAndOr(CallInst *Orig, Intrinsic::ID IntrinsicId)
static bool expandBufferStoreIntrinsic(CallInst *Orig, bool IsRaw)
static Value * expandLogIntrinsic(CallInst *Orig, float LogConstVal=numbers::ln2f)
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 bool shouldExpandFloatDotIntrinsic(Function &F)
static Value * expandFloatDotChunk(CallInst *Orig, Value *A, Value *B)
static Value * expandAbs(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
MachineInstr *& Lo
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.
Class for arbitrary precision integers.
Definition APInt.h:78
static APInt getSignMask(unsigned BitWidth)
Get the SignMask for a specific bit width.
Definition APInt.h:225
static APInt getLowBitsSet(unsigned numBits, unsigned loBitsSet)
Constructs an APInt value that has the bottom loBitsSet bits set.
Definition APInt.h:302
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:44
bool isNullValue() const
Return true if this is the value that would be returned by getNullValue.
Definition Constant.h:65
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:306
static LLVM_ABI FixedVectorType * get(Type *ElementType, unsigned NumElts)
Definition Type.cpp:843
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:2987
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.
iterator_range< user_iterator > users()
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:253
ModulePass(char &pid)
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
const Triple & getTargetTriple() const
Get the target triple which is a string describing the target host.
Definition Module.h:328
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:467
LLVM_ABI VersionTuple getOSVersion() const
Parse the version number from the OS name component of the triple, if present.
Definition Triple.cpp:1482
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:283
static LLVM_ABI IntegerType * getInt32Ty(LLVMContext &C)
Definition Type.cpp:299
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
Definition Type.cpp:297
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition Type.h:363
LLVM_ABI TypeSize getPrimitiveSizeInBits() const LLVM_READONLY
Return the basic size of this type if it is a primitive type.
Definition Type.cpp:187
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:298
bool isHalfTy() const
Return true if this is 'half', a 16-bit IEEE fp type.
Definition Type.h:144
LLVM_ABI Type * getWithNewType(Type *EltTy) const
Given vector type, change the element type, whilst keeping the old number of elements.
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:252
static LLVM_ABI IntegerType * getIntNTy(LLVMContext &C, unsigned N)
Definition Type.cpp:303
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:257
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:548
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.
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
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.
@ Offset
Definition DWP.cpp:577
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:649
constexpr bool isPowerOf2_32(uint32_t Value)
Return true if the argument is a power of two > 0.
Definition MathExtras.h:280
ModulePass * createDXILIntrinsicExpansionLegacyPass()
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
@ 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