| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // HLSignatureLower.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. // |
| // // |
| // Lower signatures of entry function to DXIL LoadInput/StoreOutput. // |
| // // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "HLSignatureLower.h" |
| #include "dxc/DXIL/DxilOperations.h" |
| #include "dxc/DXIL/DxilSemantic.h" |
| #include "dxc/DXIL/DxilSigPoint.h" |
| #include "dxc/DXIL/DxilSignatureElement.h" |
| #include "dxc/DXIL/DxilTypeSystem.h" |
| #include "dxc/DXIL/DxilUtil.h" |
| #include "dxc/HLSL/DxilPackSignatureElement.h" |
| #include "dxc/HLSL/HLMatrixLowerHelper.h" |
| #include "dxc/HLSL/HLMatrixType.h" |
| #include "dxc/HLSL/HLModule.h" |
| #include "dxc/HlslIntrinsicOp.h" |
| #include "dxc/Support/Global.h" |
| |
| #include "llvm/IR/DebugInfo.h" |
| #include "llvm/IR/IRBuilder.h" |
| #include "llvm/IR/IntrinsicInst.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Transforms/Utils/Local.h" |
| |
| using namespace llvm; |
| using namespace hlsl; |
| |
| namespace { |
| // Decompose semantic name (eg FOO1=>FOO,1), change interp mode for SV_Position. |
| // Return semantic index. |
| unsigned UpdateSemanticAndInterpMode(StringRef &semName, |
| DXIL::InterpolationMode &mode, |
| DXIL::SigPointKind kind, |
| LLVMContext &Context) { |
| llvm::StringRef baseSemName; // The 'FOO' in 'FOO1'. |
| uint32_t semIndex; // The '1' in 'FOO1' |
| |
| // Split semName and index. |
| Semantic::DecomposeNameAndIndex(semName, &baseSemName, &semIndex); |
| semName = baseSemName; |
| const Semantic *semantic = Semantic::GetByName(semName, kind); |
| if (semantic && semantic->GetKind() == Semantic::Kind::Position) { |
| // Update interp mode to no_perspective version for SV_Position. |
| switch (mode) { |
| case InterpolationMode::Kind::LinearCentroid: |
| mode = InterpolationMode::Kind::LinearNoperspectiveCentroid; |
| break; |
| case InterpolationMode::Kind::LinearSample: |
| mode = InterpolationMode::Kind::LinearNoperspectiveSample; |
| break; |
| case InterpolationMode::Kind::Linear: |
| mode = InterpolationMode::Kind::LinearNoperspective; |
| break; |
| case InterpolationMode::Kind::Constant: |
| case InterpolationMode::Kind::Undefined: |
| case InterpolationMode::Kind::Invalid: { |
| llvm_unreachable("invalid interpolation mode for SV_Position"); |
| } break; |
| case InterpolationMode::Kind::LinearNoperspective: |
| case InterpolationMode::Kind::LinearNoperspectiveCentroid: |
| case InterpolationMode::Kind::LinearNoperspectiveSample: |
| // Already Noperspective modes. |
| break; |
| } |
| } |
| return semIndex; |
| } |
| |
| DxilSignatureElement *FindArgInSignature(Argument &arg, |
| llvm::StringRef semantic, |
| DXIL::InterpolationMode interpMode, |
| DXIL::SigPointKind kind, |
| DxilSignature &sig) { |
| // Match output ID. |
| unsigned semIndex = |
| UpdateSemanticAndInterpMode(semantic, interpMode, kind, arg.getContext()); |
| |
| for (uint32_t i = 0; i < sig.GetElements().size(); i++) { |
| DxilSignatureElement &SE = sig.GetElement(i); |
| bool semNameMatch = semantic.equals_lower(SE.GetName()); |
| bool semIndexMatch = semIndex == SE.GetSemanticIndexVec()[0]; |
| |
| if (semNameMatch && semIndexMatch) { |
| // Find a match. |
| return &SE; |
| } |
| } |
| return nullptr; |
| } |
| } // namespace |
| |
| namespace { |
| void replaceInputOutputWithIntrinsic(DXIL::SemanticKind semKind, Value *GV, |
| OP *hlslOP, IRBuilder<> &Builder) { |
| Type *Ty = GV->getType(); |
| if (Ty->isPointerTy()) |
| Ty = Ty->getPointerElementType(); |
| |
| OP::OpCode opcode; |
| switch (semKind) { |
| case Semantic::Kind::DomainLocation: |
| opcode = OP::OpCode::DomainLocation; |
| break; |
| case Semantic::Kind::OutputControlPointID: |
| opcode = OP::OpCode::OutputControlPointID; |
| break; |
| case Semantic::Kind::GSInstanceID: |
| opcode = OP::OpCode::GSInstanceID; |
| break; |
| case Semantic::Kind::PrimitiveID: |
| opcode = OP::OpCode::PrimitiveID; |
| break; |
| case Semantic::Kind::SampleIndex: |
| opcode = OP::OpCode::SampleIndex; |
| break; |
| case Semantic::Kind::Coverage: |
| opcode = OP::OpCode::Coverage; |
| break; |
| case Semantic::Kind::InnerCoverage: |
| opcode = OP::OpCode::InnerCoverage; |
| break; |
| case Semantic::Kind::ViewID: |
| opcode = OP::OpCode::ViewID; |
| break; |
| case Semantic::Kind::GroupThreadID: |
| opcode = OP::OpCode::ThreadIdInGroup; |
| break; |
| case Semantic::Kind::GroupID: |
| opcode = OP::OpCode::GroupId; |
| break; |
| case Semantic::Kind::DispatchThreadID: |
| opcode = OP::OpCode::ThreadId; |
| break; |
| case Semantic::Kind::GroupIndex: |
| opcode = OP::OpCode::FlattenedThreadIdInGroup; |
| break; |
| case Semantic::Kind::CullPrimitive: { |
| GV->replaceAllUsesWith(ConstantInt::get(Ty, (uint64_t)0)); |
| return; |
| } break; |
| case Semantic::Kind::StartVertexLocation: |
| opcode = OP::OpCode::StartVertexLocation; |
| break; |
| case Semantic::Kind::StartInstanceLocation: |
| opcode = OP::OpCode::StartInstanceLocation; |
| break; |
| default: |
| DXASSERT(0, "invalid semantic"); |
| return; |
| } |
| |
| Function *dxilFunc = hlslOP->GetOpFunc(opcode, Ty->getScalarType()); |
| Constant *OpArg = hlslOP->GetU32Const((unsigned)opcode); |
| |
| Value *newArg = nullptr; |
| if (semKind == Semantic::Kind::DomainLocation || |
| semKind == Semantic::Kind::GroupThreadID || |
| semKind == Semantic::Kind::GroupID || |
| semKind == Semantic::Kind::DispatchThreadID) { |
| unsigned vecSize = 1; |
| if (FixedVectorType *VT = dyn_cast<FixedVectorType>(Ty)) |
| vecSize = VT->getNumElements(); |
| |
| newArg = Builder.CreateCall( |
| dxilFunc, {OpArg, semKind == Semantic::Kind::DomainLocation |
| ? hlslOP->GetU8Const(0) |
| : hlslOP->GetU32Const(0)}); |
| if (vecSize > 1) { |
| Value *result = UndefValue::get(Ty); |
| result = Builder.CreateInsertElement(result, newArg, (uint64_t)0); |
| |
| for (unsigned i = 1; i < vecSize; i++) { |
| Value *newElt = Builder.CreateCall( |
| dxilFunc, {OpArg, semKind == Semantic::Kind::DomainLocation |
| ? hlslOP->GetU8Const(i) |
| : hlslOP->GetU32Const(i)}); |
| result = Builder.CreateInsertElement(result, newElt, i); |
| } |
| newArg = result; |
| } |
| } else { |
| newArg = Builder.CreateCall(dxilFunc, {OpArg}); |
| } |
| |
| if (newArg->getType() != GV->getType()) { |
| DXASSERT_NOMSG(GV->getType()->isPointerTy()); |
| for (User *U : GV->users()) { |
| if (LoadInst *LI = dyn_cast<LoadInst>(U)) { |
| LI->replaceAllUsesWith(newArg); |
| } |
| } |
| } else { |
| GV->replaceAllUsesWith(newArg); |
| } |
| } |
| } // namespace |
| |
| void HLSignatureLower::ProcessArgument(Function *func, |
| DxilFunctionAnnotation *funcAnnotation, |
| Argument &arg, DxilFunctionProps &props, |
| const ShaderModel *pSM, |
| bool isPatchConstantFunction, |
| bool forceOut, bool &hasClipPlane) { |
| Type *Ty = arg.getType(); |
| DxilParameterAnnotation ¶mAnnotation = |
| funcAnnotation->GetParameterAnnotation(arg.getArgNo()); |
| hlsl::DxilParamInputQual qual = |
| forceOut ? DxilParamInputQual::Out : paramAnnotation.GetParamInputQual(); |
| bool isInout = qual == DxilParamInputQual::Inout; |
| |
| // If this was an inout param, do the output side first |
| if (isInout) { |
| DXASSERT(!isPatchConstantFunction, |
| "Patch Constant function should not have inout param"); |
| m_inoutArgSet.insert(&arg); |
| const bool bForceOutTrue = true; |
| ProcessArgument(func, funcAnnotation, arg, props, pSM, |
| isPatchConstantFunction, bForceOutTrue, hasClipPlane); |
| qual = DxilParamInputQual::In; |
| } |
| |
| // Get stream index |
| unsigned streamIdx = 0; |
| switch (qual) { |
| case DxilParamInputQual::OutStream1: |
| streamIdx = 1; |
| break; |
| case DxilParamInputQual::OutStream2: |
| streamIdx = 2; |
| break; |
| case DxilParamInputQual::OutStream3: |
| streamIdx = 3; |
| break; |
| default: |
| // Use streamIdx = 0 by default. |
| break; |
| } |
| |
| const SigPoint *sigPoint = SigPoint::GetSigPoint( |
| SigPointFromInputQual(qual, props.shaderKind, isPatchConstantFunction)); |
| |
| unsigned rows, cols; |
| HLModule::GetParameterRowsAndCols(Ty, rows, cols, paramAnnotation); |
| CompType EltTy = paramAnnotation.GetCompType(); |
| DXIL::InterpolationMode interpMode = |
| paramAnnotation.GetInterpolationMode().GetKind(); |
| |
| // Set undefined interpMode. |
| if (sigPoint->GetKind() == DXIL::SigPointKind::MSPOut) { |
| if (interpMode != InterpolationMode::Kind::Undefined && |
| interpMode != InterpolationMode::Kind::Constant) { |
| dxilutil::EmitErrorOnFunction( |
| HLM.GetModule()->getContext(), func, |
| "Mesh shader's primitive outputs' interpolation mode must be " |
| "constant or undefined."); |
| } |
| interpMode = InterpolationMode::Kind::Constant; |
| } else if (!sigPoint->NeedsInterpMode()) |
| interpMode = InterpolationMode::Kind::Undefined; |
| else if (interpMode == InterpolationMode::Kind::Undefined) { |
| // Type-based default: linear for floats, constant for others. |
| if (EltTy.IsFloatTy()) |
| interpMode = InterpolationMode::Kind::Linear; |
| else |
| interpMode = InterpolationMode::Kind::Constant; |
| } |
| |
| // back-compat mode - remap obsolete semantics |
| if (HLM.GetHLOptions().bDX9CompatMode && |
| paramAnnotation.HasSemanticString()) { |
| hlsl::RemapObsoleteSemantic(paramAnnotation, sigPoint->GetKind(), |
| HLM.GetCtx()); |
| } |
| |
| llvm::StringRef semanticStr = paramAnnotation.GetSemanticString(); |
| if (semanticStr.empty()) { |
| |
| std::string msg = "Semantic must be defined for all "; |
| msg += (qual == DxilParamInputQual::Out) ? "outputs " : "parameters "; |
| msg += "of an entry function or patch constant function"; |
| |
| dxilutil::EmitErrorOnFunction(HLM.GetModule()->getContext(), func, msg); |
| return; |
| } |
| UpdateSemanticAndInterpMode(semanticStr, interpMode, sigPoint->GetKind(), |
| arg.getContext()); |
| |
| // Get Semantic interpretation, skipping if not in signature |
| const Semantic *pSemantic = Semantic::GetByName(semanticStr); |
| DXIL::SemanticInterpretationKind interpretation = |
| SigPoint::GetInterpretation(pSemantic->GetKind(), sigPoint->GetKind(), |
| pSM->GetMajor(), pSM->GetMinor()); |
| |
| // Verify system value semantics do not overlap. |
| // Note: Arbitrary are always in the signature and will be verified with a |
| // different mechanism. For patch constant function, only validate patch |
| // constant elements (others already validated on hull function) |
| if (pSemantic->GetKind() != DXIL::SemanticKind::Arbitrary && |
| (!isPatchConstantFunction || |
| (!sigPoint->IsInput() && !sigPoint->IsOutput()))) { |
| auto &SemanticUseMap = |
| sigPoint->IsInput() |
| ? m_InputSemanticsUsed |
| : (sigPoint->IsOutput() ? m_OutputSemanticsUsed[streamIdx] |
| : (sigPoint->IsPatchConstOrPrim() |
| ? m_PatchConstantSemanticsUsed |
| : m_OtherSemanticsUsed)); |
| if (SemanticUseMap.count((unsigned)pSemantic->GetKind()) > 0) { |
| auto &SemanticIndexSet = SemanticUseMap[(unsigned)pSemantic->GetKind()]; |
| for (unsigned idx : paramAnnotation.GetSemanticIndexVec()) { |
| if (SemanticIndexSet.count(idx) > 0) { |
| dxilutil::EmitErrorOnFunction( |
| HLM.GetModule()->getContext(), func, |
| "Parameter with semantic " + semanticStr + |
| " has overlapping semantic index at " + std::to_string(idx) + |
| "."); |
| return; |
| } |
| } |
| } |
| auto &SemanticIndexSet = SemanticUseMap[(unsigned)pSemantic->GetKind()]; |
| for (unsigned idx : paramAnnotation.GetSemanticIndexVec()) { |
| SemanticIndexSet.emplace(idx); |
| } |
| // Enforce Coverage and InnerCoverage input mutual exclusivity |
| if (sigPoint->IsInput()) { |
| if ((pSemantic->GetKind() == DXIL::SemanticKind::Coverage && |
| SemanticUseMap.count((unsigned)DXIL::SemanticKind::InnerCoverage) > |
| 0) || |
| (pSemantic->GetKind() == DXIL::SemanticKind::InnerCoverage && |
| SemanticUseMap.count((unsigned)DXIL::SemanticKind::Coverage) > 0)) { |
| dxilutil::EmitErrorOnFunction( |
| HLM.GetModule()->getContext(), func, |
| "Pixel shader inputs SV_Coverage and SV_InnerCoverage are mutually " |
| "exclusive."); |
| return; |
| } |
| } |
| } |
| |
| // Validate interpretation and replace argument usage with load/store |
| // intrinsics |
| { |
| switch (interpretation) { |
| case DXIL::SemanticInterpretationKind::NA: { |
| dxilutil::EmitErrorOnFunction( |
| HLM.GetModule()->getContext(), func, |
| Twine("Semantic ") + semanticStr + |
| Twine(" is invalid for shader model: ") + |
| ShaderModel::GetKindName(props.shaderKind)); |
| |
| return; |
| } |
| case DXIL::SemanticInterpretationKind::NotInSig: |
| case DXIL::SemanticInterpretationKind::Shadow: { |
| IRBuilder<> funcBuilder(func->getEntryBlock().getFirstInsertionPt()); |
| if (DbgDeclareInst *DDI = llvm::FindAllocaDbgDeclare(&arg)) { |
| funcBuilder.SetCurrentDebugLocation(DDI->getDebugLoc()); |
| } |
| replaceInputOutputWithIntrinsic(pSemantic->GetKind(), &arg, HLM.GetOP(), |
| funcBuilder); |
| if (interpretation == DXIL::SemanticInterpretationKind::NotInSig) |
| return; // This argument should not be included in the signature |
| break; |
| } |
| case DXIL::SemanticInterpretationKind::SV: |
| case DXIL::SemanticInterpretationKind::SGV: |
| case DXIL::SemanticInterpretationKind::Arb: |
| case DXIL::SemanticInterpretationKind::Target: |
| case DXIL::SemanticInterpretationKind::TessFactor: |
| case DXIL::SemanticInterpretationKind::NotPacked: |
| case DXIL::SemanticInterpretationKind::ClipCull: |
| // Will be replaced with load/store intrinsics in |
| // GenerateDxilInputsOutputs |
| break; |
| default: |
| DXASSERT(false, "Unexpected SemanticInterpretationKind"); |
| return; |
| } |
| } |
| |
| // Determine signature this argument belongs in, if any |
| DxilSignature *pSig = nullptr; |
| DXIL::SignatureKind sigKind = sigPoint->GetSignatureKindWithFallback(); |
| switch (sigKind) { |
| case DXIL::SignatureKind::Input: |
| pSig = &EntrySig.InputSignature; |
| break; |
| case DXIL::SignatureKind::Output: |
| pSig = &EntrySig.OutputSignature; |
| break; |
| case DXIL::SignatureKind::PatchConstOrPrim: |
| pSig = &EntrySig.PatchConstOrPrimSignature; |
| break; |
| default: |
| DXASSERT(false, "Expected real signature kind at this point"); |
| return; // No corresponding signature |
| } |
| |
| // Create and add element to signature |
| DxilSignatureElement *pSE = nullptr; |
| { |
| // Add signature element to appropriate maps |
| if (isPatchConstantFunction && |
| sigKind != DXIL::SignatureKind::PatchConstOrPrim) { |
| pSE = FindArgInSignature(arg, paramAnnotation.GetSemanticString(), |
| interpMode, sigPoint->GetKind(), *pSig); |
| if (!pSE) { |
| dxilutil::EmitErrorOnFunction( |
| HLM.GetModule()->getContext(), func, |
| Twine("Signature element ") + semanticStr + |
| Twine( |
| ", referred to by patch constant function, is not found in " |
| "corresponding hull shader ") + |
| (sigKind == DXIL::SignatureKind::Input ? "input." : "output.")); |
| return; |
| } |
| m_patchConstantInputsSigMap[arg.getArgNo()] = pSE; |
| } else { |
| std::unique_ptr<DxilSignatureElement> SE = pSig->CreateElement(); |
| pSE = SE.get(); |
| pSig->AppendElement(std::move(SE)); |
| pSE->SetSigPointKind(sigPoint->GetKind()); |
| pSE->Initialize(semanticStr, EltTy, interpMode, rows, cols, |
| Semantic::kUndefinedRow, Semantic::kUndefinedCol, |
| pSE->GetID(), paramAnnotation.GetSemanticIndexVec()); |
| m_sigValueMap[pSE] = &arg; |
| } |
| } |
| |
| if (paramAnnotation.IsPrecise()) |
| m_preciseSigSet.insert(pSE); |
| if (sigKind == DXIL::SignatureKind::Output && |
| pSemantic->GetKind() == Semantic::Kind::Position && hasClipPlane) { |
| GenerateClipPlanesForVS(&arg); |
| hasClipPlane = false; |
| } |
| |
| // Set Output Stream. |
| if (streamIdx > 0) |
| pSE->SetOutputStream(streamIdx); |
| } |
| |
| void HLSignatureLower::CreateDxilSignatures() { |
| DxilFunctionProps &props = HLM.GetDxilFunctionProps(Entry); |
| const ShaderModel *pSM = HLM.GetShaderModel(); |
| |
| DXASSERT(Entry->getReturnType()->isVoidTy(), |
| "Should changed in SROA_Parameter_HLSL"); |
| |
| DxilFunctionAnnotation *EntryAnnotation = HLM.GetFunctionAnnotation(Entry); |
| DXASSERT(EntryAnnotation, "must have function annotation for entry function"); |
| bool bHasClipPlane = |
| props.shaderKind == DXIL::ShaderKind::Vertex ? HasClipPlanes() : false; |
| const bool isPatchConstantFunctionFalse = false; |
| const bool bForOutFasle = false; |
| for (Argument &arg : Entry->getArgumentList()) { |
| Type *Ty = arg.getType(); |
| // Skip streamout obj. |
| if (HLModule::IsStreamOutputPtrType(Ty)) |
| continue; |
| |
| // Skip OutIndices and InPayload |
| DxilParameterAnnotation ¶mAnnotation = |
| EntryAnnotation->GetParameterAnnotation(arg.getArgNo()); |
| hlsl::DxilParamInputQual qual = paramAnnotation.GetParamInputQual(); |
| if (qual == hlsl::DxilParamInputQual::OutIndices || |
| qual == hlsl::DxilParamInputQual::InPayload) |
| continue; |
| |
| ProcessArgument(Entry, EntryAnnotation, arg, props, pSM, |
| isPatchConstantFunctionFalse, bForOutFasle, bHasClipPlane); |
| } |
| |
| if (bHasClipPlane) { |
| dxilutil::EmitErrorOnFunction(HLM.GetModule()->getContext(), Entry, |
| "Cannot use clipplanes attribute without " |
| "specifying a 4-component SV_Position " |
| "output"); |
| } |
| |
| m_OtherSemanticsUsed.clear(); |
| |
| if (props.shaderKind == DXIL::ShaderKind::Hull) { |
| Function *patchConstantFunc = props.ShaderProps.HS.patchConstantFunc; |
| if (patchConstantFunc == nullptr) { |
| llvm_unreachable("Patch constant function is not specified."); |
| } |
| |
| DxilFunctionAnnotation *patchFuncAnnotation = |
| HLM.GetFunctionAnnotation(patchConstantFunc); |
| DXASSERT(patchFuncAnnotation, |
| "must have function annotation for patch constant function"); |
| const bool isPatchConstantFunctionTrue = true; |
| for (Argument &arg : patchConstantFunc->getArgumentList()) { |
| ProcessArgument(patchConstantFunc, patchFuncAnnotation, arg, props, pSM, |
| isPatchConstantFunctionTrue, bForOutFasle, bHasClipPlane); |
| } |
| } |
| } |
| |
| // Allocate input/output slots |
| void HLSignatureLower::AllocateDxilInputOutputs() { |
| DxilFunctionProps &props = HLM.GetDxilFunctionProps(Entry); |
| const ShaderModel *pSM = HLM.GetShaderModel(); |
| const HLOptions &opts = HLM.GetHLOptions(); |
| DXASSERT_NOMSG(opts.PackingStrategy < |
| (unsigned)DXIL::PackingStrategy::Invalid); |
| DXIL::PackingStrategy packing = (DXIL::PackingStrategy)opts.PackingStrategy; |
| if (packing == DXIL::PackingStrategy::Default) |
| packing = pSM->GetDefaultPackingStrategy(); |
| |
| hlsl::PackDxilSignature(EntrySig.InputSignature, packing); |
| if (!EntrySig.InputSignature.IsFullyAllocated()) { |
| llvm_unreachable( |
| "Failed to allocate all input signature elements in available space."); |
| } |
| |
| if (props.shaderKind != DXIL::ShaderKind::Amplification) { |
| hlsl::PackDxilSignature(EntrySig.OutputSignature, packing); |
| if (!EntrySig.OutputSignature.IsFullyAllocated()) { |
| llvm_unreachable("Failed to allocate all output signature elements in " |
| "available space."); |
| } |
| } |
| |
| if (props.shaderKind == DXIL::ShaderKind::Hull || |
| props.shaderKind == DXIL::ShaderKind::Domain || |
| props.shaderKind == DXIL::ShaderKind::Mesh) { |
| hlsl::PackDxilSignature(EntrySig.PatchConstOrPrimSignature, packing); |
| if (!EntrySig.PatchConstOrPrimSignature.IsFullyAllocated()) { |
| llvm_unreachable("Failed to allocate all patch constant signature " |
| "elements in available space."); |
| } |
| } |
| } |
| |
| namespace { |
| // Helper functions and class for lower signature. |
| void GenerateStOutput(Function *stOutput, MutableArrayRef<Value *> args, |
| IRBuilder<> &Builder, bool cast) { |
| if (cast) { |
| Value *value = args[DXIL::OperandIndex::kStoreOutputValOpIdx]; |
| args[DXIL::OperandIndex::kStoreOutputValOpIdx] = |
| Builder.CreateZExt(value, Builder.getInt32Ty()); |
| } |
| |
| Builder.CreateCall(stOutput, args); |
| } |
| |
| void replaceStWithStOutput(Function *stOutput, StoreInst *stInst, |
| Constant *OpArg, Constant *outputID, Value *idx, |
| unsigned cols, Value *vertexOrPrimID, bool bI1Cast) { |
| IRBuilder<> Builder(stInst); |
| Value *val = stInst->getValueOperand(); |
| |
| if (VectorType *VT = dyn_cast<VectorType>(val->getType())) { |
| DXASSERT_LOCALVAR(VT, cols == VT->getNumElements(), "vec size must match"); |
| for (unsigned col = 0; col < cols; col++) { |
| Value *subVal = Builder.CreateExtractElement(val, col); |
| Value *colIdx = Builder.getInt8(col); |
| SmallVector<Value *, 4> args = {OpArg, outputID, idx, colIdx, subVal}; |
| if (vertexOrPrimID) |
| args.emplace_back(vertexOrPrimID); |
| GenerateStOutput(stOutput, args, Builder, bI1Cast); |
| } |
| // remove stInst |
| stInst->eraseFromParent(); |
| } else if (!val->getType()->isArrayTy()) { |
| // TODO: support case cols not 1 |
| DXASSERT(cols == 1, "only support scalar here"); |
| Value *colIdx = Builder.getInt8(0); |
| SmallVector<Value *, 4> args = {OpArg, outputID, idx, colIdx, val}; |
| if (vertexOrPrimID) |
| args.emplace_back(vertexOrPrimID); |
| GenerateStOutput(stOutput, args, Builder, bI1Cast); |
| // remove stInst |
| stInst->eraseFromParent(); |
| } else { |
| DXASSERT(0, "not support array yet"); |
| // TODO: support array. |
| Value *colIdx = Builder.getInt8(0); |
| ArrayType *AT = cast<ArrayType>(val->getType()); |
| Value *args[] = {OpArg, outputID, idx, colIdx, /*val*/ nullptr}; |
| (void)args; |
| (void)AT; |
| } |
| } |
| |
| Value *GenerateLdInput(Function *loadInput, ArrayRef<Value *> args, |
| IRBuilder<> &Builder, Value *zero, bool bCast, |
| Type *Ty) { |
| Value *input = Builder.CreateCall(loadInput, args); |
| if (!bCast) |
| return input; |
| else { |
| Value *bVal = Builder.CreateICmpNE(input, zero); |
| IntegerType *IT = cast<IntegerType>(Ty); |
| if (IT->getBitWidth() == 1) |
| return bVal; |
| else |
| return Builder.CreateZExt(bVal, Ty); |
| } |
| } |
| |
| Value *replaceLdWithLdInput(Function *loadInput, LoadInst *ldInst, |
| unsigned cols, MutableArrayRef<Value *> args, |
| bool bCast) { |
| IRBuilder<> Builder(ldInst); |
| IRBuilder<> AllocaBuilder(dxilutil::FindAllocaInsertionPt(ldInst)); |
| Type *Ty = ldInst->getType(); |
| Type *EltTy = Ty->getScalarType(); |
| // Change i1 to i32 for load input. |
| Value *zero = Builder.getInt32(0); |
| |
| if (VectorType *VT = dyn_cast<VectorType>(Ty)) { |
| Value *newVec = llvm::UndefValue::get(VT); |
| DXASSERT(cols == VT->getNumElements(), "vec size must match"); |
| for (unsigned col = 0; col < cols; col++) { |
| Value *colIdx = Builder.getInt8(col); |
| args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx; |
| Value *input = |
| GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy); |
| newVec = Builder.CreateInsertElement(newVec, input, col); |
| } |
| ldInst->replaceAllUsesWith(newVec); |
| ldInst->eraseFromParent(); |
| return newVec; |
| } else { |
| Value *colIdx = args[DXIL::OperandIndex::kLoadInputColOpIdx]; |
| if (colIdx == nullptr) { |
| DXASSERT(cols == 1, "only support scalar here"); |
| colIdx = Builder.getInt8(0); |
| } else { |
| if (colIdx->getType() == Builder.getInt32Ty()) { |
| colIdx = Builder.CreateTrunc(colIdx, Builder.getInt8Ty()); |
| } |
| } |
| |
| if (isa<ConstantInt>(colIdx)) { |
| args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx; |
| Value *input = |
| GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy); |
| ldInst->replaceAllUsesWith(input); |
| ldInst->eraseFromParent(); |
| return input; |
| } else { |
| // Vector indexing. |
| // Load to array. |
| ArrayType *AT = ArrayType::get(ldInst->getType(), cols); |
| Value *arrayVec = AllocaBuilder.CreateAlloca(AT); |
| Value *zeroIdx = Builder.getInt32(0); |
| |
| for (unsigned col = 0; col < cols; col++) { |
| Value *colIdx = Builder.getInt8(col); |
| args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx; |
| Value *input = |
| GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy); |
| Value *GEP = Builder.CreateInBoundsGEP(arrayVec, {zeroIdx, colIdx}); |
| Builder.CreateStore(input, GEP); |
| } |
| Value *vecIndexingPtr = |
| Builder.CreateInBoundsGEP(arrayVec, {zeroIdx, colIdx}); |
| Value *input = Builder.CreateLoad(vecIndexingPtr); |
| ldInst->replaceAllUsesWith(input); |
| ldInst->eraseFromParent(); |
| return input; |
| } |
| } |
| } |
| |
| void replaceMatStWithStOutputs(CallInst *CI, HLMatLoadStoreOpcode matOp, |
| Function *ldStFunc, Constant *OpArg, |
| Constant *ID, Constant *columnConsts[], |
| Value *vertexOrPrimID, Value *idxVal) { |
| IRBuilder<> LocalBuilder(CI); |
| Value *Val = CI->getArgOperand(HLOperandIndex::kMatStoreValOpIdx); |
| HLMatrixType MatTy = |
| HLMatrixType::cast(CI->getArgOperand(HLOperandIndex::kMatStoreDstPtrOpIdx) |
| ->getType() |
| ->getPointerElementType()); |
| |
| Val = MatTy.emitLoweredRegToMem(Val, LocalBuilder); |
| |
| if (matOp == HLMatLoadStoreOpcode::ColMatStore) { |
| for (unsigned c = 0; c < MatTy.getNumColumns(); c++) { |
| Constant *constColIdx = LocalBuilder.getInt32(c); |
| Value *colIdx = LocalBuilder.CreateAdd(idxVal, constColIdx); |
| |
| for (unsigned r = 0; r < MatTy.getNumRows(); r++) { |
| unsigned matIdx = MatTy.getColumnMajorIndex(r, c); |
| Value *Elt = LocalBuilder.CreateExtractElement(Val, matIdx); |
| |
| SmallVector<Value *, 6> argList = {OpArg, ID, colIdx, columnConsts[r], |
| Elt}; |
| if (vertexOrPrimID) |
| argList.emplace_back(vertexOrPrimID); |
| LocalBuilder.CreateCall(ldStFunc, argList); |
| } |
| } |
| } else { |
| for (unsigned r = 0; r < MatTy.getNumRows(); r++) { |
| Constant *constRowIdx = LocalBuilder.getInt32(r); |
| Value *rowIdx = LocalBuilder.CreateAdd(idxVal, constRowIdx); |
| for (unsigned c = 0; c < MatTy.getNumColumns(); c++) { |
| unsigned matIdx = MatTy.getRowMajorIndex(r, c); |
| Value *Elt = LocalBuilder.CreateExtractElement(Val, matIdx); |
| |
| SmallVector<Value *, 6> argList = {OpArg, ID, rowIdx, columnConsts[c], |
| Elt}; |
| if (vertexOrPrimID) |
| argList.emplace_back(vertexOrPrimID); |
| LocalBuilder.CreateCall(ldStFunc, argList); |
| } |
| } |
| } |
| CI->eraseFromParent(); |
| } |
| |
| void replaceMatLdWithLdInputs(CallInst *CI, HLMatLoadStoreOpcode matOp, |
| Function *ldStFunc, Constant *OpArg, Constant *ID, |
| Constant *columnConsts[], Value *vertexOrPrimID, |
| Value *idxVal) { |
| IRBuilder<> LocalBuilder(CI); |
| HLMatrixType MatTy = |
| HLMatrixType::cast(CI->getArgOperand(HLOperandIndex::kMatLoadPtrOpIdx) |
| ->getType() |
| ->getPointerElementType()); |
| std::vector<Value *> matElts(MatTy.getNumElements()); |
| |
| if (matOp == HLMatLoadStoreOpcode::ColMatLoad) { |
| for (unsigned c = 0; c < MatTy.getNumColumns(); c++) { |
| Constant *constRowIdx = LocalBuilder.getInt32(c); |
| Value *rowIdx = LocalBuilder.CreateAdd(idxVal, constRowIdx); |
| for (unsigned r = 0; r < MatTy.getNumRows(); r++) { |
| SmallVector<Value *, 4> args = {OpArg, ID, rowIdx, columnConsts[r]}; |
| if (vertexOrPrimID) |
| args.emplace_back(vertexOrPrimID); |
| |
| Value *input = LocalBuilder.CreateCall(ldStFunc, args); |
| unsigned matIdx = MatTy.getColumnMajorIndex(r, c); |
| matElts[matIdx] = input; |
| } |
| } |
| } else { |
| for (unsigned r = 0; r < MatTy.getNumRows(); r++) { |
| Constant *constRowIdx = LocalBuilder.getInt32(r); |
| Value *rowIdx = LocalBuilder.CreateAdd(idxVal, constRowIdx); |
| for (unsigned c = 0; c < MatTy.getNumColumns(); c++) { |
| SmallVector<Value *, 4> args = {OpArg, ID, rowIdx, columnConsts[c]}; |
| if (vertexOrPrimID) |
| args.emplace_back(vertexOrPrimID); |
| |
| Value *input = LocalBuilder.CreateCall(ldStFunc, args); |
| unsigned matIdx = MatTy.getRowMajorIndex(r, c); |
| matElts[matIdx] = input; |
| } |
| } |
| } |
| |
| Value *newVec = |
| HLMatrixLower::BuildVector(matElts[0]->getType(), matElts, LocalBuilder); |
| newVec = MatTy.emitLoweredMemToReg(newVec, LocalBuilder); |
| |
| CI->replaceAllUsesWith(newVec); |
| CI->eraseFromParent(); |
| } |
| |
| void replaceDirectInputParameter(Value *param, Function *loadInput, |
| unsigned cols, MutableArrayRef<Value *> args, |
| bool bCast, OP *hlslOP, IRBuilder<> &Builder) { |
| Value *zero = hlslOP->GetU32Const(0); |
| Type *Ty = param->getType(); |
| Type *EltTy = Ty->getScalarType(); |
| |
| if (VectorType *VT = dyn_cast<VectorType>(Ty)) { |
| Value *newVec = llvm::UndefValue::get(VT); |
| DXASSERT(cols == VT->getNumElements(), "vec size must match"); |
| |
| for (unsigned col = 0; col < cols; col++) { |
| Value *colIdx = hlslOP->GetU8Const(col); |
| args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx; |
| Value *input = |
| GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy); |
| newVec = Builder.CreateInsertElement(newVec, input, col); |
| } |
| |
| param->replaceAllUsesWith(newVec); |
| |
| // THe individual loadInputs are the authoritative source of values for the |
| // vector. |
| dxilutil::TryScatterDebugValueToVectorElements(newVec); |
| } else if (!Ty->isArrayTy() && !HLMatrixType::isa(Ty)) { |
| DXASSERT(cols == 1, "only support scalar here"); |
| Value *colIdx = hlslOP->GetU8Const(0); |
| args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx; |
| Value *input = |
| GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy); |
| param->replaceAllUsesWith(input); // Will properly relocate any DbgValueInst |
| } else if (HLMatrixType::isa(Ty)) { |
| if (param->use_empty()) |
| return; |
| DXASSERT(param->hasOneUse(), |
| "matrix arg should only has one use as matrix to vec"); |
| CallInst *CI = cast<CallInst>(param->user_back()); |
| HLOpcodeGroup group = GetHLOpcodeGroupByName(CI->getCalledFunction()); |
| DXASSERT_LOCALVAR(group, group == HLOpcodeGroup::HLCast, |
| "must be hlcast here"); |
| unsigned opcode = GetHLOpcode(CI); |
| HLCastOpcode matOp = static_cast<HLCastOpcode>(opcode); |
| switch (matOp) { |
| case HLCastOpcode::ColMatrixToVecCast: { |
| IRBuilder<> LocalBuilder(CI); |
| HLMatrixType MatTy = HLMatrixType::cast( |
| CI->getArgOperand(HLOperandIndex::kUnaryOpSrc0Idx)->getType()); |
| Type *EltTy = MatTy.getElementTypeForReg(); |
| std::vector<Value *> matElts(MatTy.getNumElements()); |
| for (unsigned c = 0; c < MatTy.getNumColumns(); c++) { |
| Value *rowIdx = hlslOP->GetI32Const(c); |
| args[DXIL::OperandIndex::kLoadInputRowOpIdx] = rowIdx; |
| for (unsigned r = 0; r < MatTy.getNumRows(); r++) { |
| Value *colIdx = hlslOP->GetU8Const(r); |
| args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx; |
| Value *input = |
| GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy); |
| matElts[MatTy.getColumnMajorIndex(r, c)] = input; |
| } |
| } |
| Value *newVec = HLMatrixLower::BuildVector(EltTy, matElts, LocalBuilder); |
| CI->replaceAllUsesWith(newVec); |
| CI->eraseFromParent(); |
| } break; |
| case HLCastOpcode::RowMatrixToVecCast: { |
| IRBuilder<> LocalBuilder(CI); |
| HLMatrixType MatTy = HLMatrixType::cast( |
| CI->getArgOperand(HLOperandIndex::kUnaryOpSrc0Idx)->getType()); |
| Type *EltTy = MatTy.getElementTypeForReg(); |
| std::vector<Value *> matElts(MatTy.getNumElements()); |
| for (unsigned r = 0; r < MatTy.getNumRows(); r++) { |
| Value *rowIdx = hlslOP->GetI32Const(r); |
| args[DXIL::OperandIndex::kLoadInputRowOpIdx] = rowIdx; |
| for (unsigned c = 0; c < MatTy.getNumColumns(); c++) { |
| Value *colIdx = hlslOP->GetU8Const(c); |
| args[DXIL::OperandIndex::kLoadInputColOpIdx] = colIdx; |
| Value *input = |
| GenerateLdInput(loadInput, args, Builder, zero, bCast, EltTy); |
| matElts[MatTy.getRowMajorIndex(r, c)] = input; |
| } |
| } |
| Value *newVec = HLMatrixLower::BuildVector(EltTy, matElts, LocalBuilder); |
| CI->replaceAllUsesWith(newVec); |
| CI->eraseFromParent(); |
| } break; |
| default: |
| // Only matrix to vector casts are valid. |
| break; |
| } |
| } else { |
| DXASSERT(0, "invalid type for direct input"); |
| } |
| } |
| |
| struct InputOutputAccessInfo { |
| // For input output which has only 1 row, idx is 0. |
| Value *idx; |
| // VertexID for HS/DS/GS input, MS vertex output. PrimitiveID for MS primitive |
| // output |
| Value *vertexOrPrimID; |
| // Vector index. |
| Value *vectorIdx; |
| // Load/Store/LoadMat/StoreMat on input/output. |
| Instruction *user; |
| InputOutputAccessInfo(Value *index, Instruction *I) |
| : idx(index), vertexOrPrimID(nullptr), vectorIdx(nullptr), user(I) {} |
| InputOutputAccessInfo(Value *index, Instruction *I, Value *ID, Value *vecIdx) |
| : idx(index), vertexOrPrimID(ID), vectorIdx(vecIdx), user(I) {} |
| }; |
| |
| void collectInputOutputAccessInfo( |
| Value *GV, Constant *constZero, |
| std::vector<InputOutputAccessInfo> &accessInfoList, bool hasVertexOrPrimID, |
| bool bInput, bool bRowMajor, bool isMS) { |
| // merge GEP use for input output. |
| dxilutil::MergeGepUse(GV); |
| for (auto User = GV->user_begin(); User != GV->user_end();) { |
| Value *I = *(User++); |
| if (LoadInst *ldInst = dyn_cast<LoadInst>(I)) { |
| if (bInput) { |
| InputOutputAccessInfo info = {constZero, ldInst}; |
| accessInfoList.push_back(info); |
| } |
| } else if (StoreInst *stInst = dyn_cast<StoreInst>(I)) { |
| if (!bInput) { |
| InputOutputAccessInfo info = {constZero, stInst}; |
| accessInfoList.push_back(info); |
| } |
| } else if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(I)) { |
| // Vector indexing may has more indices. |
| // Vector indexing changed to array indexing in SROA_HLSL. |
| auto idx = GEP->idx_begin(); |
| DXASSERT_LOCALVAR(idx, idx->get() == constZero, |
| "only support 0 offset for input pointer"); |
| |
| Value *vertexOrPrimID = nullptr; |
| Value *vectorIdx = nullptr; |
| gep_type_iterator GEPIt = gep_type_begin(GEP), E = gep_type_end(GEP); |
| |
| // Skip first pointer idx which must be 0. |
| GEPIt++; |
| if (hasVertexOrPrimID) { |
| // Save vertexOrPrimID. |
| vertexOrPrimID = GEPIt.getOperand(); |
| GEPIt++; |
| } |
| // Start from first index. |
| Value *rowIdx = GEPIt.getOperand(); |
| if (GEPIt != E) { |
| if ((*GEPIt)->isVectorTy()) { |
| // Vector indexing. |
| rowIdx = constZero; |
| vectorIdx = GEPIt.getOperand(); |
| DXASSERT_NOMSG((++GEPIt) == E); |
| } else { |
| // Array which may have vector indexing. |
| // Highest dim index is saved in rowIdx, |
| // array size for highest dim not affect index. |
| GEPIt++; |
| IRBuilder<> Builder(GEP); |
| Type *idxTy = rowIdx->getType(); |
| for (; GEPIt != E; ++GEPIt) { |
| DXASSERT(!GEPIt->isStructTy(), |
| "Struct should be flattened SROA_Parameter_HLSL"); |
| DXASSERT(!GEPIt->isPointerTy(), |
| "not support pointer type in middle of GEP"); |
| if (GEPIt->isArrayTy()) { |
| Constant *arraySize = |
| ConstantInt::get(idxTy, GEPIt->getArrayNumElements()); |
| rowIdx = Builder.CreateMul(rowIdx, arraySize); |
| rowIdx = Builder.CreateAdd(rowIdx, GEPIt.getOperand()); |
| } else { |
| Type *Ty = *GEPIt; |
| DXASSERT_LOCALVAR(Ty, Ty->isVectorTy(), |
| "must be vector type here to index"); |
| // Save vector idx. |
| vectorIdx = GEPIt.getOperand(); |
| } |
| } |
| if (HLMatrixType MatTy = HLMatrixType::dyn_cast(*GEPIt)) { |
| Constant *arraySize = |
| ConstantInt::get(idxTy, MatTy.getNumColumns()); |
| if (bRowMajor) { |
| arraySize = ConstantInt::get(idxTy, MatTy.getNumRows()); |
| } |
| rowIdx = Builder.CreateMul(rowIdx, arraySize); |
| } |
| } |
| } else |
| rowIdx = constZero; |
| |
| auto GepUser = GEP->user_begin(); |
| auto GepUserE = GEP->user_end(); |
| Value *idxVal = rowIdx; |
| |
| for (; GepUser != GepUserE;) { |
| auto GepUserIt = GepUser++; |
| if (LoadInst *ldInst = dyn_cast<LoadInst>(*GepUserIt)) { |
| if (bInput) { |
| InputOutputAccessInfo info = {idxVal, ldInst, vertexOrPrimID, |
| vectorIdx}; |
| accessInfoList.push_back(info); |
| } |
| } else if (StoreInst *stInst = dyn_cast<StoreInst>(*GepUserIt)) { |
| if (!bInput) { |
| InputOutputAccessInfo info = {idxVal, stInst, vertexOrPrimID, |
| vectorIdx}; |
| accessInfoList.push_back(info); |
| } |
| } else if (CallInst *CI = dyn_cast<CallInst>(*GepUserIt)) { |
| HLOpcodeGroup group = GetHLOpcodeGroupByName(CI->getCalledFunction()); |
| DXASSERT_LOCALVAR(group, group == HLOpcodeGroup::HLMatLoadStore, |
| "input/output should only used by ld/st"); |
| HLMatLoadStoreOpcode opcode = (HLMatLoadStoreOpcode)GetHLOpcode(CI); |
| if ((opcode == HLMatLoadStoreOpcode::ColMatLoad || |
| opcode == HLMatLoadStoreOpcode::RowMatLoad) |
| ? bInput |
| : !bInput) { |
| InputOutputAccessInfo info = {idxVal, CI, vertexOrPrimID, |
| vectorIdx}; |
| accessInfoList.push_back(info); |
| } |
| } else { |
| DXASSERT(0, "input output should only used by ld/st"); |
| } |
| } |
| } else if (CallInst *CI = dyn_cast<CallInst>(I)) { |
| InputOutputAccessInfo info = {constZero, CI}; |
| accessInfoList.push_back(info); |
| } else { |
| DXASSERT(0, "input output should only used by ld/st"); |
| } |
| } |
| } |
| |
| void GenerateInputOutputUserCall(InputOutputAccessInfo &info, |
| Value *undefVertexIdx, Function *ldStFunc, |
| Constant *OpArg, Constant *ID, unsigned cols, |
| bool bI1Cast, Constant *columnConsts[], |
| bool bNeedVertexOrPrimID, bool isArrayTy, |
| bool bInput, bool bIsInout) { |
| Value *idxVal = info.idx; |
| Value *vertexOrPrimID = undefVertexIdx; |
| if (bNeedVertexOrPrimID && isArrayTy) { |
| vertexOrPrimID = info.vertexOrPrimID; |
| } |
| |
| if (LoadInst *ldInst = dyn_cast<LoadInst>(info.user)) { |
| SmallVector<Value *, 4> args = {OpArg, ID, idxVal, info.vectorIdx}; |
| if (vertexOrPrimID) |
| args.emplace_back(vertexOrPrimID); |
| |
| replaceLdWithLdInput(ldStFunc, ldInst, cols, args, bI1Cast); |
| } else if (StoreInst *stInst = dyn_cast<StoreInst>(info.user)) { |
| if (bInput) { |
| DXASSERT_LOCALVAR(bIsInout, bIsInout, "input should not have store use."); |
| } else { |
| if (!info.vectorIdx) { |
| replaceStWithStOutput(ldStFunc, stInst, OpArg, ID, idxVal, cols, |
| vertexOrPrimID, bI1Cast); |
| } else { |
| Value *V = stInst->getValueOperand(); |
| Type *Ty = V->getType(); |
| DXASSERT_LOCALVAR(Ty == Ty->getScalarType() && !Ty->isAggregateType(), |
| Ty, "only support scalar here"); |
| |
| if (ConstantInt *ColIdx = dyn_cast<ConstantInt>(info.vectorIdx)) { |
| IRBuilder<> Builder(stInst); |
| if (ColIdx->getType()->getBitWidth() != 8) { |
| ColIdx = Builder.getInt8(ColIdx->getValue().getLimitedValue()); |
| } |
| SmallVector<Value *, 6> args = {OpArg, ID, idxVal, ColIdx, V}; |
| if (vertexOrPrimID) |
| args.emplace_back(vertexOrPrimID); |
| GenerateStOutput(ldStFunc, args, Builder, bI1Cast); |
| } else { |
| BasicBlock *BB = stInst->getParent(); |
| BasicBlock *EndBB = BB->splitBasicBlock(stInst); |
| |
| TerminatorInst *TI = BB->getTerminator(); |
| IRBuilder<> SwitchBuilder(TI); |
| LLVMContext &Ctx = stInst->getContext(); |
| SwitchInst *Switch = |
| SwitchBuilder.CreateSwitch(info.vectorIdx, EndBB, cols); |
| TI->eraseFromParent(); |
| |
| Function *F = EndBB->getParent(); |
| for (unsigned i = 0; i < cols; i++) { |
| BasicBlock *CaseBB = BasicBlock::Create(Ctx, "case", F, EndBB); |
| Switch->addCase(SwitchBuilder.getInt32(i), CaseBB); |
| IRBuilder<> CaseBuilder(CaseBB); |
| |
| ConstantInt *CaseIdx = SwitchBuilder.getInt8(i); |
| |
| SmallVector<Value *, 6> args = {OpArg, ID, idxVal, CaseIdx, V}; |
| if (vertexOrPrimID) |
| args.emplace_back(vertexOrPrimID); |
| GenerateStOutput(ldStFunc, args, CaseBuilder, bI1Cast); |
| |
| CaseBuilder.CreateBr(EndBB); |
| } |
| } |
| // remove stInst |
| stInst->eraseFromParent(); |
| } |
| } |
| } else if (CallInst *CI = dyn_cast<CallInst>(info.user)) { |
| HLOpcodeGroup group = GetHLOpcodeGroupByName(CI->getCalledFunction()); |
| // Intrinsic will be translated later. |
| if (group == HLOpcodeGroup::HLIntrinsic || group == HLOpcodeGroup::NotHL) |
| return; |
| unsigned opcode = GetHLOpcode(CI); |
| DXASSERT_NOMSG(group == HLOpcodeGroup::HLMatLoadStore); |
| HLMatLoadStoreOpcode matOp = static_cast<HLMatLoadStoreOpcode>(opcode); |
| switch (matOp) { |
| case HLMatLoadStoreOpcode::ColMatLoad: |
| case HLMatLoadStoreOpcode::RowMatLoad: { |
| replaceMatLdWithLdInputs(CI, matOp, ldStFunc, OpArg, ID, columnConsts, |
| vertexOrPrimID, idxVal); |
| } break; |
| case HLMatLoadStoreOpcode::ColMatStore: |
| case HLMatLoadStoreOpcode::RowMatStore: { |
| replaceMatStWithStOutputs(CI, matOp, ldStFunc, OpArg, ID, columnConsts, |
| vertexOrPrimID, idxVal); |
| } break; |
| } |
| } else { |
| DXASSERT(0, "invalid operation on input output"); |
| } |
| } |
| |
| } // namespace |
| |
| void HLSignatureLower::GenerateDxilInputs() { |
| GenerateDxilInputsOutputs(DXIL::SignatureKind::Input); |
| } |
| |
| void HLSignatureLower::GenerateDxilOutputs() { |
| GenerateDxilInputsOutputs(DXIL::SignatureKind::Output); |
| } |
| |
| void HLSignatureLower::GenerateDxilPrimOutputs() { |
| GenerateDxilInputsOutputs(DXIL::SignatureKind::PatchConstOrPrim); |
| } |
| |
| void HLSignatureLower::GenerateDxilInputsOutputs(DXIL::SignatureKind SK) { |
| OP *hlslOP = HLM.GetOP(); |
| DxilFunctionProps &props = HLM.GetDxilFunctionProps(Entry); |
| Module &M = *(HLM.GetModule()); |
| |
| OP::OpCode opcode = (OP::OpCode)-1; |
| switch (SK) { |
| case DXIL::SignatureKind::Input: |
| opcode = OP::OpCode::LoadInput; |
| break; |
| case DXIL::SignatureKind::Output: |
| opcode = |
| props.IsMS() ? OP::OpCode::StoreVertexOutput : OP::OpCode::StoreOutput; |
| break; |
| case DXIL::SignatureKind::PatchConstOrPrim: |
| opcode = OP::OpCode::StorePrimitiveOutput; |
| break; |
| default: |
| DXASSERT_NOMSG(0); |
| } |
| bool bInput = SK == DXIL::SignatureKind::Input; |
| bool bNeedVertexOrPrimID = |
| bInput && (props.IsGS() || props.IsDS() || props.IsHS()); |
| bNeedVertexOrPrimID |= !bInput && props.IsMS(); |
| |
| Constant *OpArg = hlslOP->GetU32Const((unsigned)opcode); |
| |
| Constant *columnConsts[] = { |
| hlslOP->GetU8Const(0), hlslOP->GetU8Const(1), hlslOP->GetU8Const(2), |
| hlslOP->GetU8Const(3), hlslOP->GetU8Const(4), hlslOP->GetU8Const(5), |
| hlslOP->GetU8Const(6), hlslOP->GetU8Const(7), hlslOP->GetU8Const(8), |
| hlslOP->GetU8Const(9), hlslOP->GetU8Const(10), hlslOP->GetU8Const(11), |
| hlslOP->GetU8Const(12), hlslOP->GetU8Const(13), hlslOP->GetU8Const(14), |
| hlslOP->GetU8Const(15)}; |
| |
| Constant *constZero = hlslOP->GetU32Const(0); |
| |
| Value *undefVertexIdx = props.IsMS() || !bInput |
| ? nullptr |
| : UndefValue::get(Type::getInt32Ty(HLM.GetCtx())); |
| |
| DxilSignature &Sig = bInput ? EntrySig.InputSignature |
| : SK == DXIL::SignatureKind::Output |
| ? EntrySig.OutputSignature |
| : EntrySig.PatchConstOrPrimSignature; |
| |
| DxilTypeSystem &typeSys = HLM.GetTypeSystem(); |
| DxilFunctionAnnotation *pFuncAnnot = typeSys.GetFunctionAnnotation(Entry); |
| |
| Type *i1Ty = Type::getInt1Ty(constZero->getContext()); |
| Type *i32Ty = constZero->getType(); |
| |
| llvm::SmallVector<unsigned, 8> removeIndices; |
| for (unsigned i = 0; i < Sig.GetElements().size(); i++) { |
| DxilSignatureElement *SE = &Sig.GetElement(i); |
| llvm::Type *Ty = SE->GetCompType().GetLLVMType(HLM.GetCtx()); |
| // Cast i1 to i32 for load input. |
| bool bI1Cast = false; |
| if (Ty == i1Ty) { |
| bI1Cast = true; |
| Ty = i32Ty; |
| } |
| if (!hlslOP->IsOverloadLegal(opcode, Ty)) { |
| std::string O; |
| raw_string_ostream OSS(O); |
| Ty->print(OSS); |
| OSS << "(type for " << SE->GetName() << ")"; |
| OSS << " cannot be used as shader inputs or outputs."; |
| OSS.flush(); |
| dxilutil::EmitErrorOnFunction(M.getContext(), Entry, O); |
| continue; |
| } |
| Function *dxilFunc = hlslOP->GetOpFunc(opcode, Ty); |
| Constant *ID = hlslOP->GetU32Const(i); |
| unsigned cols = SE->GetCols(); |
| Value *GV = m_sigValueMap[SE]; |
| |
| bool bIsInout = m_inoutArgSet.count(GV) > 0; |
| |
| IRBuilder<> EntryBuilder(Entry->getEntryBlock().getFirstInsertionPt()); |
| |
| if (DbgDeclareInst *DDI = llvm::FindAllocaDbgDeclare(GV)) { |
| EntryBuilder.SetCurrentDebugLocation(DDI->getDebugLoc()); |
| } |
| |
| DXIL::SemanticInterpretationKind SI = SE->GetInterpretation(); |
| DXASSERT_NOMSG(SI < DXIL::SemanticInterpretationKind::Invalid); |
| DXASSERT_NOMSG(SI != DXIL::SemanticInterpretationKind::NA); |
| DXASSERT_NOMSG(SI != DXIL::SemanticInterpretationKind::NotInSig); |
| if (SI == DXIL::SemanticInterpretationKind::Shadow) |
| continue; // Handled in ProcessArgument |
| |
| if (!GV->getType()->isPointerTy()) { |
| DXASSERT(bInput, "direct parameter must be input"); |
| Value *vertexOrPrimID = undefVertexIdx; |
| Value *args[] = {OpArg, ID, /*rowIdx*/ constZero, /*colIdx*/ nullptr, |
| vertexOrPrimID}; |
| replaceDirectInputParameter(GV, dxilFunc, cols, args, bI1Cast, hlslOP, |
| EntryBuilder); |
| continue; |
| } |
| |
| bool bIsArrayTy = GV->getType()->getPointerElementType()->isArrayTy(); |
| bool bIsPrecise = m_preciseSigSet.count(SE); |
| if (bIsPrecise) |
| HLModule::MarkPreciseAttributeOnPtrWithFunctionCall(GV, M); |
| bool bRowMajor = false; |
| if (Argument *Arg = dyn_cast<Argument>(GV)) { |
| if (pFuncAnnot) { |
| auto ¶mAnnot = pFuncAnnot->GetParameterAnnotation(Arg->getArgNo()); |
| if (paramAnnot.HasMatrixAnnotation()) |
| bRowMajor = paramAnnot.GetMatrixAnnotation().Orientation == |
| MatrixOrientation::RowMajor; |
| } |
| } |
| std::vector<InputOutputAccessInfo> accessInfoList; |
| collectInputOutputAccessInfo(GV, constZero, accessInfoList, |
| bNeedVertexOrPrimID && bIsArrayTy, bInput, |
| bRowMajor, props.IsMS()); |
| |
| for (InputOutputAccessInfo &info : accessInfoList) { |
| GenerateInputOutputUserCall( |
| info, undefVertexIdx, dxilFunc, OpArg, ID, cols, bI1Cast, |
| columnConsts, bNeedVertexOrPrimID, bIsArrayTy, bInput, bIsInout); |
| } |
| } |
| } |
| |
| void HLSignatureLower::GenerateDxilComputeAndNodeCommonInputs() { |
| OP *hlslOP = HLM.GetOP(); |
| |
| DxilFunctionAnnotation *funcAnnotation = HLM.GetFunctionAnnotation(Entry); |
| DXASSERT(funcAnnotation, "must find annotation for entry function"); |
| auto &funcProps = HLM.GetDxilFunctionProps(Entry); |
| IRBuilder<> Builder(Entry->getEntryBlock().getFirstInsertionPt()); |
| |
| for (Argument &arg : Entry->args()) { |
| DxilParameterAnnotation ¶mAnnotation = |
| funcAnnotation->GetParameterAnnotation(arg.getArgNo()); |
| |
| llvm::StringRef semanticStr = paramAnnotation.GetSemanticString(); |
| |
| if (semanticStr.empty()) { |
| if (funcProps.IsNode() && paramAnnotation.IsParamInputQualNode()) |
| continue; |
| dxilutil::EmitErrorOnFunction(HLM.GetModule()->getContext(), Entry, |
| "Semantic must be defined for all " |
| "parameters of an entry function or patch " |
| "constant function."); |
| return; |
| } |
| |
| const Semantic *semantic = |
| Semantic::GetByName(semanticStr, DXIL::SigPointKind::CSIn); |
| OP::OpCode opcode; |
| switch (semantic->GetKind()) { |
| case Semantic::Kind::GroupThreadID: |
| opcode = OP::OpCode::ThreadIdInGroup; |
| break; |
| case Semantic::Kind::GroupID: |
| opcode = OP::OpCode::GroupId; |
| break; |
| case Semantic::Kind::DispatchThreadID: |
| opcode = OP::OpCode::ThreadId; |
| break; |
| case Semantic::Kind::GroupIndex: |
| opcode = OP::OpCode::FlattenedThreadIdInGroup; |
| break; |
| default: |
| DXASSERT(semantic->IsInvalid(), |
| "else compute shader semantics out-of-date"); |
| dxilutil::EmitErrorOnFunction(HLM.GetModule()->getContext(), Entry, |
| "invalid semantic found in CS"); |
| return; |
| } |
| |
| Constant *OpArg = hlslOP->GetU32Const((unsigned)opcode); |
| Type *NumTy = arg.getType(); |
| DXASSERT(!NumTy->isPointerTy(), |
| "Unexpected byref value for CS SV_***ID semantic."); |
| DXASSERT(NumTy->getScalarType()->isIntegerTy(), |
| "Unexpected non-integer value for CS SV_***ID semantic."); |
| |
| // Always use the i32 overload of those intrinsics, and then cast as needed |
| Function *dxilFunc = hlslOP->GetOpFunc(opcode, Builder.getInt32Ty()); |
| Value *newArg = nullptr; |
| if (opcode == OP::OpCode::FlattenedThreadIdInGroup) { |
| newArg = Builder.CreateCall(dxilFunc, {OpArg}); |
| } else { |
| unsigned vecSize = 1; |
| if (FixedVectorType *VT = dyn_cast<FixedVectorType>(NumTy)) |
| vecSize = VT->getNumElements(); |
| |
| newArg = Builder.CreateCall(dxilFunc, {OpArg, hlslOP->GetU32Const(0)}); |
| if (vecSize > 1) { |
| Value *result = |
| UndefValue::get(VectorType::get(Builder.getInt32Ty(), vecSize)); |
| result = Builder.CreateInsertElement(result, newArg, (uint64_t)0); |
| |
| for (unsigned i = 1; i < vecSize; i++) { |
| Value *newElt = |
| Builder.CreateCall(dxilFunc, {OpArg, hlslOP->GetU32Const(i)}); |
| result = Builder.CreateInsertElement(result, newElt, i); |
| } |
| newArg = result; |
| } |
| } |
| |
| // If the argument is of non-i32 type, convert here |
| if (newArg->getType() != NumTy) |
| newArg = Builder.CreateZExtOrTrunc(newArg, NumTy); |
| |
| if (newArg->getType() != arg.getType()) { |
| DXASSERT_NOMSG(arg.getType()->isPointerTy()); |
| for (User *U : arg.users()) { |
| LoadInst *LI = cast<LoadInst>(U); |
| LI->replaceAllUsesWith(newArg); |
| } |
| } else { |
| arg.replaceAllUsesWith(newArg); |
| } |
| } |
| } |
| |
| void HLSignatureLower::GenerateDxilPatchConstantLdSt() { |
| OP *hlslOP = HLM.GetOP(); |
| DxilFunctionProps &props = HLM.GetDxilFunctionProps(Entry); |
| Module &M = *(HLM.GetModule()); |
| Constant *constZero = hlslOP->GetU32Const(0); |
| DxilSignature &Sig = EntrySig.PatchConstOrPrimSignature; |
| DxilTypeSystem &typeSys = HLM.GetTypeSystem(); |
| DxilFunctionAnnotation *pFuncAnnot = typeSys.GetFunctionAnnotation(Entry); |
| auto InsertPt = Entry->getEntryBlock().getFirstInsertionPt(); |
| const bool bIsHs = props.IsHS(); |
| const bool bIsInput = !bIsHs; |
| const bool bIsInout = false; |
| const bool bNeedVertexOrPrimID = false; |
| if (bIsHs) { |
| DxilFunctionProps &EntryQual = HLM.GetDxilFunctionProps(Entry); |
| Function *patchConstantFunc = EntryQual.ShaderProps.HS.patchConstantFunc; |
| InsertPt = patchConstantFunc->getEntryBlock().getFirstInsertionPt(); |
| pFuncAnnot = typeSys.GetFunctionAnnotation(patchConstantFunc); |
| } |
| IRBuilder<> Builder(InsertPt); |
| Type *i1Ty = Builder.getInt1Ty(); |
| Type *i32Ty = Builder.getInt32Ty(); |
| // LoadPatchConst don't have vertexIdx operand. |
| Value *undefVertexIdx = nullptr; |
| |
| Constant *columnConsts[] = { |
| hlslOP->GetU8Const(0), hlslOP->GetU8Const(1), hlslOP->GetU8Const(2), |
| hlslOP->GetU8Const(3), hlslOP->GetU8Const(4), hlslOP->GetU8Const(5), |
| hlslOP->GetU8Const(6), hlslOP->GetU8Const(7), hlslOP->GetU8Const(8), |
| hlslOP->GetU8Const(9), hlslOP->GetU8Const(10), hlslOP->GetU8Const(11), |
| hlslOP->GetU8Const(12), hlslOP->GetU8Const(13), hlslOP->GetU8Const(14), |
| hlslOP->GetU8Const(15)}; |
| |
| OP::OpCode opcode = |
| bIsInput ? OP::OpCode::LoadPatchConstant : OP::OpCode::StorePatchConstant; |
| Constant *OpArg = hlslOP->GetU32Const((unsigned)opcode); |
| |
| for (unsigned i = 0; i < Sig.GetElements().size(); i++) { |
| DxilSignatureElement *SE = &Sig.GetElement(i); |
| Value *GV = m_sigValueMap[SE]; |
| |
| DXIL::SemanticInterpretationKind SI = SE->GetInterpretation(); |
| DXASSERT_NOMSG(SI < DXIL::SemanticInterpretationKind::Invalid); |
| DXASSERT_NOMSG(SI != DXIL::SemanticInterpretationKind::NA); |
| DXASSERT_NOMSG(SI != DXIL::SemanticInterpretationKind::NotInSig); |
| if (SI == DXIL::SemanticInterpretationKind::Shadow) |
| continue; // Handled in ProcessArgument |
| |
| Constant *ID = hlslOP->GetU32Const(i); |
| // Generate LoadPatchConstant. |
| Type *Ty = SE->GetCompType().GetLLVMType(HLM.GetCtx()); |
| // Cast i1 to i32 for load input. |
| bool bI1Cast = false; |
| if (Ty == i1Ty) { |
| bI1Cast = true; |
| Ty = i32Ty; |
| } |
| |
| unsigned cols = SE->GetCols(); |
| |
| Function *dxilFunc = hlslOP->GetOpFunc(opcode, Ty); |
| |
| if (!GV->getType()->isPointerTy()) { |
| DXASSERT(bIsInput, "Must be DS input."); |
| Constant *OpArg = hlslOP->GetU32Const( |
| static_cast<unsigned>(OP::OpCode::LoadPatchConstant)); |
| Value *args[] = {OpArg, ID, /*rowIdx*/ constZero, /*colIdx*/ nullptr}; |
| replaceDirectInputParameter(GV, dxilFunc, cols, args, bI1Cast, hlslOP, |
| Builder); |
| continue; |
| } |
| bool bRowMajor = false; |
| if (Argument *Arg = dyn_cast<Argument>(GV)) { |
| if (pFuncAnnot) { |
| auto ¶mAnnot = pFuncAnnot->GetParameterAnnotation(Arg->getArgNo()); |
| if (paramAnnot.HasMatrixAnnotation()) |
| bRowMajor = paramAnnot.GetMatrixAnnotation().Orientation == |
| MatrixOrientation::RowMajor; |
| } |
| } |
| std::vector<InputOutputAccessInfo> accessInfoList; |
| collectInputOutputAccessInfo(GV, constZero, accessInfoList, |
| bNeedVertexOrPrimID, bIsInput, bRowMajor, |
| false); |
| |
| bool bIsArrayTy = GV->getType()->getPointerElementType()->isArrayTy(); |
| bool isPrecise = m_preciseSigSet.count(SE); |
| if (isPrecise) |
| HLModule::MarkPreciseAttributeOnPtrWithFunctionCall(GV, M); |
| |
| for (InputOutputAccessInfo &info : accessInfoList) { |
| GenerateInputOutputUserCall( |
| info, undefVertexIdx, dxilFunc, OpArg, ID, cols, bI1Cast, |
| columnConsts, bNeedVertexOrPrimID, bIsArrayTy, bIsInput, bIsInout); |
| } |
| } |
| } |
| |
| void HLSignatureLower::GenerateDxilPatchConstantFunctionInputs() { |
| // Map input patch, to input sig |
| // LoadOutputControlPoint for output patch . |
| OP *hlslOP = HLM.GetOP(); |
| Constant *constZero = hlslOP->GetU32Const(0); |
| |
| DxilFunctionProps &EntryQual = HLM.GetDxilFunctionProps(Entry); |
| |
| Function *patchConstantFunc = EntryQual.ShaderProps.HS.patchConstantFunc; |
| DxilFunctionAnnotation *patchFuncAnnotation = |
| HLM.GetFunctionAnnotation(patchConstantFunc); |
| DXASSERT(patchFuncAnnotation, |
| "must find annotation for patch constant function"); |
| Type *i1Ty = Type::getInt1Ty(constZero->getContext()); |
| Type *i32Ty = constZero->getType(); |
| |
| Constant *columnConsts[] = { |
| hlslOP->GetU8Const(0), hlslOP->GetU8Const(1), hlslOP->GetU8Const(2), |
| hlslOP->GetU8Const(3), hlslOP->GetU8Const(4), hlslOP->GetU8Const(5), |
| hlslOP->GetU8Const(6), hlslOP->GetU8Const(7), hlslOP->GetU8Const(8), |
| hlslOP->GetU8Const(9), hlslOP->GetU8Const(10), hlslOP->GetU8Const(11), |
| hlslOP->GetU8Const(12), hlslOP->GetU8Const(13), hlslOP->GetU8Const(14), |
| hlslOP->GetU8Const(15)}; |
| |
| for (Argument &arg : patchConstantFunc->args()) { |
| DxilParameterAnnotation ¶mAnnotation = |
| patchFuncAnnotation->GetParameterAnnotation(arg.getArgNo()); |
| DxilParamInputQual inputQual = paramAnnotation.GetParamInputQual(); |
| if (inputQual == DxilParamInputQual::InputPatch || |
| inputQual == DxilParamInputQual::OutputPatch) { |
| DxilSignatureElement *SE = m_patchConstantInputsSigMap[arg.getArgNo()]; |
| if (!SE) // Error should have been reported at an earlier stage. |
| continue; |
| |
| Constant *inputID = hlslOP->GetU32Const(SE->GetID()); |
| unsigned cols = SE->GetCols(); |
| Type *Ty = SE->GetCompType().GetLLVMType(HLM.GetCtx()); |
| // Cast i1 to i32 for load input. |
| bool bI1Cast = false; |
| if (Ty == i1Ty) { |
| bI1Cast = true; |
| Ty = i32Ty; |
| } |
| OP::OpCode opcode = inputQual == DxilParamInputQual::InputPatch |
| ? OP::OpCode::LoadInput |
| : OP::OpCode::LoadOutputControlPoint; |
| Function *dxilLdFunc = hlslOP->GetOpFunc(opcode, Ty); |
| bool bRowMajor = false; |
| if (Argument *Arg = dyn_cast<Argument>(&arg)) { |
| if (patchFuncAnnotation) { |
| auto ¶mAnnot = |
| patchFuncAnnotation->GetParameterAnnotation(Arg->getArgNo()); |
| if (paramAnnot.HasMatrixAnnotation()) |
| bRowMajor = paramAnnot.GetMatrixAnnotation().Orientation == |
| MatrixOrientation::RowMajor; |
| } |
| } |
| std::vector<InputOutputAccessInfo> accessInfoList; |
| collectInputOutputAccessInfo(&arg, constZero, accessInfoList, |
| /*hasVertexOrPrimID*/ true, true, bRowMajor, |
| false); |
| for (InputOutputAccessInfo &info : accessInfoList) { |
| Constant *OpArg = hlslOP->GetU32Const((unsigned)opcode); |
| if (LoadInst *ldInst = dyn_cast<LoadInst>(info.user)) { |
| Value *args[] = {OpArg, inputID, info.idx, info.vectorIdx, |
| info.vertexOrPrimID}; |
| replaceLdWithLdInput(dxilLdFunc, ldInst, cols, args, bI1Cast); |
| } else if (CallInst *CI = dyn_cast<CallInst>(info.user)) { |
| HLOpcodeGroup group = GetHLOpcodeGroupByName(CI->getCalledFunction()); |
| // Intrinsic will be translated later. |
| if (group == HLOpcodeGroup::HLIntrinsic || |
| group == HLOpcodeGroup::NotHL) |
| return; |
| unsigned opcode = GetHLOpcode(CI); |
| DXASSERT_NOMSG(group == HLOpcodeGroup::HLMatLoadStore); |
| HLMatLoadStoreOpcode matOp = |
| static_cast<HLMatLoadStoreOpcode>(opcode); |
| if (matOp == HLMatLoadStoreOpcode::ColMatLoad || |
| matOp == HLMatLoadStoreOpcode::RowMatLoad) |
| replaceMatLdWithLdInputs(CI, matOp, dxilLdFunc, OpArg, inputID, |
| columnConsts, info.vertexOrPrimID, |
| info.idx); |
| } else { |
| DXASSERT(0, "input should only be ld"); |
| } |
| } |
| } |
| } |
| } |
| |
| bool HLSignatureLower::HasClipPlanes() { |
| if (!HLM.HasDxilFunctionProps(Entry)) |
| return false; |
| |
| DxilFunctionProps &EntryQual = HLM.GetDxilFunctionProps(Entry); |
| auto &VS = EntryQual.ShaderProps.VS; |
| unsigned numClipPlanes = 0; |
| |
| for (unsigned i = 0; i < DXIL::kNumClipPlanes; i++) { |
| if (!VS.clipPlanes[i]) |
| break; |
| numClipPlanes++; |
| } |
| |
| return numClipPlanes != 0; |
| } |
| |
| void HLSignatureLower::GenerateClipPlanesForVS(Value *outPosition) { |
| DxilFunctionProps &EntryQual = HLM.GetDxilFunctionProps(Entry); |
| auto &VS = EntryQual.ShaderProps.VS; |
| unsigned numClipPlanes = 0; |
| |
| for (unsigned i = 0; i < DXIL::kNumClipPlanes; i++) { |
| if (!VS.clipPlanes[i]) |
| break; |
| numClipPlanes++; |
| } |
| |
| if (!numClipPlanes) |
| return; |
| |
| LLVMContext &Ctx = HLM.GetCtx(); |
| |
| Function *dp4 = |
| HLM.GetOP()->GetOpFunc(DXIL::OpCode::Dot4, Type::getFloatTy(Ctx)); |
| Value *dp4Args[] = { |
| ConstantInt::get(Type::getInt32Ty(Ctx), |
| static_cast<unsigned>(DXIL::OpCode::Dot4)), |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| }; |
| |
| // out SV_Position should only have StoreInst use. |
| // Done by LegalizeDxilInputOutputs in ScalarReplAggregatesHLSL.cpp |
| for (User *U : outPosition->users()) { |
| StoreInst *ST = cast<StoreInst>(U); |
| Value *posVal = ST->getValueOperand(); |
| DXASSERT(posVal->getType()->isVectorTy(), "SV_Position must be a vector"); |
| IRBuilder<> Builder(ST); |
| // Put position to args. |
| for (unsigned i = 0; i < 4; i++) |
| dp4Args[i + 1] = Builder.CreateExtractElement(posVal, i); |
| |
| // For each clip plane. |
| // clipDistance = dp4 position, clipPlane. |
| auto argIt = Entry->getArgumentList().rbegin(); |
| |
| for (int clipIdx = numClipPlanes - 1; clipIdx >= 0; clipIdx--) { |
| Constant *GV = VS.clipPlanes[clipIdx]; |
| DXASSERT_NOMSG(GV->hasOneUse()); |
| StoreInst *ST = cast<StoreInst>(GV->user_back()); |
| Value *clipPlane = ST->getValueOperand(); |
| ST->eraseFromParent(); |
| |
| Argument &arg = *(argIt++); |
| |
| // Put clipPlane to args. |
| for (unsigned i = 0; i < 4; i++) |
| dp4Args[i + 5] = Builder.CreateExtractElement(clipPlane, i); |
| |
| Value *clipDistance = Builder.CreateCall(dp4, dp4Args); |
| Builder.CreateStore(clipDistance, &arg); |
| } |
| } |
| } |
| |
| namespace { |
| Value *TranslateStreamAppend(CallInst *CI, unsigned ID, hlsl::OP *OP) { |
| Function *DxilFunc = OP->GetOpFunc(OP::OpCode::EmitStream, CI->getType()); |
| // TODO: generate a emit which has the data being emited as its argment. |
| // Value *data = CI->getArgOperand(HLOperandIndex::kStreamAppendDataOpIndex); |
| Constant *opArg = OP->GetU32Const((unsigned)OP::OpCode::EmitStream); |
| IRBuilder<> Builder(CI); |
| |
| Constant *streamID = OP->GetU8Const(ID); |
| Value *args[] = {opArg, streamID}; |
| return Builder.CreateCall(DxilFunc, args); |
| } |
| |
| Value *TranslateStreamCut(CallInst *CI, unsigned ID, hlsl::OP *OP) { |
| Function *DxilFunc = OP->GetOpFunc(OP::OpCode::CutStream, CI->getType()); |
| // TODO: generate a emit which has the data being emited as its argment. |
| // Value *data = CI->getArgOperand(HLOperandIndex::kStreamAppendDataOpIndex); |
| Constant *opArg = OP->GetU32Const((unsigned)OP::OpCode::CutStream); |
| IRBuilder<> Builder(CI); |
| |
| Constant *streamID = OP->GetU8Const(ID); |
| Value *args[] = {opArg, streamID}; |
| return Builder.CreateCall(DxilFunc, args); |
| } |
| |
| } // namespace |
| |
| // Generate DXIL stream output operation. |
| void HLSignatureLower::GenerateStreamOutputOperation(Value *streamVal, |
| unsigned ID) { |
| OP *hlslOP = HLM.GetOP(); |
| |
| for (auto U = streamVal->user_begin(); U != streamVal->user_end();) { |
| Value *user = *(U++); |
| // Should only used by append, restartStrip . |
| CallInst *CI = cast<CallInst>(user); |
| HLOpcodeGroup group = GetHLOpcodeGroupByName(CI->getCalledFunction()); |
| // Ignore user functions. |
| if (group == HLOpcodeGroup::NotHL) |
| continue; |
| unsigned opcode = GetHLOpcode(CI); |
| DXASSERT_LOCALVAR(group, group == HLOpcodeGroup::HLIntrinsic, |
| "Must be HLIntrinsic here"); |
| IntrinsicOp IOP = static_cast<IntrinsicOp>(opcode); |
| switch (IOP) { |
| case IntrinsicOp::MOP_Append: |
| TranslateStreamAppend(CI, ID, hlslOP); |
| break; |
| case IntrinsicOp::MOP_RestartStrip: |
| TranslateStreamCut(CI, ID, hlslOP); |
| break; |
| default: |
| DXASSERT(0, "invalid operation on stream"); |
| } |
| CI->eraseFromParent(); |
| } |
| } |
| // Generate DXIL stream output operations. |
| void HLSignatureLower::GenerateStreamOutputOperations() { |
| DxilFunctionAnnotation *EntryAnnotation = HLM.GetFunctionAnnotation(Entry); |
| DXASSERT(EntryAnnotation, "must find annotation for entry function"); |
| |
| for (Argument &arg : Entry->getArgumentList()) { |
| if (HLModule::IsStreamOutputPtrType(arg.getType())) { |
| unsigned streamID = 0; |
| DxilParameterAnnotation ¶mAnnotation = |
| EntryAnnotation->GetParameterAnnotation(arg.getArgNo()); |
| DxilParamInputQual inputQual = paramAnnotation.GetParamInputQual(); |
| switch (inputQual) { |
| case DxilParamInputQual::OutStream0: |
| streamID = 0; |
| break; |
| case DxilParamInputQual::OutStream1: |
| streamID = 1; |
| break; |
| case DxilParamInputQual::OutStream2: |
| streamID = 2; |
| break; |
| case DxilParamInputQual::OutStream3: |
| default: |
| DXASSERT(inputQual == DxilParamInputQual::OutStream3, |
| "invalid input qual."); |
| streamID = 3; |
| break; |
| } |
| GenerateStreamOutputOperation(&arg, streamID); |
| } |
| } |
| } |
| // Generate DXIL EmitIndices operation. |
| void HLSignatureLower::GenerateEmitIndicesOperation(Value *indicesOutput) { |
| OP *hlslOP = HLM.GetOP(); |
| Function *DxilFunc = hlslOP->GetOpFunc( |
| OP::OpCode::EmitIndices, Type::getVoidTy(indicesOutput->getContext())); |
| Constant *opArg = hlslOP->GetU32Const((unsigned)OP::OpCode::EmitIndices); |
| |
| for (auto U = indicesOutput->user_begin(); U != indicesOutput->user_end();) { |
| Value *user = *(U++); |
| GetElementPtrInst *GEP = cast<GetElementPtrInst>(user); |
| auto idx = GEP->idx_begin(); |
| DXASSERT_LOCALVAR(idx, idx->get() == hlslOP->GetU32Const(0), |
| "only support 0 offset for input pointer"); |
| gep_type_iterator GEPIt = gep_type_begin(GEP), E = gep_type_end(GEP); |
| |
| // Skip first pointer idx which must be 0. |
| GEPIt++; |
| Value *primIdx = GEPIt.getOperand(); |
| DXASSERT(++GEPIt == E, "invalid GEP here"); |
| (void)E; |
| |
| auto GepUser = GEP->user_begin(); |
| auto GepUserE = GEP->user_end(); |
| for (; GepUser != GepUserE;) { |
| auto GepUserIt = GepUser++; |
| StoreInst *stInst = cast<StoreInst>(*GepUserIt); |
| Value *stVal = stInst->getValueOperand(); |
| VectorType *VT = cast<VectorType>(stVal->getType()); |
| unsigned eleCount = VT->getNumElements(); |
| IRBuilder<> Builder(stInst); |
| Value *subVal0 = |
| Builder.CreateExtractElement(stVal, hlslOP->GetU32Const(0)); |
| Value *subVal1 = |
| Builder.CreateExtractElement(stVal, hlslOP->GetU32Const(1)); |
| Value *subVal2 = |
| eleCount == 3 |
| ? Builder.CreateExtractElement(stVal, hlslOP->GetU32Const(2)) |
| : hlslOP->GetU32Const(0); |
| Value *args[] = {opArg, primIdx, subVal0, subVal1, subVal2}; |
| Builder.CreateCall(DxilFunc, args); |
| stInst->eraseFromParent(); |
| } |
| GEP->eraseFromParent(); |
| } |
| } |
| // Generate DXIL EmitIndices operations. |
| void HLSignatureLower::GenerateEmitIndicesOperations() { |
| DxilFunctionAnnotation *EntryAnnotation = HLM.GetFunctionAnnotation(Entry); |
| DXASSERT(EntryAnnotation, "must find annotation for entry function"); |
| |
| for (Argument &arg : Entry->getArgumentList()) { |
| DxilParameterAnnotation ¶mAnnotation = |
| EntryAnnotation->GetParameterAnnotation(arg.getArgNo()); |
| DxilParamInputQual inputQual = paramAnnotation.GetParamInputQual(); |
| if (inputQual == DxilParamInputQual::OutIndices) { |
| GenerateEmitIndicesOperation(&arg); |
| } |
| } |
| } |
| // Generate DXIL GetMeshPayload operation. |
| void HLSignatureLower::GenerateGetMeshPayloadOperation() { |
| DxilFunctionAnnotation *EntryAnnotation = HLM.GetFunctionAnnotation(Entry); |
| DXASSERT(EntryAnnotation, "must find annotation for entry function"); |
| |
| for (Argument &arg : Entry->getArgumentList()) { |
| DxilParameterAnnotation ¶mAnnotation = |
| EntryAnnotation->GetParameterAnnotation(arg.getArgNo()); |
| DxilParamInputQual inputQual = paramAnnotation.GetParamInputQual(); |
| if (inputQual == DxilParamInputQual::InPayload) { |
| OP *hlslOP = HLM.GetOP(); |
| Function *DxilFunc = |
| hlslOP->GetOpFunc(OP::OpCode::GetMeshPayload, arg.getType()); |
| Constant *opArg = |
| hlslOP->GetU32Const((unsigned)OP::OpCode::GetMeshPayload); |
| IRBuilder<> Builder( |
| arg.getParent()->getEntryBlock().getFirstInsertionPt()); |
| Value *args[] = {opArg}; |
| Value *payload = Builder.CreateCall(DxilFunc, args); |
| arg.replaceAllUsesWith(payload); |
| } |
| } |
| } |
| // Lower signatures. |
| void HLSignatureLower::Run() { |
| DxilFunctionProps &props = HLM.GetDxilFunctionProps(Entry); |
| if (props.IsGraphics()) { |
| if (props.IsMS()) { |
| GenerateEmitIndicesOperations(); |
| GenerateGetMeshPayloadOperation(); |
| } |
| CreateDxilSignatures(); |
| |
| // Allocate input output. |
| AllocateDxilInputOutputs(); |
| |
| GenerateDxilInputs(); |
| GenerateDxilOutputs(); |
| if (props.IsMS()) { |
| GenerateDxilPrimOutputs(); |
| } |
| } else if (props.IsCS() || props.IsNode()) { |
| GenerateDxilComputeAndNodeCommonInputs(); |
| } |
| |
| if (props.IsDS() || props.IsHS()) |
| GenerateDxilPatchConstantLdSt(); |
| if (props.IsHS()) |
| GenerateDxilPatchConstantFunctionInputs(); |
| if (props.IsGS()) |
| GenerateStreamOutputOperations(); |
| } |