| 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 | |
| 20 | using namespace llvm; |
| 21 | using namespace llvm::dxil; |
| 22 | |
| 23 | namespace { |
| 24 | |
| 25 | Type *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. |
| 29 | Type *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. |
| 100 | Type *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 | |
| 140 | static 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 | |
| 199 | static 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 | |
| 216 | PointerTypeMap 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 | |