LLVM 24.0.0git
DXILMemIntrinsics.cpp
Go to the documentation of this file.
1//===- DXILMemIntrinsics.cpp - Eliminate Memory Intrinsics ----------------===//
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#include "DirectX.h"
11#include "llvm/IR/IRBuilder.h"
13#include "llvm/IR/IntrinsicsDirectX.h"
14#include "llvm/IR/Module.h"
15
16#define DEBUG_TYPE "dxil-mem-intrinsics"
17
18using namespace llvm;
19
20void expandMemSet(MemSetInst *MemSet) {
21 IRBuilder<> Builder(MemSet);
22 Value *Dst = MemSet->getDest();
23 Value *Val = MemSet->getValue();
24 ConstantInt *LengthCI = dyn_cast<ConstantInt>(MemSet->getLength());
25 assert(LengthCI && "Expected length to be a ConstantInt");
26
27 [[maybe_unused]] const DataLayout &DL = Builder.getDataLayout();
28 [[maybe_unused]] uint64_t OrigLength = LengthCI->getZExtValue();
29
30 AllocaInst *Alloca = dyn_cast<AllocaInst>(Dst);
31
32 assert(Alloca && "Expected memset on an Alloca");
33 assert(OrigLength == Alloca->getAllocationSize(DL)->getFixedValue() &&
34 "Expected for memset size to match DataLayout size");
35
36 Type *AllocatedTy = Alloca->getAllocatedType();
37 ArrayType *ArrTy = dyn_cast<ArrayType>(AllocatedTy);
38 assert(ArrTy && "Expected Alloca for an Array Type");
39
40 Type *ElemTy = ArrTy->getElementType();
41 uint64_t Size = ArrTy->getArrayNumElements();
42
43 [[maybe_unused]] uint64_t ElemSize = DL.getTypeStoreSize(ElemTy);
44
45 assert(ElemSize > 0 && "Size must be set");
46 assert(OrigLength == ElemSize * Size && "Size in bytes must match");
47
48 Value *TypedVal = Val;
49
50 if (Val->getType() != ElemTy)
51 TypedVal = Builder.CreateIntCast(Val, ElemTy, false);
52
53 for (uint64_t I = 0; I < Size; ++I) {
54 Value *Zero = Builder.getInt32(0);
55 Value *Offset = Builder.getInt32(I);
56 Value *Ptr = GetElementPtrInst::Create(ArrTy, Dst, {Zero, Offset}, "gep",
57 MemSet->getIterator());
58 Builder.CreateStore(TypedVal, Ptr);
59 }
60
61 MemSet->eraseFromParent();
62}
63
64static Type *getPointeeType(Value *Ptr, const DataLayout &DL) {
65 if (auto *GV = dyn_cast<GlobalVariable>(Ptr))
66 return GV->getValueType();
67 if (auto *AI = dyn_cast<AllocaInst>(Ptr))
68 return AI->getAllocatedType();
69
70 if (auto *II = dyn_cast<IntrinsicInst>(Ptr)) {
71 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
72 Type *Ty = cast<dxil::AnyResourceExtType>(II->getArgOperand(0)->getType())
73 ->getResourceType();
74 assert(Ty && "getpointer used on untyped resource");
75 return Ty;
76 }
77 }
78
79 if (auto *GEP = dyn_cast<GEPOperator>(Ptr)) {
80 Type *Ty = GEP->getResultElementType();
81 if (!Ty->isIntegerTy(8))
82 return Ty;
83
84 // We have ptradd, so we have to hope there's enough information to work out
85 // what we're indexing.
86 Type *IndexedType = getPointeeType(GEP->getPointerOperand(), DL);
87 if (auto *AT = dyn_cast<ArrayType>(IndexedType))
88 return AT->getElementType();
89
90 if (auto *ST = dyn_cast<StructType>(IndexedType)) {
91 // Indexing a struct should always be constant
92 APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
93 [[maybe_unused]] bool IsConst =
94 GEP->accumulateConstantOffset(DL, ConstantOffset);
95 assert(IsConst && "Non-constant GEP into struct?");
96
97 // Now, work out what we'll find at that offset.
98 const StructLayout *Layout = DL.getStructLayout(ST);
99 unsigned Idx =
100 Layout->getElementContainingOffset(ConstantOffset.getZExtValue());
101
102 return ST->getTypeAtIndex(Idx);
103 }
104
105 llvm_unreachable("Could not infer type from GEP");
106 }
107
108 llvm_unreachable("Could not calculate pointee type");
109}
110
111static size_t flattenTypes(Type *ContainerTy, const DataLayout &DL,
112 SmallVectorImpl<std::pair<Type *, size_t>> &FlatTys,
113 size_t NextOffset = 0) {
114 if (auto *AT = dyn_cast<ArrayType>(ContainerTy)) {
115 for (uint64_t I = 0, E = AT->getNumElements(); I != E; ++I)
116 NextOffset = flattenTypes(AT->getElementType(), DL, FlatTys, NextOffset);
117 return NextOffset;
118 }
119 if (auto *ST = dyn_cast<StructType>(ContainerTy)) {
120 for (Type *Ty : ST->elements())
121 NextOffset = flattenTypes(Ty, DL, FlatTys, NextOffset);
122 return NextOffset;
123 }
124
125 FlatTys.emplace_back(ContainerTy, NextOffset);
126 return NextOffset + DL.getTypeStoreSize(ContainerTy);
127}
128
130 IRBuilder<> Builder(MemCpy);
131 Value *Dst = MemCpy->getDest();
132 Value *Src = MemCpy->getSource();
133 ConstantInt *LengthCI = dyn_cast<ConstantInt>(MemCpy->getLength());
134 assert(LengthCI && "Expected Length to be a ConstantInt");
135 assert(!MemCpy->isVolatile() && "Handling for volatile not implemented");
136
137 uint64_t ByteLength = LengthCI->getZExtValue();
138 // If length to copy is zero, no memcpy is needed.
139 if (ByteLength == 0)
140 return;
141
142 const DataLayout &DL = Builder.getDataLayout();
143
145 [[maybe_unused]] size_t MaxLength =
146 flattenTypes(getPointeeType(Dst, DL), DL, FlattenedTypes);
147 assert(MaxLength >= ByteLength && "Dst not large enough for memcpy");
148
149 LLVM_DEBUG({
150 // Check if Src is layout compatible with Dst. This should always be true
151 // unless the frontend did something wrong.
153 size_t SrcLength = flattenTypes(getPointeeType(Src, DL), DL, SrcTypes);
154 assert(SrcLength >= ByteLength && "Src not large enough for memcpy");
155 for (const auto &[LHS, RHS] : zip(FlattenedTypes, SrcTypes)) {
156 auto &[DstTy, DstOffset] = LHS;
157 auto &[SrcTy, SrcOffset] = RHS;
158 assert(DstTy == SrcTy && "Mismatched types for memcpy");
159 assert(DstOffset == SrcOffset && "Incompatible layouts for memcpy");
160 if (DstOffset >= ByteLength)
161 break;
162 }
163 });
164
165 for (const auto &[Ty, Offset] : FlattenedTypes) {
166 if (Offset >= ByteLength)
167 break;
168 // TODO: Should we skip padding types here?
169 Value *ByteOffset = Builder.getInt32(Offset);
170 Value *SrcPtr = Builder.CreateInBoundsPtrAdd(Src, ByteOffset);
171 Value *SrcVal = Builder.CreateLoad(Ty, SrcPtr);
172 Value *DstPtr = Builder.CreateInBoundsPtrAdd(Dst, ByteOffset);
173 Builder.CreateStore(SrcVal, DstPtr);
174 }
175
176 MemCpy->eraseFromParent();
177}
178
180 report_fatal_error("memmove expansion is not implemented yet.");
181}
182
184 bool HadMemIntrinsicUses = false;
185 for (auto &F : make_early_inc_range(M.functions())) {
186 Intrinsic::ID IID = F.getIntrinsicID();
187 switch (IID) {
188 case Intrinsic::memcpy:
189 case Intrinsic::memcpy_inline:
190 case Intrinsic::memmove:
191 case Intrinsic::memset:
192 case Intrinsic::memset_inline:
193 break;
194 default:
195 continue;
196 }
197 for (User *U : make_early_inc_range(F.users())) {
198 HadMemIntrinsicUses = true;
199 if (auto *MemSet = dyn_cast<MemSetInst>(U))
200 expandMemSet(MemSet);
201 else if (auto *MemCpy = dyn_cast<MemCpyInst>(U))
202 expandMemCpy(MemCpy);
203 else if (auto *MemMove = dyn_cast<MemMoveInst>(U))
204 expandMemMove(MemMove);
205 else
206 llvm_unreachable("Unhandled memory intrinsic");
207 }
208 assert(F.user_empty() && "Mem intrinsic not eliminated?");
209 F.eraseFromParent();
210 }
211 return HadMemIntrinsicUses;
212}
213
220
222public:
223 bool runOnModule(Module &M) override { return eliminateMemIntrinsics(M); }
225
226 static char ID; // Pass identification.
227};
229
231 "DXIL Memory Intrinsic Elimination", false, false)
233 "DXIL Memory Intrinsic Elimination", false, false)
234
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
uint64_t uint64_t
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static size_t flattenTypes(Type *ContainerTy, const DataLayout &DL, SmallVectorImpl< std::pair< Type *, size_t > > &FlatTys, size_t NextOffset=0)
static bool eliminateMemIntrinsics(Module &M)
void expandMemSet(MemSetInst *MemSet)
void expandMemCpy(MemCpyInst *MemCpy)
static Type * getPointeeType(Value *Ptr, const DataLayout &DL)
void expandMemMove(MemMoveInst *MemMove)
#define DEBUG_TYPE
Hexagon Common GEP
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
uint64_t IntrinsicInst * II
#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
#define LLVM_DEBUG(...)
Definition Debug.h:119
Value * RHS
Value * LHS
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
uint64_t getZExtValue() const
Get zero extended value.
Definition APInt.h:1560
an instruction to allocate memory on the stack
Type * getAllocatedType() const
Return the type that is being allocated by the instruction.
LLVM_ABI std::optional< TypeSize > getAllocationSize(const DataLayout &DL) const
Get allocation size in bytes.
This is the shared class of boolean and integer constants.
Definition Constants.h:87
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
Definition Constants.h:168
PreservedAnalyses run(Module &M, ModuleAnalysisManager &)
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
static GetElementPtrInst * Create(Type *PointeeType, Value *Ptr, ArrayRef< Value * > IdxList, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2947
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
This class wraps the llvm.memcpy intrinsic.
Value * getLength() const
Value * getDest() const
This is just like getRawDest, but it strips off any cast instructions (including addrspacecast) that ...
bool isVolatile() const
This class wraps the llvm.memmove intrinsic.
Value * getValue() const
This class wraps the llvm.memset and llvm.memset.inline intrinsics.
Value * getSource() const
This is just like getRawSource, but it strips off any cast instructions that feed it,...
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
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
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Used to lazily calculate structure layout information for a target machine, based on the DataLayout s...
Definition DataLayout.h:743
LLVM_ABI unsigned getElementContainingOffset(uint64_t FixedOffset) const
Given a valid byte offset into the structure, returns the structure index that contains it.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
self_iterator getIterator()
Definition ilist_node.h:123
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
detail::zippy< detail::zip_shortest, T, U, Args... > zip(T &&t, U &&u, Args &&...args)
zip iterator for two or more iteratable types.
Definition STLExtras.h:846
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
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
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
ModulePass * createDXILMemIntrinsicsLegacyPass()
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