| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // DxilUtilDbgInfoAndMisc.cpp // |
| // Copyright (C) Microsoft Corporation. All rights reserved. // |
| // This file is distributed under the University of Illinois Open Source // |
| // License. See LICENSE.TXT for details. // |
| // // |
| // Dxil helper functions. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "dxc/DXIL/DxilModule.h" |
| #include "dxc/DXIL/DxilOperations.h" |
| #include "dxc/DXIL/DxilTypeSystem.h" |
| #include "dxc/DXIL/DxilUtil.h" |
| #include "dxc/HLSL/DxilConvergentName.h" |
| #include "dxc/Support/Global.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Bitcode/ReaderWriter.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/DIBuilder.h" |
| #include "llvm/IR/DiagnosticInfo.h" |
| #include "llvm/IR/DiagnosticPrinter.h" |
| #include "llvm/IR/GetElementPtrTypeIterator.h" |
| #include "llvm/IR/GlobalVariable.h" |
| #include "llvm/IR/IRBuilder.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/IntrinsicInst.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace llvm; |
| using namespace hlsl; |
| |
| namespace { |
| |
| // Attempt to merge the two GEPs into a single GEP. |
| // |
| // If `AsCast` is non-null the merged GEP will be wrapped |
| // in an addrspacecast before replacing users. This allows |
| // merging GEPs of the form |
| // |
| // gep(addrspacecast(gep(p0, gep_args0) to p1*), gep_args1) |
| // into |
| // addrspacecast(gep(p0, gep_args0+gep_args1) to p1*) |
| // |
| Value *MergeGEP(GEPOperator *SrcGEP, GEPOperator *GEP, |
| AddrSpaceCastOperator *AsCast) { |
| IRBuilder<> Builder(GEP->getContext()); |
| StringRef Name = ""; |
| if (Instruction *I = dyn_cast<Instruction>(GEP)) { |
| Builder.SetInsertPoint(I); |
| Name = GEP->getName(); |
| } |
| SmallVector<Value *, 8> Indices; |
| |
| // Find out whether the last index in the source GEP is a sequential idx. |
| bool EndsWithSequential = false; |
| for (gep_type_iterator I = gep_type_begin(*SrcGEP), E = gep_type_end(*SrcGEP); |
| I != E; ++I) |
| EndsWithSequential = !(*I)->isStructTy(); |
| if (EndsWithSequential) { |
| Value *Sum; |
| Value *SO1 = SrcGEP->getOperand(SrcGEP->getNumOperands() - 1); |
| Value *GO1 = GEP->getOperand(1); |
| if (SO1 == Constant::getNullValue(SO1->getType())) { |
| Sum = GO1; |
| } else if (GO1 == Constant::getNullValue(GO1->getType())) { |
| Sum = SO1; |
| } else { |
| // If they aren't the same type, then the input hasn't been processed |
| // by the loop above yet (which canonicalizes sequential index types to |
| // intptr_t). Just avoid transforming this until the input has been |
| // normalized. |
| if (SO1->getType() != GO1->getType()) |
| return nullptr; |
| // Only do the combine when GO1 and SO1 are both constants. Only in |
| // this case, we are sure the cost after the merge is never more than |
| // that before the merge. |
| if (!isa<Constant>(GO1) || !isa<Constant>(SO1)) |
| return nullptr; |
| Sum = Builder.CreateAdd(SO1, GO1); |
| } |
| |
| // Update the GEP in place if possible. |
| if (SrcGEP->getNumOperands() == 2 && !AsCast) { |
| GEP->setOperand(0, SrcGEP->getOperand(0)); |
| GEP->setOperand(1, Sum); |
| return GEP; |
| } |
| Indices.append(SrcGEP->op_begin() + 1, SrcGEP->op_end() - 1); |
| Indices.push_back(Sum); |
| Indices.append(GEP->op_begin() + 2, GEP->op_end()); |
| } else if (isa<Constant>(*GEP->idx_begin()) && |
| cast<Constant>(*GEP->idx_begin())->isNullValue() && |
| SrcGEP->getNumOperands() != 1) { |
| // Otherwise we can do the fold if the first index of the GEP is a zero |
| Indices.append(SrcGEP->op_begin() + 1, SrcGEP->op_end()); |
| Indices.append(GEP->idx_begin() + 1, GEP->idx_end()); |
| } |
| |
| DXASSERT(!Indices.empty(), "must merge"); |
| Value *newGEP = |
| Builder.CreateInBoundsGEP(nullptr, SrcGEP->getOperand(0), Indices, Name); |
| |
| // Wrap the new gep in an addrspacecast if needed. |
| if (AsCast) |
| newGEP = Builder.CreateAddrSpaceCast( |
| newGEP, PointerType::get(GEP->getType()->getPointerElementType(), |
| AsCast->getDestAddressSpace())); |
| GEP->replaceAllUsesWith(newGEP); |
| if (Instruction *I = dyn_cast<Instruction>(GEP)) |
| I->eraseFromParent(); |
| return newGEP; |
| } |
| |
| // Examine the gep and try to merge it when the input pointer is |
| // itself a gep. We handle two forms here: |
| // |
| // gep(gep(p)) |
| // gep(addrspacecast(gep(p))) |
| // |
| // If the gep was merged successfully then return the updated value, otherwise |
| // return nullptr. |
| // |
| // When the gep is sucessfully merged we will delete the gep and also try to |
| // delete the nested gep and addrspacecast. |
| static Value *TryMegeWithNestedGEP(GEPOperator *GEP) { |
| // Sentinal value to return when we fail to merge. |
| Value *FailedToMerge = nullptr; |
| |
| Value *Ptr = GEP->getPointerOperand(); |
| GEPOperator *prevGEP = dyn_cast<GEPOperator>(Ptr); |
| AddrSpaceCastOperator *AsCast = nullptr; |
| |
| // If there is no directly nested gep try looking through an addrspacecast to |
| // find one. |
| if (!prevGEP) { |
| AsCast = dyn_cast<AddrSpaceCastOperator>(Ptr); |
| if (AsCast) |
| prevGEP = dyn_cast<GEPOperator>(AsCast->getPointerOperand()); |
| } |
| |
| // Not a nested gep expression. |
| if (!prevGEP) |
| return FailedToMerge; |
| |
| // Try merging the two geps. |
| Value *newGEP = MergeGEP(prevGEP, GEP, AsCast); |
| if (!newGEP) |
| return FailedToMerge; |
| |
| // Delete the nested gep and addrspacecast if no more users. |
| if (AsCast && AsCast->user_empty() && isa<AddrSpaceCastInst>(AsCast)) |
| cast<AddrSpaceCastInst>(AsCast)->eraseFromParent(); |
| |
| if (prevGEP->user_empty() && isa<GetElementPtrInst>(prevGEP)) |
| cast<GetElementPtrInst>(prevGEP)->eraseFromParent(); |
| |
| return newGEP; |
| } |
| |
| } // namespace |
| |
| namespace hlsl { |
| |
| namespace dxilutil { |
| |
| bool MergeGepUse(Value *V) { |
| bool changed = false; |
| SmallVector<Value *, 16> worklist; |
| auto addUsersToWorklist = [&worklist](Value *V) { |
| if (!V->user_empty()) { |
| // Add users in reverse to the worklist, so they are processed in order |
| // This makes it equivalent to recursive traversal |
| size_t start = worklist.size(); |
| worklist.append(V->user_begin(), V->user_end()); |
| size_t end = worklist.size(); |
| std::reverse(worklist.data() + start, worklist.data() + end); |
| } |
| }; |
| addUsersToWorklist(V); |
| while (worklist.size()) { |
| V = worklist.pop_back_val(); |
| if (isa<BitCastOperator>(V)) { |
| if (Value *NewV = dxilutil::TryReplaceBaseCastWithGep(V)) { |
| changed = true; |
| worklist.push_back(NewV); |
| } else { |
| // merge any GEP users of the untranslated bitcast |
| addUsersToWorklist(V); |
| } |
| } else if (isa<AddrSpaceCastOperator>(V)) { |
| addUsersToWorklist(V); |
| } else if (GEPOperator *GEP = dyn_cast<GEPOperator>(V)) { |
| if (Value *newGEP = TryMegeWithNestedGEP(GEP)) { |
| changed = true; |
| worklist.push_back(newGEP); |
| } else { |
| addUsersToWorklist(GEP); |
| } |
| } |
| } |
| |
| return changed; |
| } |
| |
| std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::MemoryBuffer *MB, |
| llvm::LLVMContext &Ctx, |
| std::string &DiagStr) { |
| // Note: the DiagStr is not used. |
| auto pModule = llvm::parseBitcodeFile(MB->getMemBufferRef(), Ctx); |
| if (!pModule) { |
| return nullptr; |
| } |
| return std::unique_ptr<llvm::Module>(pModule.get().release()); |
| } |
| |
| std::unique_ptr<llvm::Module> |
| LoadModuleFromBitcodeLazy(std::unique_ptr<llvm::MemoryBuffer> &&MB, |
| llvm::LLVMContext &Ctx, std::string &DiagStr) { |
| // Note: the DiagStr is not used. |
| auto pModule = llvm::getLazyBitcodeModule(std::move(MB), Ctx, nullptr, true); |
| if (!pModule) { |
| return nullptr; |
| } |
| return std::unique_ptr<llvm::Module>(pModule.get().release()); |
| } |
| |
| std::unique_ptr<llvm::Module> LoadModuleFromBitcode(llvm::StringRef BC, |
| llvm::LLVMContext &Ctx, |
| std::string &DiagStr) { |
| std::unique_ptr<llvm::MemoryBuffer> pBitcodeBuf( |
| llvm::MemoryBuffer::getMemBuffer(BC, "", false)); |
| return LoadModuleFromBitcode(pBitcodeBuf.get(), Ctx, DiagStr); |
| } |
| |
| DIGlobalVariable *FindGlobalVariableDebugInfo(GlobalVariable *GV, |
| DebugInfoFinder &DbgInfoFinder) { |
| struct GlobalFinder { |
| GlobalVariable *GV; |
| bool operator()(llvm::DIGlobalVariable *const arg) const { |
| return arg->getVariable() == GV; |
| } |
| }; |
| GlobalFinder F = {GV}; |
| DebugInfoFinder::global_variable_iterator Found = |
| std::find_if(DbgInfoFinder.global_variables().begin(), |
| DbgInfoFinder.global_variables().end(), F); |
| if (Found != DbgInfoFinder.global_variables().end()) { |
| return *Found; |
| } |
| return nullptr; |
| } |
| |
| static void EmitWarningOrErrorOnInstruction(Instruction *I, Twine Msg, |
| DiagnosticSeverity severity); |
| |
| // If we don't have debug location and this is select/phi, |
| // try recursing users to find instruction with debug info. |
| // Only recurse phi/select and limit depth to prevent doing |
| // too much work if no debug location found. |
| static bool |
| EmitWarningOrErrorOnInstructionFollowPhiSelect(Instruction *I, Twine Msg, |
| DiagnosticSeverity severity, |
| unsigned depth = 0) { |
| if (depth > 4) |
| return false; |
| if (I->getDebugLoc().get()) { |
| EmitWarningOrErrorOnInstruction(I, Msg, severity); |
| return true; |
| } |
| if (isa<PHINode>(I) || isa<SelectInst>(I)) { |
| for (auto U : I->users()) |
| if (Instruction *UI = dyn_cast<Instruction>(U)) |
| if (EmitWarningOrErrorOnInstructionFollowPhiSelect(UI, Msg, severity, |
| depth + 1)) |
| return true; |
| } |
| return false; |
| } |
| |
| static void EmitWarningOrErrorOnInstruction(Instruction *I, Twine Msg, |
| DiagnosticSeverity severity) { |
| const DebugLoc &DL = I->getDebugLoc(); |
| if (!DL.get() && (isa<PHINode>(I) || isa<SelectInst>(I))) { |
| if (EmitWarningOrErrorOnInstructionFollowPhiSelect(I, Msg, severity)) |
| return; |
| } |
| |
| I->getContext().diagnose( |
| DiagnosticInfoDxil(I->getParent()->getParent(), DL.get(), Msg, severity)); |
| } |
| |
| void EmitErrorOnInstruction(Instruction *I, Twine Msg) { |
| EmitWarningOrErrorOnInstruction(I, Msg, DiagnosticSeverity::DS_Error); |
| } |
| |
| void EmitWarningOnInstruction(Instruction *I, Twine Msg) { |
| EmitWarningOrErrorOnInstruction(I, Msg, DiagnosticSeverity::DS_Warning); |
| } |
| |
| static void EmitWarningOrErrorOnFunction(llvm::LLVMContext &Ctx, Function *F, |
| Twine Msg, |
| DiagnosticSeverity severity) { |
| DILocation *DLoc = nullptr; |
| |
| if (DISubprogram *DISP = getDISubprogram(F)) { |
| DLoc = DILocation::get(F->getContext(), DISP->getLine(), 0, DISP, |
| nullptr /*InlinedAt*/); |
| } |
| Ctx.diagnose(DiagnosticInfoDxil(F, DLoc, Msg, severity)); |
| } |
| |
| void EmitErrorOnFunction(llvm::LLVMContext &Ctx, Function *F, Twine Msg) { |
| EmitWarningOrErrorOnFunction(Ctx, F, Msg, DiagnosticSeverity::DS_Error); |
| } |
| |
| void EmitWarningOnFunction(llvm::LLVMContext &Ctx, Function *F, Twine Msg) { |
| EmitWarningOrErrorOnFunction(Ctx, F, Msg, DiagnosticSeverity::DS_Warning); |
| } |
| |
| static void EmitWarningOrErrorOnGlobalVariable(llvm::LLVMContext &Ctx, |
| GlobalVariable *GV, Twine Msg, |
| DiagnosticSeverity severity) { |
| DIGlobalVariable *DIV = nullptr; |
| |
| if (GV) { |
| Module &M = *GV->getParent(); |
| if (hasDebugInfo(M)) { |
| DebugInfoFinder FinderObj; |
| DebugInfoFinder &Finder = FinderObj; |
| // Debug modules have no dxil modules. Use it if you got it. |
| if (M.HasDxilModule()) |
| Finder = M.GetDxilModule().GetOrCreateDebugInfoFinder(); |
| else |
| Finder.processModule(M); |
| DIV = FindGlobalVariableDebugInfo(GV, Finder); |
| } |
| } |
| |
| Ctx.diagnose(DiagnosticInfoDxil(nullptr /*Function*/, DIV, Msg, severity)); |
| } |
| |
| void EmitErrorOnGlobalVariable(llvm::LLVMContext &Ctx, GlobalVariable *GV, |
| Twine Msg) { |
| EmitWarningOrErrorOnGlobalVariable(Ctx, GV, Msg, |
| DiagnosticSeverity::DS_Error); |
| } |
| |
| void EmitWarningOnGlobalVariable(llvm::LLVMContext &Ctx, GlobalVariable *GV, |
| Twine Msg) { |
| EmitWarningOrErrorOnGlobalVariable(Ctx, GV, Msg, |
| DiagnosticSeverity::DS_Warning); |
| } |
| |
| const char *kResourceMapErrorMsg = |
| "local resource not guaranteed to map to unique global resource."; |
| void EmitResMappingError(Instruction *Res) { |
| EmitErrorOnInstruction(Res, kResourceMapErrorMsg); |
| } |
| |
| // Mostly just a locationless diagnostic output |
| static void EmitWarningOrErrorOnContext(LLVMContext &Ctx, Twine Msg, |
| DiagnosticSeverity severity) { |
| Ctx.diagnose(DiagnosticInfoDxil(nullptr /*Func*/, Msg, severity)); |
| } |
| |
| void EmitErrorOnContext(LLVMContext &Ctx, Twine Msg) { |
| EmitWarningOrErrorOnContext(Ctx, Msg, DiagnosticSeverity::DS_Error); |
| } |
| |
| void EmitWarningOnContext(LLVMContext &Ctx, Twine Msg) { |
| EmitWarningOrErrorOnContext(Ctx, Msg, DiagnosticSeverity::DS_Warning); |
| } |
| |
| void EmitNoteOnContext(LLVMContext &Ctx, Twine Msg) { |
| EmitWarningOrErrorOnContext(Ctx, Msg, DiagnosticSeverity::DS_Note); |
| } |
| |
| Value::user_iterator mdv_users_end(Value *V) { return Value::user_iterator(); } |
| Value::user_iterator mdv_users_begin(Value *V) { |
| if (auto *L = LocalAsMetadata::getIfExists(V)) { |
| if (auto *MDV = MetadataAsValue::getIfExists(L->getContext(), L)) { |
| return MDV->user_begin(); |
| } |
| } |
| return mdv_users_end(V); |
| } |
| |
| static DbgValueInst *FindDbgValueInst(Value *Val) { |
| for (auto it = mdv_users_begin(Val), end = mdv_users_end(Val); it != end; |
| it++) { |
| if (DbgValueInst *DbgValInst = dyn_cast<DbgValueInst>(*it)) |
| return DbgValInst; |
| } |
| return nullptr; |
| } |
| |
| void MigrateDebugValue(Value *Old, Value *New) { |
| DbgValueInst *DbgValInst = FindDbgValueInst(Old); |
| if (DbgValInst == nullptr) |
| return; |
| |
| DbgValInst->setOperand( |
| 0, MetadataAsValue::get(New->getContext(), ValueAsMetadata::get(New))); |
| |
| // Move the dbg value after the new instruction |
| if (Instruction *NewInst = dyn_cast<Instruction>(New)) { |
| if (NewInst->getNextNode() != DbgValInst) { |
| DbgValInst->removeFromParent(); |
| DbgValInst->insertAfter(NewInst); |
| } |
| } |
| } |
| |
| // Propagates any llvm.dbg.value instruction for a given vector |
| // to the elements that were used to create it through a series |
| // of insertelement instructions. |
| // |
| // This is used after lowering a vector-returning intrinsic. |
| // If we just keep the debug info on the recomposed vector, |
| // we will lose it when we break it apart again during later |
| // optimization stages. |
| void TryScatterDebugValueToVectorElements(Value *Val) { |
| if (!isa<InsertElementInst>(Val) || !Val->getType()->isVectorTy()) |
| return; |
| |
| DbgValueInst *VecDbgValInst = FindDbgValueInst(Val); |
| if (VecDbgValInst == nullptr) |
| return; |
| |
| Type *ElemTy = Val->getType()->getVectorElementType(); |
| DIBuilder DbgInfoBuilder(*VecDbgValInst->getModule()); |
| unsigned ElemSizeInBits = |
| VecDbgValInst->getModule()->getDataLayout().getTypeSizeInBits(ElemTy); |
| |
| DIExpression *ParentBitPiece = VecDbgValInst->getExpression(); |
| if (ParentBitPiece != nullptr && !ParentBitPiece->isBitPiece()) |
| ParentBitPiece = nullptr; |
| |
| while (InsertElementInst *InsertElt = dyn_cast<InsertElementInst>(Val)) { |
| Value *NewElt = InsertElt->getOperand(1); |
| unsigned EltIdx = static_cast<unsigned>( |
| cast<ConstantInt>(InsertElt->getOperand(2))->getLimitedValue()); |
| unsigned OffsetInBits = EltIdx * ElemSizeInBits; |
| |
| if (ParentBitPiece) { |
| assert(OffsetInBits + ElemSizeInBits <= |
| ParentBitPiece->getBitPieceSize() && |
| "Nested bit piece expression exceeds bounds of its parent."); |
| OffsetInBits += ParentBitPiece->getBitPieceOffset(); |
| } |
| |
| DIExpression *DIExpr = |
| DbgInfoBuilder.createBitPieceExpression(OffsetInBits, ElemSizeInBits); |
| // Offset is basically unused and deprecated in later LLVM versions. |
| // Emit it as zero otherwise later versions of the bitcode reader will drop |
| // the intrinsic. |
| DbgInfoBuilder.insertDbgValueIntrinsic( |
| NewElt, /* Offset */ 0, VecDbgValInst->getVariable(), DIExpr, |
| VecDbgValInst->getDebugLoc(), InsertElt); |
| Val = InsertElt->getOperand(0); |
| } |
| } |
| |
| } // namespace dxilutil |
| } // namespace hlsl |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| namespace { |
| class DxilLoadMetadata : public ModulePass { |
| public: |
| static char ID; // Pass identification, replacement for typeid |
| explicit DxilLoadMetadata() : ModulePass(ID) {} |
| |
| StringRef getPassName() const override { |
| return "HLSL load DxilModule from metadata"; |
| } |
| |
| bool runOnModule(Module &M) override { |
| if (!M.HasDxilModule()) { |
| (void)M.GetOrCreateDxilModule(); |
| return true; |
| } |
| |
| return false; |
| } |
| }; |
| } // namespace |
| |
| char DxilLoadMetadata::ID = 0; |
| |
| ModulePass *llvm::createDxilLoadMetadataPass() { |
| return new DxilLoadMetadata(); |
| } |
| |
| INITIALIZE_PASS(DxilLoadMetadata, "hlsl-dxilload", |
| "HLSL load DxilModule from metadata", false, false) |