//===- Target/DirectX/PointerTypeAnalisis.cpp - PointerType analysis ------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Analysis pass to assign types to opaque pointers. // //===----------------------------------------------------------------------===// #include "PointerTypeAnalysis.h" #include "llvm/IR/Constants.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" using namespace llvm; using namespace llvm::dxil; namespace { Type *classifyFunctionType(const Function &F, PointerTypeMap &Map); // Classifies the type of the value passed in by walking the value's users to // find a typed instruction to materialize a type from. Type *classifyPointerType(const Value *V, PointerTypeMap &Map) { assert(V->getType()->isPointerTy() && "classifyPointerType called with non-pointer"); // A CallInst will trigger this case, and we want to classify its Function // operand as a Function rather than a generic Value. if (const Function *F = dyn_cast(V)) return classifyFunctionType(*F, Map); // There can potentially be dead constants hanging off of the globals we do // not want to deal with. So we remove them here. if (const GlobalVariable *GV = dyn_cast(V)) GV->removeDeadConstantUsers(); auto It = Map.find(V); if (It != Map.end()) return It->second; Type *PointeeTy = nullptr; if (auto *GEP = dyn_cast(V)) { if (!GEP->getResultElementType()->isPointerTy()) PointeeTy = GEP->getResultElementType(); } else if (auto *Inst = dyn_cast(V)) { PointeeTy = Inst->getAllocatedType(); } else if (auto *GV = dyn_cast(V)) { PointeeTy = GV->getValueType(); } for (const auto *User : V->users()) { Type *NewPointeeTy = nullptr; if (const auto *Inst = dyn_cast(User)) { NewPointeeTy = Inst->getType(); } else if (const auto *Inst = dyn_cast(User)) { NewPointeeTy = Inst->getValueOperand()->getType(); // When store value is ptr type, cannot get more type info. if (NewPointeeTy->isPointerTy()) continue; } else if (const auto *GEP = dyn_cast(User)) { NewPointeeTy = GEP->getSourceElementType(); } if (NewPointeeTy) { // HLSL doesn't support pointers, so it is unlikely to get more than one // or two levels of indirection in the IR. Because of this, recursion is // pretty safe. if (NewPointeeTy->isPointerTy()) { PointeeTy = classifyPointerType(User, Map); break; } if (!PointeeTy) PointeeTy = NewPointeeTy; else if (PointeeTy != NewPointeeTy) PointeeTy = Type::getInt8Ty(V->getContext()); } } // If we were unable to determine the pointee type, set to i8 if (!PointeeTy) PointeeTy = Type::getInt8Ty(V->getContext()); auto *TypedPtrTy = TypedPointerType::get(PointeeTy, V->getType()->getPointerAddressSpace()); Map[V] = TypedPtrTy; return TypedPtrTy; } // This function constructs a function type accepting typed pointers. It only // handles function arguments and return types, and assigns the function type to // the function's value in the type map. Type *classifyFunctionType(const Function &F, PointerTypeMap &Map) { auto It = Map.find(&F); if (It != Map.end()) return It->second; SmallVector NewArgs; Type *RetTy = F.getReturnType(); LLVMContext &Ctx = F.getContext(); if (RetTy->isPointerTy()) { RetTy = nullptr; for (const auto &B : F) { const auto *RetInst = dyn_cast_or_null(B.getTerminator()); if (!RetInst) continue; Type *NewRetTy = classifyPointerType(RetInst->getReturnValue(), Map); if (!RetTy) RetTy = NewRetTy; else if (RetTy != NewRetTy) RetTy = TypedPointerType::get( Type::getInt8Ty(Ctx), F.getReturnType()->getPointerAddressSpace()); } // For function decl. if (!RetTy) RetTy = TypedPointerType::get( Type::getInt8Ty(Ctx), F.getReturnType()->getPointerAddressSpace()); } for (auto &A : F.args()) { Type *ArgTy = A.getType(); if (ArgTy->isPointerTy()) ArgTy = classifyPointerType(&A, Map); NewArgs.push_back(ArgTy); } auto *TypedPtrTy = TypedPointerType::get(FunctionType::get(RetTy, NewArgs, false), 0); Map[&F] = TypedPtrTy; return TypedPtrTy; } } // anonymous namespace static Type *classifyConstantWithOpaquePtr(const Constant *C, PointerTypeMap &Map) { // FIXME: support ConstantPointerNull which could map to more than one // TypedPointerType. // See https://github.com/llvm/llvm-project/issues/57942. if (isa(C)) return TypedPointerType::get(Type::getInt8Ty(C->getContext()), C->getType()->getPointerAddressSpace()); // Skip ConstantData which cannot have opaque ptr. if (isa(C)) return C->getType(); auto It = Map.find(C); if (It != Map.end()) return It->second; if (const auto *F = dyn_cast(C)) return classifyFunctionType(*F, Map); Type *Ty = C->getType(); Type *TargetTy = nullptr; if (auto *CS = dyn_cast(C)) { SmallVector EltTys; for (unsigned int I = 0; I < CS->getNumOperands(); ++I) { const Constant *Elt = C->getAggregateElement(I); Type *EltTy = classifyConstantWithOpaquePtr(Elt, Map); EltTys.emplace_back(EltTy); } TargetTy = StructType::get(C->getContext(), EltTys); } else if (auto *CA = dyn_cast(C)) { Type *TargetEltTy = nullptr; for (auto &Elt : CA->operands()) { Type *EltTy = classifyConstantWithOpaquePtr(cast(&Elt), Map); assert(TargetEltTy == EltTy || TargetEltTy == nullptr); TargetEltTy = EltTy; } if (auto *AT = dyn_cast(Ty)) { TargetTy = ArrayType::get(TargetEltTy, AT->getNumElements()); } else { // Not struct, not array, must be vector here. auto *VT = cast(Ty); TargetTy = VectorType::get(TargetEltTy, VT); } } // Must have a target ty when map. assert(TargetTy && "PointerTypeAnalyisis failed to identify target type"); // Same type, no need to map. if (TargetTy == Ty) return Ty; Map[C] = TargetTy; return TargetTy; } static void classifyGlobalCtorPointerType(const GlobalVariable &GV, PointerTypeMap &Map) { const auto *CA = cast(GV.getInitializer()); // Type for global ctor should be array of { i32, void ()*, i8* }. Type *CtorArrayTy = classifyConstantWithOpaquePtr(CA, Map); // Map the global type. Map[&GV] = TypedPointerType::get(CtorArrayTy, GV.getType()->getPointerAddressSpace()); } PointerTypeMap PointerTypeAnalysis::run(const Module &M) { PointerTypeMap Map; for (auto &G : M.globals()) { if (G.getType()->isPointerTy()) classifyPointerType(&G, Map); if (G.getName() == "llvm.global_ctors") classifyGlobalCtorPointerType(G, Map); } for (auto &F : M) { classifyFunctionType(F, Map); for (const auto &B : F) { for (const auto &I : B) { if (I.getType()->isPointerTy()) classifyPointerType(&I, Map); for (const auto &O : I.operands()) if (O.get()->getType()->isPointerTy()) classifyPointerType(O.get(), Map); } } } return Map; }