1//===- Target/DirectX/PointerTypeAnalisis.cpp - PointerType analysis ------===//
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// Analysis pass to assign types to opaque pointers.
10//
11//===----------------------------------------------------------------------===//
12
13#include "PointerTypeAnalysis.h"
14#include "llvm/IR/Constants.h"
15#include "llvm/IR/GlobalVariable.h"
16#include "llvm/IR/Instructions.h"
17#include "llvm/IR/Module.h"
18#include "llvm/IR/Operator.h"
19
20using namespace llvm;
21using namespace llvm::dxil;
22
23namespace {
24
25Type *classifyFunctionType(const Function &F, PointerTypeMap &Map);
26
27// Classifies the type of the value passed in by walking the value's users to
28// find a typed instruction to materialize a type from.
29Type *classifyPointerType(const Value *V, PointerTypeMap &Map) {
30 assert(V->getType()->isPointerTy() &&
31 "classifyPointerType called with non-pointer");
32
33 // A CallInst will trigger this case, and we want to classify its Function
34 // operand as a Function rather than a generic Value.
35 if (const Function *F = dyn_cast<Function>(Val: V))
36 return classifyFunctionType(F: *F, Map);
37
38 // There can potentially be dead constants hanging off of the globals we do
39 // not want to deal with. So we remove them here.
40 if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Val: V))
41 GV->removeDeadConstantUsers();
42
43 auto It = Map.find(Val: V);
44 if (It != Map.end())
45 return It->second;
46
47 Type *PointeeTy = nullptr;
48 if (auto *GEP = dyn_cast<GEPOperator>(Val: V)) {
49 if (!GEP->getResultElementType()->isPointerTy())
50 PointeeTy = GEP->getResultElementType();
51 } else if (auto *Inst = dyn_cast<AllocaInst>(Val: V)) {
52 PointeeTy = Inst->getAllocatedType();
53 } else if (auto *GV = dyn_cast<GlobalVariable>(Val: V)) {
54 PointeeTy = GV->getValueType();
55 }
56
57 for (const auto *User : V->users()) {
58 Type *NewPointeeTy = nullptr;
59 if (const auto *Inst = dyn_cast<LoadInst>(Val: User)) {
60 NewPointeeTy = Inst->getType();
61 } else if (const auto *Inst = dyn_cast<StoreInst>(Val: User)) {
62 NewPointeeTy = Inst->getValueOperand()->getType();
63 // When store value is ptr type, cannot get more type info.
64 if (NewPointeeTy->isPointerTy())
65 continue;
66 } else if (const auto *GEP = dyn_cast<GEPOperator>(Val: User)) {
67 NewPointeeTy = GEP->getSourceElementType();
68 }
69 if (NewPointeeTy) {
70 // HLSL doesn't support pointers, so it is unlikely to get more than one
71 // or two levels of indirection in the IR. Because of this, recursion is
72 // pretty safe.
73 if (NewPointeeTy->isPointerTy()) {
74 PointeeTy = classifyPointerType(V: User, Map);
75 break;
76 }
77 if (!PointeeTy)
78 PointeeTy = NewPointeeTy;
79 else if (PointeeTy != NewPointeeTy)
80 PointeeTy = Type::getInt8Ty(C&: V->getContext());
81 }
82 }
83 // If we were unable to determine the pointee type, set to i8
84 // If we were able to determine the pointee type as ptr, set to i8*
85 if (!PointeeTy)
86 PointeeTy = Type::getInt8Ty(C&: V->getContext());
87 if (PointeeTy->isPointerTy())
88 PointeeTy = TypedPointerType::get(ElementType: Type::getInt8Ty(C&: V->getContext()),
89 AddressSpace: PointeeTy->getPointerAddressSpace());
90 auto *TypedPtrTy =
91 TypedPointerType::get(ElementType: PointeeTy, AddressSpace: V->getType()->getPointerAddressSpace());
92
93 Map[V] = TypedPtrTy;
94 return TypedPtrTy;
95}
96
97// This function constructs a function type accepting typed pointers. It only
98// handles function arguments and return types, and assigns the function type to
99// the function's value in the type map.
100Type *classifyFunctionType(const Function &F, PointerTypeMap &Map) {
101 auto It = Map.find(Val: &F);
102 if (It != Map.end())
103 return It->second;
104
105 SmallVector<Type *, 8> NewArgs;
106 Type *RetTy = F.getReturnType();
107 LLVMContext &Ctx = F.getContext();
108 if (RetTy->isPointerTy()) {
109 RetTy = nullptr;
110 for (const auto &B : F) {
111 const auto *RetInst = dyn_cast_or_null<ReturnInst>(Val: B.getTerminator());
112 if (!RetInst)
113 continue;
114
115 Type *NewRetTy = classifyPointerType(V: RetInst->getReturnValue(), Map);
116 if (!RetTy)
117 RetTy = NewRetTy;
118 else if (RetTy != NewRetTy)
119 RetTy = TypedPointerType::get(
120 ElementType: Type::getInt8Ty(C&: Ctx), AddressSpace: F.getReturnType()->getPointerAddressSpace());
121 }
122 // For function decl.
123 if (!RetTy)
124 RetTy = TypedPointerType::get(
125 ElementType: Type::getInt8Ty(C&: Ctx), AddressSpace: F.getReturnType()->getPointerAddressSpace());
126 }
127 for (auto &A : F.args()) {
128 Type *ArgTy = A.getType();
129 if (ArgTy->isPointerTy())
130 ArgTy = classifyPointerType(V: &A, Map);
131 NewArgs.push_back(Elt: ArgTy);
132 }
133 auto *TypedPtrTy =
134 TypedPointerType::get(ElementType: FunctionType::get(Result: RetTy, Params: NewArgs, isVarArg: false), AddressSpace: 0);
135 Map[&F] = TypedPtrTy;
136 return TypedPtrTy;
137}
138} // anonymous namespace
139
140static Type *classifyConstantWithOpaquePtr(const Constant *C,
141 PointerTypeMap &Map) {
142 // FIXME: support ConstantPointerNull and UndefValue which could map to more
143 // than one TypedPointerType. See
144 // https://github.com/llvm/llvm-project/issues/57942.
145 if (isa<ConstantPointerNull>(Val: C) ||
146 (isa<UndefValue>(Val: C) && C->getType()->isPointerTy()))
147 return TypedPointerType::get(ElementType: Type::getInt8Ty(C&: C->getContext()),
148 AddressSpace: C->getType()->getPointerAddressSpace());
149
150 // Skip ConstantData which cannot have opaque ptr.
151 if (isa<ConstantData>(Val: C))
152 return C->getType();
153
154 auto It = Map.find(Val: C);
155 if (It != Map.end())
156 return It->second;
157
158 if (const auto *F = dyn_cast<Function>(Val: C))
159 return classifyFunctionType(F: *F, Map);
160
161 Type *Ty = C->getType();
162 Type *TargetTy = nullptr;
163 if (auto *CS = dyn_cast<ConstantStruct>(Val: C)) {
164 SmallVector<Type *> EltTys;
165 for (unsigned int I = 0; I < CS->getNumOperands(); ++I) {
166 const Constant *Elt = C->getAggregateElement(Elt: I);
167 Type *EltTy = classifyConstantWithOpaquePtr(C: Elt, Map);
168 EltTys.emplace_back(Args&: EltTy);
169 }
170 TargetTy = StructType::get(Context&: C->getContext(), Elements: EltTys);
171 } else if (auto *CA = dyn_cast<ConstantAggregate>(Val: C)) {
172
173 Type *TargetEltTy = nullptr;
174 for (auto &Elt : CA->operands()) {
175 Type *EltTy = classifyConstantWithOpaquePtr(C: cast<Constant>(Val: &Elt), Map);
176 assert(TargetEltTy == EltTy || TargetEltTy == nullptr);
177 TargetEltTy = EltTy;
178 }
179
180 if (auto *AT = dyn_cast<ArrayType>(Val: Ty)) {
181 TargetTy = ArrayType::get(ElementType: TargetEltTy, NumElements: AT->getNumElements());
182 } else {
183 // Not struct, not array, must be vector here.
184 auto *VT = cast<VectorType>(Val: Ty);
185 TargetTy = VectorType::get(ElementType: TargetEltTy, Other: VT);
186 }
187 }
188 // Must have a target ty when map.
189 assert(TargetTy && "PointerTypeAnalyisis failed to identify target type");
190
191 // Same type, no need to map.
192 if (TargetTy == Ty)
193 return Ty;
194
195 Map[C] = TargetTy;
196 return TargetTy;
197}
198
199static void classifyGlobalCtorPointerType(const GlobalVariable &GV,
200 PointerTypeMap &Map) {
201 const auto *CA = dyn_cast<ConstantArray>(Val: GV.getInitializer());
202 if (!CA) {
203 // An empty global_ctors will be a zeroinitializer, so just skip it.
204 assert(isa<ConstantAggregateZero>(GV.getInitializer()) &&
205 "global_ctors should be a ConstantArray or ConstantAggregateZero");
206 return;
207 }
208 // Type for global ctor should be array of { i32, void ()*, i8* }.
209 Type *CtorArrayTy = classifyConstantWithOpaquePtr(C: CA, Map);
210
211 // Map the global type.
212 Map[&GV] = TypedPointerType::get(ElementType: CtorArrayTy,
213 AddressSpace: GV.getType()->getPointerAddressSpace());
214}
215
216PointerTypeMap PointerTypeAnalysis::run(const Module &M) {
217 PointerTypeMap Map;
218 for (auto &G : M.globals()) {
219 if (G.getType()->isPointerTy())
220 classifyPointerType(V: &G, Map);
221 if (G.getName() == "llvm.global_ctors")
222 classifyGlobalCtorPointerType(GV: G, Map);
223 }
224
225 for (auto &F : M) {
226 classifyFunctionType(F, Map);
227
228 for (const auto &B : F) {
229 for (const auto &I : B) {
230 if (I.getType()->isPointerTy())
231 classifyPointerType(V: &I, Map);
232 for (const auto &O : I.operands())
233 if (O.get()->getType()->isPointerTy())
234 classifyPointerType(V: O.get(), Map);
235 }
236 }
237 }
238 return Map;
239}
240