LLVM 22.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 "DXILMemIntrinsics.h"
10#include "DirectX.h"
12#include "llvm/IR/IRBuilder.h"
14#include "llvm/IR/IntrinsicsDirectX.h"
15#include "llvm/IR/Module.h"
16
17#define DEBUG_TYPE "dxil-mem-intrinsics"
18
19using namespace llvm;
20
21void expandMemSet(MemSetInst *MemSet) {
22 IRBuilder<> Builder(MemSet);
23 Value *Dst = MemSet->getDest();
24 Value *Val = MemSet->getValue();
25 ConstantInt *LengthCI = dyn_cast<ConstantInt>(MemSet->getLength());
26 assert(LengthCI && "Expected length to be a ConstantInt");
27
28 [[maybe_unused]] const DataLayout &DL =
29 Builder.GetInsertBlock()->getModule()->getDataLayout();
30 [[maybe_unused]] uint64_t OrigLength = LengthCI->getZExtValue();
31
32 AllocaInst *Alloca = dyn_cast<AllocaInst>(Dst);
33
34 assert(Alloca && "Expected memset on an Alloca");
35 assert(OrigLength == Alloca->getAllocationSize(DL)->getFixedValue() &&
36 "Expected for memset size to match DataLayout size");
37
38 Type *AllocatedTy = Alloca->getAllocatedType();
39 ArrayType *ArrTy = dyn_cast<ArrayType>(AllocatedTy);
40 assert(ArrTy && "Expected Alloca for an Array Type");
41
42 Type *ElemTy = ArrTy->getElementType();
43 uint64_t Size = ArrTy->getArrayNumElements();
44
45 [[maybe_unused]] uint64_t ElemSize = DL.getTypeStoreSize(ElemTy);
46
47 assert(ElemSize > 0 && "Size must be set");
48 assert(OrigLength == ElemSize * Size && "Size in bytes must match");
49
50 Value *TypedVal = Val;
51
52 if (Val->getType() != ElemTy)
53 TypedVal = Builder.CreateIntCast(Val, ElemTy, false);
54
55 for (uint64_t I = 0; I < Size; ++I) {
56 Value *Zero = Builder.getInt32(0);
57 Value *Offset = Builder.getInt32(I);
58 Value *Ptr = Builder.CreateGEP(ArrTy, Dst, {Zero, Offset}, "gep");
59 Builder.CreateStore(TypedVal, Ptr);
60 }
61
62 MemSet->eraseFromParent();
63}
64
65static Type *getPointeeType(Value *Ptr, const DataLayout &DL) {
66 if (auto *GV = dyn_cast<GlobalVariable>(Ptr))
67 return GV->getValueType();
68 if (auto *AI = dyn_cast<AllocaInst>(Ptr))
69 return AI->getAllocatedType();
70
71 if (auto *II = dyn_cast<IntrinsicInst>(Ptr)) {
72 if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
73 Type *Ty = cast<dxil::AnyResourceExtType>(II->getArgOperand(0)->getType())
74 ->getResourceType();
75 assert(Ty && "getpointer used on untyped resource");
76 return Ty;
77 }
78 }
79
80 if (auto *GEP = dyn_cast<GEPOperator>(Ptr)) {
81 Type *Ty = GEP->getResultElementType();
82 if (!Ty->isIntegerTy(8))
83 return Ty;
84
85 // We have ptradd, so we have to hope there's enough information to work out
86 // what we're indexing.
87 Type *IndexedType = getPointeeType(GEP->getPointerOperand(), DL);
88 if (auto *AT = dyn_cast<ArrayType>(IndexedType))
89 return AT->getElementType();
90
91 if (auto *ST = dyn_cast<StructType>(IndexedType)) {
92 // Indexing a struct should always be constant
93 APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
94 [[maybe_unused]] bool IsConst =
95 GEP->accumulateConstantOffset(DL, ConstantOffset);
96 assert(IsConst && "Non-constant GEP into struct?");
97
98 // Now, work out what we'll find at that offset.
99 const StructLayout *Layout = DL.getStructLayout(ST);
100 unsigned Idx =
101 Layout->getElementContainingOffset(ConstantOffset.getZExtValue());
102
103 return ST->getTypeAtIndex(Idx);
104 }
105
106 llvm_unreachable("Could not infer type from GEP");
107 }
108
109 llvm_unreachable("Could not calculate pointee type");
110}
111
112static size_t flattenTypes(Type *ContainerTy, const DataLayout &DL,
113 SmallVectorImpl<std::pair<Type *, size_t>> &FlatTys,
114 size_t NextOffset = 0) {
115 if (auto *AT = dyn_cast<ArrayType>(ContainerTy)) {
116 for (uint64_t I = 0, E = AT->getNumElements(); I != E; ++I)
117 NextOffset = flattenTypes(AT->getElementType(), DL, FlatTys, NextOffset);
118 return NextOffset;
119 }
120 if (auto *ST = dyn_cast<StructType>(ContainerTy)) {
121 for (Type *Ty : ST->elements())
122 NextOffset = flattenTypes(Ty, DL, FlatTys, NextOffset);
123 return NextOffset;
124 }
125
126 FlatTys.emplace_back(ContainerTy, NextOffset);
127 return NextOffset + DL.getTypeStoreSize(ContainerTy);
128}
129
131 IRBuilder<> Builder(MemCpy);
132 Value *Dst = MemCpy->getDest();
133 Value *Src = MemCpy->getSource();
134 ConstantInt *LengthCI = dyn_cast<ConstantInt>(MemCpy->getLength());
135 assert(LengthCI && "Expected Length to be a ConstantInt");
136 assert(!MemCpy->isVolatile() && "Handling for volatile not implemented");
137
138 uint64_t ByteLength = LengthCI->getZExtValue();
139 // If length to copy is zero, no memcpy is needed.
140 if (ByteLength == 0)
141 return;
142
143 const DataLayout &DL = Builder.GetInsertBlock()->getModule()->getDataLayout();
144
146 [[maybe_unused]] size_t MaxLength =
147 flattenTypes(getPointeeType(Dst, DL), DL, FlattenedTypes);
148 assert(MaxLength >= ByteLength && "Dst not large enough for memcpy");
149
150 LLVM_DEBUG({
151 // Check if Src is layout compatible with Dst. This should always be true
152 // unless the frontend did something wrong.
154 size_t SrcLength = flattenTypes(getPointeeType(Src, DL), DL, SrcTypes);
155 assert(SrcLength >= ByteLength && "Src not large enough for memcpy");
156 for (const auto &[LHS, RHS] : zip(FlattenedTypes, SrcTypes)) {
157 auto &[DstTy, DstOffset] = LHS;
158 auto &[SrcTy, SrcOffset] = RHS;
159 assert(DstTy == SrcTy && "Mismatched types for memcpy");
160 assert(DstOffset == SrcOffset && "Incompatible layouts for memcpy");
161 if (DstOffset >= ByteLength)
162 break;
163 }
164 });
165
166 for (const auto &[Ty, Offset] : FlattenedTypes) {
167 if (Offset >= ByteLength)
168 break;
169 // TODO: Should we skip padding types here?
170 Type *Int8Ty = Builder.getInt8Ty();
171 Value *ByteOffset = Builder.getInt32(Offset);
172 Value *SrcPtr = Builder.CreateInBoundsGEP(Int8Ty, Src, ByteOffset);
173 Value *SrcVal = Builder.CreateLoad(Ty, SrcPtr);
174 Value *DstPtr = Builder.CreateInBoundsGEP(Int8Ty, Dst, ByteOffset);
175 Builder.CreateStore(SrcVal, DstPtr);
176 }
177
178 MemCpy->eraseFromParent();
179}
180
182 report_fatal_error("memmove expansion is not implemented yet.");
183}
184
186 bool HadMemIntrinsicUses = false;
187 for (auto &F : make_early_inc_range(M.functions())) {
188 Intrinsic::ID IID = F.getIntrinsicID();
189 switch (IID) {
190 case Intrinsic::memcpy:
191 case Intrinsic::memcpy_inline:
192 case Intrinsic::memmove:
193 case Intrinsic::memset:
194 case Intrinsic::memset_inline:
195 break;
196 default:
197 continue;
198 }
199 for (User *U : make_early_inc_range(F.users())) {
200 HadMemIntrinsicUses = true;
201 if (auto *MemSet = dyn_cast<MemSetInst>(U))
202 expandMemSet(MemSet);
203 else if (auto *MemCpy = dyn_cast<MemCpyInst>(U))
204 expandMemCpy(MemCpy);
205 else if (auto *MemMove = dyn_cast<MemMoveInst>(U))
206 expandMemMove(MemMove);
207 else
208 llvm_unreachable("Unhandled memory intrinsic");
209 }
210 assert(F.user_empty() && "Mem intrinsic not eliminated?");
211 F.eraseFromParent();
212 }
213 return HadMemIntrinsicUses;
214}
215
221
223public:
224 bool runOnModule(Module &M) override { return eliminateMemIntrinsics(M); }
226
227 static char ID; // Pass identification.
228};
230
232 "DXIL Memory Intrinsic Elimination", false, false)
234 "DXIL Memory Intrinsic Elimination", false, false)
235
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
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:114
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:1541
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:171
PreservedAnalyses run(Module &M, ModuleAnalysisManager &)
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2788
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: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
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:723
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:45
static LLVM_ABI IntegerType * getInt8Ty(LLVMContext &C)
Definition Type.cpp:294
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:256
#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:532
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:829
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:632
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:167
ModulePass * createDXILMemIntrinsicsLegacyPass()
Pass to transform all llvm memory intrinsics to explicit loads and stores.
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