LLVM 19.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/STLExtras.h"
17#include "llvm/CodeGen/Passes.h"
18#include "llvm/IR/IRBuilder.h"
19#include "llvm/IR/Instruction.h"
21#include "llvm/IR/Intrinsics.h"
22#include "llvm/IR/IntrinsicsDirectX.h"
23#include "llvm/IR/Module.h"
24#include "llvm/IR/PassManager.h"
25#include "llvm/IR/Type.h"
26#include "llvm/Pass.h"
29
30#define DEBUG_TYPE "dxil-intrinsic-expansion"
31
32using namespace llvm;
33
35 switch (F.getIntrinsicID()) {
36 case Intrinsic::abs:
37 case Intrinsic::exp:
38 case Intrinsic::log:
39 case Intrinsic::log10:
40 case Intrinsic::pow:
41 case Intrinsic::dx_any:
42 case Intrinsic::dx_clamp:
43 case Intrinsic::dx_uclamp:
44 case Intrinsic::dx_lerp:
45 case Intrinsic::dx_sdot:
46 case Intrinsic::dx_udot:
47 return true;
48 }
49 return false;
50}
51
52static bool expandAbs(CallInst *Orig) {
53 Value *X = Orig->getOperand(0);
54 IRBuilder<> Builder(Orig->getParent());
55 Builder.SetInsertPoint(Orig);
56 Type *Ty = X->getType();
57 Type *EltTy = Ty->getScalarType();
58 Constant *Zero = Ty->isVectorTy()
61 cast<FixedVectorType>(Ty)->getNumElements()),
62 ConstantInt::get(EltTy, 0))
63 : ConstantInt::get(EltTy, 0);
64 auto *V = Builder.CreateSub(Zero, X);
65 auto *MaxCall =
66 Builder.CreateIntrinsic(Ty, Intrinsic::smax, {X, V}, nullptr, "dx.max");
67 Orig->replaceAllUsesWith(MaxCall);
68 Orig->eraseFromParent();
69 return true;
70}
71
72static bool expandIntegerDot(CallInst *Orig, Intrinsic::ID DotIntrinsic) {
73 assert(DotIntrinsic == Intrinsic::dx_sdot ||
74 DotIntrinsic == Intrinsic::dx_udot);
75 Intrinsic::ID MadIntrinsic = DotIntrinsic == Intrinsic::dx_sdot
76 ? Intrinsic::dx_imad
77 : Intrinsic::dx_umad;
78 Value *A = Orig->getOperand(0);
79 Value *B = Orig->getOperand(1);
80 Type *ATy = A->getType();
81 Type *BTy = B->getType();
82 assert(ATy->isVectorTy() && BTy->isVectorTy());
83
84 IRBuilder<> Builder(Orig->getParent());
85 Builder.SetInsertPoint(Orig);
86
87 auto *AVec = dyn_cast<FixedVectorType>(A->getType());
88 Value *Elt0 = Builder.CreateExtractElement(A, (uint64_t)0);
89 Value *Elt1 = Builder.CreateExtractElement(B, (uint64_t)0);
90 Value *Result = Builder.CreateMul(Elt0, Elt1);
91 for (unsigned I = 1; I < AVec->getNumElements(); I++) {
92 Elt0 = Builder.CreateExtractElement(A, I);
93 Elt1 = Builder.CreateExtractElement(B, I);
94 Result = Builder.CreateIntrinsic(Result->getType(), MadIntrinsic,
95 ArrayRef<Value *>{Elt0, Elt1, Result},
96 nullptr, "dx.mad");
97 }
98 Orig->replaceAllUsesWith(Result);
99 Orig->eraseFromParent();
100 return true;
101}
102
103static bool expandExpIntrinsic(CallInst *Orig) {
104 Value *X = Orig->getOperand(0);
105 IRBuilder<> Builder(Orig->getParent());
106 Builder.SetInsertPoint(Orig);
107 Type *Ty = X->getType();
108 Type *EltTy = Ty->getScalarType();
109 Constant *Log2eConst =
112 cast<FixedVectorType>(Ty)->getNumElements()),
113 ConstantFP::get(EltTy, numbers::log2ef))
114 : ConstantFP::get(EltTy, numbers::log2ef);
115 Value *NewX = Builder.CreateFMul(Log2eConst, X);
116 auto *Exp2Call =
117 Builder.CreateIntrinsic(Ty, Intrinsic::exp2, {NewX}, nullptr, "dx.exp2");
118 Exp2Call->setTailCall(Orig->isTailCall());
119 Exp2Call->setAttributes(Orig->getAttributes());
120 Orig->replaceAllUsesWith(Exp2Call);
121 Orig->eraseFromParent();
122 return true;
123}
124
125static bool expandAnyIntrinsic(CallInst *Orig) {
126 Value *X = Orig->getOperand(0);
127 IRBuilder<> Builder(Orig->getParent());
128 Builder.SetInsertPoint(Orig);
129 Type *Ty = X->getType();
130 Type *EltTy = Ty->getScalarType();
131
132 if (!Ty->isVectorTy()) {
133 Value *Cond = EltTy->isFloatingPointTy()
134 ? Builder.CreateFCmpUNE(X, ConstantFP::get(EltTy, 0))
135 : Builder.CreateICmpNE(X, ConstantInt::get(EltTy, 0));
137 } else {
138 auto *XVec = dyn_cast<FixedVectorType>(Ty);
139 Value *Cond =
140 EltTy->isFloatingPointTy()
141 ? Builder.CreateFCmpUNE(
143 ElementCount::getFixed(XVec->getNumElements()),
144 ConstantFP::get(EltTy, 0)))
145 : Builder.CreateICmpNE(
147 ElementCount::getFixed(XVec->getNumElements()),
148 ConstantInt::get(EltTy, 0)));
149 Value *Result = Builder.CreateExtractElement(Cond, (uint64_t)0);
150 for (unsigned I = 1; I < XVec->getNumElements(); I++) {
151 Value *Elt = Builder.CreateExtractElement(Cond, I);
152 Result = Builder.CreateOr(Result, Elt);
153 }
154 Orig->replaceAllUsesWith(Result);
155 }
156 Orig->eraseFromParent();
157 return true;
158}
159
160static bool expandLerpIntrinsic(CallInst *Orig) {
161 Value *X = Orig->getOperand(0);
162 Value *Y = Orig->getOperand(1);
163 Value *S = Orig->getOperand(2);
164 IRBuilder<> Builder(Orig->getParent());
165 Builder.SetInsertPoint(Orig);
166 auto *V = Builder.CreateFSub(Y, X);
167 V = Builder.CreateFMul(S, V);
168 auto *Result = Builder.CreateFAdd(X, V, "dx.lerp");
169 Orig->replaceAllUsesWith(Result);
170 Orig->eraseFromParent();
171 return true;
172}
173
174static bool expandLogIntrinsic(CallInst *Orig,
175 float LogConstVal = numbers::ln2f) {
176 Value *X = Orig->getOperand(0);
177 IRBuilder<> Builder(Orig->getParent());
178 Builder.SetInsertPoint(Orig);
179 Type *Ty = X->getType();
180 Type *EltTy = Ty->getScalarType();
181 Constant *Ln2Const =
184 cast<FixedVectorType>(Ty)->getNumElements()),
185 ConstantFP::get(EltTy, LogConstVal))
186 : ConstantFP::get(EltTy, LogConstVal);
187 auto *Log2Call =
188 Builder.CreateIntrinsic(Ty, Intrinsic::log2, {X}, nullptr, "elt.log2");
189 Log2Call->setTailCall(Orig->isTailCall());
190 Log2Call->setAttributes(Orig->getAttributes());
191 auto *Result = Builder.CreateFMul(Ln2Const, Log2Call);
192 Orig->replaceAllUsesWith(Result);
193 Orig->eraseFromParent();
194 return true;
195}
196static bool expandLog10Intrinsic(CallInst *Orig) {
198}
199
200static bool expandPowIntrinsic(CallInst *Orig) {
201
202 Value *X = Orig->getOperand(0);
203 Value *Y = Orig->getOperand(1);
204 Type *Ty = X->getType();
205 IRBuilder<> Builder(Orig->getParent());
206 Builder.SetInsertPoint(Orig);
207
208 auto *Log2Call =
209 Builder.CreateIntrinsic(Ty, Intrinsic::log2, {X}, nullptr, "elt.log2");
210 auto *Mul = Builder.CreateFMul(Log2Call, Y);
211 auto *Exp2Call =
212 Builder.CreateIntrinsic(Ty, Intrinsic::exp2, {Mul}, nullptr, "elt.exp2");
213 Exp2Call->setTailCall(Orig->isTailCall());
214 Exp2Call->setAttributes(Orig->getAttributes());
215 Orig->replaceAllUsesWith(Exp2Call);
216 Orig->eraseFromParent();
217 return true;
218}
219
221 Intrinsic::ID ClampIntrinsic) {
222 if (ClampIntrinsic == Intrinsic::dx_uclamp)
223 return Intrinsic::umax;
224 assert(ClampIntrinsic == Intrinsic::dx_clamp);
225 if (ElemTy->isVectorTy())
226 ElemTy = ElemTy->getScalarType();
227 if (ElemTy->isIntegerTy())
228 return Intrinsic::smax;
229 assert(ElemTy->isFloatingPointTy());
230 return Intrinsic::maxnum;
231}
232
234 Intrinsic::ID ClampIntrinsic) {
235 if (ClampIntrinsic == Intrinsic::dx_uclamp)
236 return Intrinsic::umin;
237 assert(ClampIntrinsic == Intrinsic::dx_clamp);
238 if (ElemTy->isVectorTy())
239 ElemTy = ElemTy->getScalarType();
240 if (ElemTy->isIntegerTy())
241 return Intrinsic::smin;
242 assert(ElemTy->isFloatingPointTy());
243 return Intrinsic::minnum;
244}
245
246static bool expandClampIntrinsic(CallInst *Orig, Intrinsic::ID ClampIntrinsic) {
247 Value *X = Orig->getOperand(0);
248 Value *Min = Orig->getOperand(1);
249 Value *Max = Orig->getOperand(2);
250 Type *Ty = X->getType();
251 IRBuilder<> Builder(Orig->getParent());
252 Builder.SetInsertPoint(Orig);
253 auto *MaxCall = Builder.CreateIntrinsic(
254 Ty, getMaxForClamp(Ty, ClampIntrinsic), {X, Min}, nullptr, "dx.max");
255 auto *MinCall =
256 Builder.CreateIntrinsic(Ty, getMinForClamp(Ty, ClampIntrinsic),
257 {MaxCall, Max}, nullptr, "dx.min");
258
259 Orig->replaceAllUsesWith(MinCall);
260 Orig->eraseFromParent();
261 return true;
262}
263
264static bool expandIntrinsic(Function &F, CallInst *Orig) {
265 switch (F.getIntrinsicID()) {
266 case Intrinsic::abs:
267 return expandAbs(Orig);
268 case Intrinsic::exp:
269 return expandExpIntrinsic(Orig);
270 case Intrinsic::log:
271 return expandLogIntrinsic(Orig);
272 case Intrinsic::log10:
273 return expandLog10Intrinsic(Orig);
274 case Intrinsic::pow:
275 return expandPowIntrinsic(Orig);
276 case Intrinsic::dx_any:
277 return expandAnyIntrinsic(Orig);
278 case Intrinsic::dx_uclamp:
279 case Intrinsic::dx_clamp:
280 return expandClampIntrinsic(Orig, F.getIntrinsicID());
281 case Intrinsic::dx_lerp:
282 return expandLerpIntrinsic(Orig);
283 case Intrinsic::dx_sdot:
284 case Intrinsic::dx_udot:
285 return expandIntegerDot(Orig, F.getIntrinsicID());
286 }
287 return false;
288}
289
291 for (auto &F : make_early_inc_range(M.functions())) {
293 continue;
294 bool IntrinsicExpanded = false;
295 for (User *U : make_early_inc_range(F.users())) {
296 auto *IntrinsicCall = dyn_cast<CallInst>(U);
297 if (!IntrinsicCall)
298 continue;
299 IntrinsicExpanded = expandIntrinsic(F, IntrinsicCall);
300 }
301 if (F.user_empty() && IntrinsicExpanded)
302 F.eraseFromParent();
303 }
304 return true;
305}
306
309 if (expansionIntrinsics(M))
311 return PreservedAnalyses::all();
312}
313
315 return expansionIntrinsics(M);
316}
317
319
321 "DXIL Intrinsic Expansion", false, false)
323 "DXIL Intrinsic Expansion", false, false)
324
326 return new DXILIntrinsicExpansionLegacy();
327}
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
DXIL Intrinsic Expansion
static bool expandIntrinsic(Function &F, CallInst *Orig)
static bool expandLog10Intrinsic(CallInst *Orig)
static bool expansionIntrinsics(Module &M)
static bool expandAbs(CallInst *Orig)
static bool expandExpIntrinsic(CallInst *Orig)
static bool expandPowIntrinsic(CallInst *Orig)
static bool expandAnyIntrinsic(CallInst *Orig)
static bool expandIntegerDot(CallInst *Orig, Intrinsic::ID DotIntrinsic)
static bool expandLogIntrinsic(CallInst *Orig, float LogConstVal=numbers::ln2f)
static Intrinsic::ID getMaxForClamp(Type *ElemTy, Intrinsic::ID ClampIntrinsic)
static Intrinsic::ID getMinForClamp(Type *ElemTy, Intrinsic::ID ClampIntrinsic)
static bool isIntrinsicExpansion(Function &F)
static bool expandClampIntrinsic(CallInst *Orig, Intrinsic::ID ClampIntrinsic)
static bool expandLerpIntrinsic(CallInst *Orig)
static GCMetadataPrinterRegistry::Add< ErlangGCPrinter > X("erlang", "erlang-compatible garbage collector")
#define DEBUG_TYPE
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
Module.h This file contains the declarations for the Module class.
static GCMetadataPrinterRegistry::Add< OcamlGCMetadataPrinter > Y("ocaml", "ocaml 3.10-compatible collector")
This header defines various interfaces for pass management in LLVM.
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:59
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:52
const SmallVectorImpl< MachineOperand > & Cond
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
BinaryOperator * Mul
A container for analyses that lazily runs them and caches their results.
Definition: PassManager.h:321
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
AttributeList getAttributes() const
Return the parameter attributes for this call.
Definition: InstrTypes.h:1819
This class represents a function call, abstracting a target machine's calling convention.
bool isTailCall() const
void setTailCall(bool IsTc=true)
static Constant * getSplat(ElementCount EC, Constant *Elt)
Return a ConstantVector with the specified constant in each element.
Definition: Constants.cpp:1449
This is an important base class in LLVM.
Definition: Constant.h:41
bool runOnModule(Module &M) override
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
PreservedAnalyses run(Module &M, ModuleAnalysisManager &)
static constexpr ElementCount getFixed(ScalarTy MinVal)
Definition: TypeSize.h:296
Value * CreateFSub(Value *L, Value *R, const Twine &Name="", MDNode *FPMD=nullptr)
Definition: IRBuilder.h:1560
Value * CreateExtractElement(Value *Vec, Value *Idx, const Twine &Name="")
Definition: IRBuilder.h:2460
Value * CreateFAdd(Value *L, Value *R, const Twine &Name="", MDNode *FPMD=nullptr)
Definition: IRBuilder.h:1533
CallInst * CreateIntrinsic(Intrinsic::ID ID, ArrayRef< Type * > Types, ArrayRef< Value * > Args, Instruction *FMFSource=nullptr, const Twine &Name="")
Create a call to intrinsic ID with Args, mangled using Types.
Definition: IRBuilder.cpp:932
Value * CreateFCmpUNE(Value *LHS, Value *RHS, const Twine &Name="", MDNode *FPMathTag=nullptr)
Definition: IRBuilder.h:2346
Value * CreateICmpNE(Value *LHS, Value *RHS, const Twine &Name="")
Definition: IRBuilder.h:2245
Value * CreateSub(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition: IRBuilder.h:1344
Value * CreateOr(Value *LHS, Value *RHS, const Twine &Name="")
Definition: IRBuilder.h:1497
void SetInsertPoint(BasicBlock *TheBB)
This specifies that created instructions should be appended to the end of the specified block.
Definition: IRBuilder.h:180
Value * CreateFMul(Value *L, Value *R, const Twine &Name="", MDNode *FPMD=nullptr)
Definition: IRBuilder.h:1587
Value * CreateMul(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition: IRBuilder.h:1361
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition: IRBuilder.h:2666
const BasicBlock * getParent() const
Definition: Instruction.h:152
InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition: Pass.h:251
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
A set of analyses that are preserved following a run of a transformation pass.
Definition: Analysis.h:109
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition: Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition: Analysis.h:115
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
bool isVectorTy() const
True if this is an instance of VectorType.
Definition: Type.h:265
bool isFloatingPointTy() const
Return true if this is one of the floating-point types.
Definition: Type.h:185
bool isIntegerTy() const
True if this is an instance of IntegerType.
Definition: Type.h:228
Type * getScalarType() const
If this is a vector type, return the element type, otherwise return 'this'.
Definition: Type.h:348
Value * getOperand(unsigned i) const
Definition: User.h:169
LLVM Value Representation.
Definition: Value.h:74
void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition: Value.cpp:534
constexpr float ln10f
Definition: MathExtras.h:49
constexpr float log2ef
Definition: MathExtras.h:50
constexpr float ln2f
Definition: MathExtras.h:48
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
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:656
ModulePass * createDXILIntrinsicExpansionLegacyPass()
Pass to expand intrinsic operations that lack DXIL opCodes.