From 832d4dedf2eb05b7280bb1243e0ee195b9522bd4 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Tue, 30 May 2023 11:54:09 -0500 Subject: [PATCH 1/3] [Draft] Rewrite output parameters This change is a huge swath of changes rewriting how HLSL represents pointers, references and arrays as well as the handling of output parameters in function signatures. The main changes included in this PR are: * Restoring Array->Pointer Decay in the HLSL ASTs. * Representing all output parameters using reference types in the AST. * Adding a new HLSLOutParamExpr AST node for output parameters. *Adding a new HLSLArrayTemporaryExpr to represent temporary array values. There are also a large number of associated fixes and patches to make the code generation and optimization code resilient to the new AST and IR structures that are generated. --- include/dxc/HLSL/DxilGenerationPass.h | 3 - lib/HLSL/CMakeLists.txt | 1 - lib/HLSL/HLLegalizeParameter.cpp | 326 --------------- lib/Transforms/IPO/PassManagerBuilder.cpp | 3 - lib/Transforms/Scalar/LowerTypePasses.cpp | 14 +- .../Scalar/ScalarReplAggregatesHLSL.cpp | 56 +-- .../clang/AST/DataRecursiveASTVisitor.h | 4 + tools/clang/include/clang/AST/Expr.h | 97 ++++- .../include/clang/AST/RecursiveASTVisitor.h | 2 + tools/clang/include/clang/Basic/StmtNodes.td | 2 + .../clang/include/clang/SPIRV/SpirvBuilder.h | 3 + .../include/clang/SPIRV/SpirvInstruction.h | 25 ++ .../clang/include/clang/SPIRV/SpirvVisitor.h | 1 + tools/clang/lib/AST/ASTDumper.cpp | 20 + tools/clang/lib/AST/Decl.cpp | 4 +- tools/clang/lib/AST/Expr.cpp | 19 + tools/clang/lib/AST/ExprClassification.cpp | 4 + tools/clang/lib/AST/ExprConstant.cpp | 2 + tools/clang/lib/AST/HlslTypes.cpp | 47 ++- tools/clang/lib/AST/StmtPrinter.cpp | 6 + tools/clang/lib/AST/StmtProfile.cpp | 12 +- tools/clang/lib/CodeGen/CGCall.cpp | 96 ++++- tools/clang/lib/CodeGen/CGCall.h | 20 +- tools/clang/lib/CodeGen/CGExpr.cpp | 136 +++---- tools/clang/lib/CodeGen/CGExprAgg.cpp | 8 + tools/clang/lib/CodeGen/CGExprCXX.cpp | 62 +-- tools/clang/lib/CodeGen/CGExprScalar.cpp | 7 +- tools/clang/lib/CodeGen/CGHLSLMS.cpp | 380 +----------------- tools/clang/lib/CodeGen/CGHLSLRuntime.h | 11 +- tools/clang/lib/CodeGen/CodeGenFunction.h | 7 +- tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 6 +- tools/clang/lib/SPIRV/EmitVisitor.cpp | 11 + tools/clang/lib/SPIRV/EmitVisitor.h | 1 + tools/clang/lib/SPIRV/GlPerVertex.cpp | 2 +- tools/clang/lib/SPIRV/SpirvBuilder.cpp | 13 + tools/clang/lib/SPIRV/SpirvEmitter.cpp | 131 +++--- tools/clang/lib/SPIRV/SpirvEmitter.h | 17 + tools/clang/lib/SPIRV/SpirvInstruction.cpp | 6 + tools/clang/lib/Sema/HLSLOutParamBuilder.h | 141 +++++++ tools/clang/lib/Sema/Sema.cpp | 7 - tools/clang/lib/Sema/SemaCast.cpp | 86 +++- tools/clang/lib/Sema/SemaDXR.cpp | 8 +- tools/clang/lib/Sema/SemaDecl.cpp | 28 ++ tools/clang/lib/Sema/SemaExpr.cpp | 59 +-- tools/clang/lib/Sema/SemaExprCXX.cpp | 16 +- tools/clang/lib/Sema/SemaHLSL.cpp | 51 +-- tools/clang/lib/Sema/SemaOverload.cpp | 17 +- tools/clang/lib/Sema/TreeTransform.h | 27 ++ tools/clang/test/HLSL/inout_trunc.hlsl | 58 +++ .../binary => HLSL}/vector-matrix-binops.hlsl | 24 +- .../hlsl/classes/class_method_overload.hlsl | 5 +- .../hlsl/classes/derived-to-base-casting.hlsl | 82 ++++ .../hlsl/classes/this_cast_to_base_class.hlsl | 111 ++--- .../unroll/lib_2d_array_call_export.hlsl | 74 +++- .../lib_2d_array_call_export_subarray.hlsl | 56 ++- .../diagnostics/errors/local_resource2.hlsl | 4 +- .../diagnostics/errors/local_resource3.hlsl | 4 +- .../errors/local_resource6_dbg.hlsl | 4 +- .../arguments/copyin-copyout-operators.hlsl | 2 +- .../arguments/copyin-copyout-struct.hlsl | 23 +- .../functions/arguments/copyin-copyout.hlsl | 50 ++- .../functions/arguments/gep_arg_nocopy.hlsl | 15 +- .../arguments/global_constant_const.hlsl | 11 +- .../hlsl/functions/arguments/inout_trunc.hlsl | 72 ---- .../functions/arguments/parameter_types.hlsl | 2 +- .../hlsl/intrinsics/atomic/atomic_cast1.hlsl | 4 +- .../Scenarios/unbounded-resource-arrays.hlsl | 64 +-- .../hlsl/resource_binding/res_in_cb2.hlsl | 6 +- .../HLSLFileCheck/hlsl/types/cast/cast7.hlsl | 22 - .../vector/VectorIndexingAsArgument.hlsl | 8 +- .../hl/hl_legalize_param/out_only_param.ll | 119 ------ .../pix/AnnotateVirtualRegs-Raygen.hlsl | 16 +- .../inout_struct_mismatch-strictudt.hlsl | 10 +- .../library/inout_struct_mismatch.hlsl | 10 +- .../shader_targets/library/lib_no_alias.hlsl | 6 +- .../library/lib_skip_copy_in.hlsl | 34 -- .../raytracing/raytracing_anyhit.hlsl | 2 +- .../raytracing_anyhit_geometryIndex.hlsl | 2 +- .../raytracing/raytracing_callable.hlsl | 10 +- .../raytracing/raytracing_closesthit.hlsl | 2 +- .../raytracing_closesthit_geometryIndex.hlsl | 2 +- ...raytracing_intersection_geometryIndex.hlsl | 6 +- .../raytracing/raytracing_miss.hlsl | 2 +- tools/clang/unittests/HLSL/VerifierTest.cpp | 10 + utils/hct/gen_intrin_main.txt | 2 +- utils/hct/hctdb.py | 1 - 86 files changed, 1380 insertions(+), 1560 deletions(-) delete mode 100644 lib/HLSL/HLLegalizeParameter.cpp create mode 100644 tools/clang/lib/Sema/HLSLOutParamBuilder.h create mode 100644 tools/clang/test/HLSL/inout_trunc.hlsl rename tools/clang/test/{HLSLFileCheck/hlsl/operators/binary => HLSL}/vector-matrix-binops.hlsl (54%) create mode 100644 tools/clang/test/HLSLFileCheck/hlsl/classes/derived-to-base-casting.hlsl delete mode 100644 tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/inout_trunc.hlsl delete mode 100644 tools/clang/test/HLSLFileCheck/hlsl/types/cast/cast7.hlsl delete mode 100644 tools/clang/test/HLSLFileCheck/passes/hl/hl_legalize_param/out_only_param.ll delete mode 100644 tools/clang/test/HLSLFileCheck/shader_targets/library/lib_skip_copy_in.hlsl diff --git a/include/dxc/HLSL/DxilGenerationPass.h b/include/dxc/HLSL/DxilGenerationPass.h index 418d098fab..982296cea9 100644 --- a/include/dxc/HLSL/DxilGenerationPass.h +++ b/include/dxc/HLSL/DxilGenerationPass.h @@ -120,9 +120,6 @@ void initializeCleanupDxBreakPass(llvm::PassRegistry&); FunctionPass *createDxilLoopDeletionPass(bool NoSink); void initializeDxilLoopDeletionPass(llvm::PassRegistry &); -ModulePass *createHLLegalizeParameter(); -void initializeHLLegalizeParameterPass(llvm::PassRegistry &); - bool AreDxilResourcesDense(llvm::Module *M, hlsl::DxilResourceBase **ppNonDense); ModulePass *createDxilNoOptLegalizePass(); diff --git a/lib/HLSL/CMakeLists.txt b/lib/HLSL/CMakeLists.txt index a86069202b..27e1f10537 100644 --- a/lib/HLSL/CMakeLists.txt +++ b/lib/HLSL/CMakeLists.txt @@ -40,7 +40,6 @@ add_llvm_library(LLVMHLSL DxcOptimizer.cpp HLDeadFunctionElimination.cpp HLExpandStoreIntrinsics.cpp - HLLegalizeParameter.cpp HLLowerUDT.cpp HLMatrixBitcastLowerPass.cpp HLMatrixLowerPass.cpp diff --git a/lib/HLSL/HLLegalizeParameter.cpp b/lib/HLSL/HLLegalizeParameter.cpp deleted file mode 100644 index 7056d4b790..0000000000 --- a/lib/HLSL/HLLegalizeParameter.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// // -// HLLegalizeParameter.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. // -// // -// Legalize in parameter has write and out parameter has read. // -// Must be call before inline pass. // -/////////////////////////////////////////////////////////////////////////////// - -#include "dxc/HLSL/HLModule.h" -#include "dxc/DXIL/DxilOperations.h" -#include "dxc/DXIL/DxilUtil.h" -#include "dxc/HLSL/DxilGenerationPass.h" -#include "dxc/HLSL/HLUtil.h" -#include "dxc/DXIL/DxilTypeSystem.h" - -#include "llvm/IR/IntrinsicInst.h" - -#include "dxc/Support/Global.h" -#include "llvm/Pass.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Casting.h" - -#include - -using namespace llvm; -using namespace hlsl; - -// For parameter need to legalize, create alloca to replace all uses of it, and copy between the alloca and the parameter. - -namespace { - -class HLLegalizeParameter : public ModulePass { -public: - static char ID; - explicit HLLegalizeParameter() : ModulePass(ID) {} - bool runOnModule(Module &M) override; - -private: - void patchWriteOnInParam(Function &F, Argument &Arg, const DataLayout &DL); - void patchReadOnOutParam(Function &F, Argument &Arg, const DataLayout &DL); -}; - -AllocaInst *createAllocaForPatch(Function &F, Type *Ty) { - IRBuilder<> Builder(F.getEntryBlock().getFirstInsertionPt()); - return Builder.CreateAlloca(Ty); -} - -void copyIn(AllocaInst *temp, Value *arg, CallInst *CI, unsigned size) { - if (size == 0) - return; - // Copy arg to temp before CI. - IRBuilder<> Builder(CI); - Builder.CreateMemCpy(temp, arg, size, 1); -} - -void copyOut(AllocaInst *temp, Value *arg, CallInst *CI, unsigned size) { - if (size == 0) - return; - // Copy temp to arg after CI. - IRBuilder<> Builder(CI->getNextNode()); - Builder.CreateMemCpy(arg, temp, size, 1); -} - -bool isPointerNeedToLower(Value *V, Type *HandleTy) { - // CBuffer, Buffer, Texture.... - // Anything related to dxil op. - // hl.subscript. - // Got to root of GEP. - while (GEPOperator *GEP = dyn_cast(V)) { - V = GEP->getPointerOperand(); - } - CallInst *CI = dyn_cast(V); - if (!CI) { - // If array of vector, we need a copy to handle vector to array in LowerTypePasses. - Type *Ty = V->getType(); - if (Ty->isPointerTy()) - Ty = Ty->getPointerElementType(); - if (!Ty->isArrayTy()) - return false; - while (Ty->isArrayTy()) { - Ty = Ty->getArrayElementType(); - } - return Ty->isVectorTy(); - } - HLOpcodeGroup group = GetHLOpcodeGroup(CI->getCalledFunction()); - if (group != HLOpcodeGroup::HLSubscript) - return false; - Value *Ptr = CI->getArgOperand(HLOperandIndex::kSubscriptObjectOpIdx); - - // Ptr from resource handle. - if (Ptr->getType() == HandleTy) - return true; - unsigned Opcode = GetHLOpcode(CI); - // Ptr from cbuffer. - if (Opcode == (unsigned)HLSubscriptOpcode::CBufferSubscript) - return true; - - return isPointerNeedToLower(Ptr, HandleTy); -} - -bool mayAliasWithGlobal(Value *V, CallInst *CallSite, std::vector &staticGVs) { - // The unsafe case need copy-in copy-out will be global variable alias with - // parameter. Then global variable is updated in the function, the parameter - // will be updated silently. - - // Currently add copy for all non-const static global in - // CGMSHLSLRuntime::EmitHLSLOutParamConversionInit. - //So here just return false and do nothing. - // For case like - // struct T { - // float4 a[10]; - //}; - // static T g; - // void foo(inout T t) { - // // modify g - //} - // void bar() { - // T t = g; - // // Not copy because t is local. - // // But optimizations will change t to g later. - // foo(t); - //} - // Optimizations which remove the copy should not replace foo(t) into foo(g) - // when g could be modified. - // TODO: remove copy for global in - // CGMSHLSLRuntime::EmitHLSLOutParamConversionInit, do analysis to check alias - // only generate copy when there's alias. - return false; -} - -struct CopyData { - CallInst *CallSite; - Value *Arg; - bool bCopyIn; - bool bCopyOut; -}; - -void ParameterCopyInCopyOut(hlsl::HLModule &HLM) { - Module &M = *HLM.GetModule(); - Type *HandleTy = HLM.GetOP()->GetHandleType(); - const DataLayout &DL = M.getDataLayout(); - - std::vector staticGVs; - for (GlobalVariable &GV : M.globals()) { - if (dxilutil::IsStaticGlobal(&GV) && !GV.isConstant()) { - staticGVs.emplace_back(&GV); - } - } - - SmallVector WorkList; - for (Function &F : M) { - if (F.user_empty()) - continue; - DxilFunctionAnnotation *Annot = HLM.GetFunctionAnnotation(&F); - // Skip functions don't have annotation, include llvm intrinsic and HLOp - // functions. - if (!Annot) - continue; - - bool bNoInline = F.hasFnAttribute(llvm::Attribute::NoInline) || F.isDeclaration(); - - for (User *U : F.users()) { - CallInst *CI = dyn_cast(U); - if (!CI) - continue; - for (unsigned i = 0; i < CI->getNumArgOperands(); i++) { - Value *arg = CI->getArgOperand(i); - - if (!arg->getType()->isPointerTy()) - continue; - - DxilParameterAnnotation &ParamAnnot = Annot->GetParameterAnnotation(i); - bool bCopyIn = false; - bool bCopyOut = false; - switch (ParamAnnot.GetParamInputQual()) { - default: - break; - case DxilParamInputQual::In: { - bCopyIn = true; - } break; - case DxilParamInputQual::Out: { - bCopyOut = true; - } break; - case DxilParamInputQual::Inout: { - bCopyIn = true; - bCopyOut = true; - } break; - } - - if (!bCopyIn && !bCopyOut) - continue; - - // When use ptr from cbuffer/buffer, need copy to avoid lower on user - // function. - bool bNeedCopy = mayAliasWithGlobal(arg, CI, staticGVs); - if (bNoInline) - bNeedCopy |= isPointerNeedToLower(arg, HandleTy); - - if (!bNeedCopy) - continue; - - CopyData data = {CI, arg, bCopyIn, bCopyOut}; - WorkList.emplace_back(data); - } - } - } - - for (CopyData &data : WorkList) { - CallInst *CI = data.CallSite; - Value *arg = data.Arg; - Type *Ty = arg->getType()->getPointerElementType(); - Type *EltTy = dxilutil::GetArrayEltTy(Ty); - // Skip on object type and resource type. - if (dxilutil::IsHLSLObjectType(EltTy) || - dxilutil::IsHLSLResourceType(EltTy)) - continue; - unsigned size = DL.getTypeAllocSize(Ty); - AllocaInst *temp = createAllocaForPatch(*CI->getParent()->getParent(), Ty); - // TODO: Adding lifetime intrinsics isn't easy here, have to analyze uses. - if (data.bCopyIn) - copyIn(temp, arg, CI, size); - if (data.bCopyOut) - copyOut(temp, arg, CI, size); - CI->replaceUsesOfWith(arg, temp); - } -} - -} // namespace - -bool HLLegalizeParameter::runOnModule(Module &M) { - HLModule &HLM = M.GetOrCreateHLModule(); - - auto &typeSys = HLM.GetTypeSystem(); - const DataLayout &DL = M.getDataLayout(); - - for (Function &F : M) { - if (F.isDeclaration()) - continue; - DxilFunctionAnnotation *Annot = HLM.GetFunctionAnnotation(&F); - if (!Annot) - continue; - - for (Argument &Arg : F.args()) { - if (!Arg.getType()->isPointerTy()) - continue; - Type *EltTy = dxilutil::GetArrayEltTy(Arg.getType()); - if (dxilutil::IsHLSLObjectType(EltTy) || - dxilutil::IsHLSLResourceType(EltTy)) - continue; - - DxilParameterAnnotation &ParamAnnot = - Annot->GetParameterAnnotation(Arg.getArgNo()); - switch (ParamAnnot.GetParamInputQual()) { - default: - break; - case DxilParamInputQual::In: { - hlutil::PointerStatus PS(&Arg, 0, /*bLdStOnly*/ true); - PS.analyze(typeSys, /*bStructElt*/ false); - if (PS.HasStored()) { - patchWriteOnInParam(F, Arg, DL); - } - } break; - case DxilParamInputQual::Out: { - hlutil::PointerStatus PS(&Arg, 0, /*bLdStOnly*/ true); - PS.analyze(typeSys, /*bStructElt*/false); - if (PS.HasLoaded()) { - patchReadOnOutParam(F, Arg, DL); - } - } - } - } - } - - // Copy-in copy-out for ptr arg when need. - ParameterCopyInCopyOut(HLM); - - return true; -} - -void HLLegalizeParameter::patchWriteOnInParam(Function &F, Argument &Arg, - const DataLayout &DL) { - // TODO: Adding lifetime intrinsics isn't easy here, have to analyze uses. - Type *Ty = Arg.getType()->getPointerElementType(); - AllocaInst *temp = createAllocaForPatch(F, Ty); - Arg.replaceAllUsesWith(temp); - IRBuilder<> Builder(temp->getNextNode()); - unsigned size = DL.getTypeAllocSize(Ty); - // copy arg to temp at beginning of function. - Builder.CreateMemCpy(temp, &Arg, size, 1); -} - -void HLLegalizeParameter::patchReadOnOutParam(Function &F, Argument &Arg, - const DataLayout &DL) { - // TODO: Adding lifetime intrinsics isn't easy here, have to analyze uses. - Type *Ty = Arg.getType()->getPointerElementType(); - AllocaInst *temp = createAllocaForPatch(F, Ty); - Arg.replaceAllUsesWith(temp); - - unsigned size = DL.getTypeAllocSize(Ty); - for (auto &BB : F.getBasicBlockList()) { - // copy temp to arg before every return. - if (ReturnInst *RI = dyn_cast(BB.getTerminator())) { - IRBuilder<> RetBuilder(RI); - RetBuilder.CreateMemCpy(&Arg, temp, size, 1); - } - } -} - -char HLLegalizeParameter::ID = 0; -ModulePass *llvm::createHLLegalizeParameter() { - return new HLLegalizeParameter(); -} - -INITIALIZE_PASS(HLLegalizeParameter, "hl-legalize-parameter", - "Legalize parameter", false, false) diff --git a/lib/Transforms/IPO/PassManagerBuilder.cpp b/lib/Transforms/IPO/PassManagerBuilder.cpp index 88e109e51c..dc45084eb8 100644 --- a/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -343,8 +343,6 @@ void PassManagerBuilder::populateModulePassManager( if (HLSLEnableDebugNops) MPM.add(createDxilInsertPreservesPass(HLSLAllowPreserveValues)); // HLSL Change - insert preserve instructions if (Inliner) { - MPM.add(createHLLegalizeParameter()); // HLSL Change - legalize parameters - // before inline. MPM.add(Inliner); Inliner = nullptr; } @@ -404,7 +402,6 @@ void PassManagerBuilder::populateModulePassManager( MPM.add(createDxilRewriteOutputArgDebugInfoPass()); // Fix output argument types. - MPM.add(createHLLegalizeParameter()); // legalize parameters before inline. if (HLSLEarlyInlining && Inliner) { MPM.add(Inliner); Inliner = nullptr; diff --git a/lib/Transforms/Scalar/LowerTypePasses.cpp b/lib/Transforms/Scalar/LowerTypePasses.cpp index d4d1782617..d44689755c 100644 --- a/lib/Transforms/Scalar/LowerTypePasses.cpp +++ b/lib/Transforms/Scalar/LowerTypePasses.cpp @@ -271,7 +271,7 @@ void DynamicIndexingVectorToArray::ReplaceStaticIndexingOnVector(Value *V) { ldVal = Builder.CreateLoad(V); ldVal = Builder.CreateInsertElement(ldVal, Elt, constIdx); Builder.CreateStore(ldVal, V); - } else { + } else if (StoreInst *stInst = dyn_cast(GEPUser)) { // Change // st val, a->x // into @@ -279,7 +279,6 @@ void DynamicIndexingVectorToArray::ReplaceStaticIndexingOnVector(Value *V) { // tmp.x = val // st tmp, a // Must be store inst here. - StoreInst *stInst = cast(GEPUser); Value *val = stInst->getValueOperand(); Value *ldVal = Builder.CreateLoad(V); ldVal = Builder.CreateInsertElement(ldVal, val, constIdx); @@ -287,7 +286,8 @@ void DynamicIndexingVectorToArray::ReplaceStaticIndexingOnVector(Value *V) { stInst->eraseFromParent(); } } - GEP->eraseFromParent(); + if (GEP->user_empty()) + GEP->eraseFromParent(); } else if (GEP->getNumIndices() == 1) { Value *Idx = *GEP->idx_begin(); if (ConstantInt *C = dyn_cast(Idx)) { @@ -304,9 +304,13 @@ void DynamicIndexingVectorToArray::ReplaceStaticIndexingOnVector(Value *V) { bool DynamicIndexingVectorToArray::needToLower(Value *V) { Type *Ty = V->getType()->getPointerElementType(); if (dyn_cast(Ty)) { - if (isa(V) || ReplaceAllVectors) { + if (ReplaceAllVectors) return true; - } + + auto *GV = dyn_cast(V); + if (GV && (!dxilutil::IsStaticGlobal(GV) || GV->isConstant())) + return true; + // Don't lower local vector which only static indexing. if (HasVectorDynamicIndexing(V)) { return true; diff --git a/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp b/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp index 5d4c65d104..fb885b3460 100644 --- a/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp +++ b/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp @@ -143,6 +143,8 @@ static unsigned getNestedLevelInStruct(const Type *ty) { return lvl; } +static void removeLifetimeUsers(Value *V); + // After SROA'ing a given value into a series of elements, // creates the debug info for the storage of the individual elements. static void addDebugInfoForElements(Value *ParentVal, @@ -1873,6 +1875,7 @@ bool SROAGlobalAndAllocas(HLModule &HLM, bool bHasDbgInfo) { } addDebugInfoForElements(AI, BrokenUpTy, NumInstances, Elts, DL, &DIB); + removeLifetimeUsers(AI); // Push Elts into workList. for (unsigned EltIdx = 0; EltIdx < Elts.size(); ++EltIdx) { @@ -1930,31 +1933,35 @@ bool SROAGlobalAndAllocas(HLModule &HLM, bool bHasDbgInfo) { std::vector Elts; bool SROAed = false; - if (GlobalVariable *NewEltGV = dyn_cast_or_null( - TranslatePtrIfUsedByLoweredFn(GV, typeSys))) { - GVDbgOffset dbgOffset = GVDbgOffsetMap[GV]; - // Don't need to update when skip SROA on base GV. - if (NewEltGV == dbgOffset.base) - continue; + // In Library shaders only promote global constants. + if (!HLM.GetShaderModel()->IsLib() || + (!dxilutil::IsStaticGlobal(GV) || GV->isConstant())) { + if (GlobalVariable *NewEltGV = dyn_cast_or_null( + TranslatePtrIfUsedByLoweredFn(GV, typeSys))) { + GVDbgOffset dbgOffset = GVDbgOffsetMap[GV]; + // Don't need to update when skip SROA on base GV. + if (NewEltGV == dbgOffset.base) + continue; - if (GV != NewEltGV) { - GVDbgOffsetMap[NewEltGV] = dbgOffset; - // Remove GV from GVDbgOffsetMap. - GVDbgOffsetMap.erase(GV); - if (GV != dbgOffset.base) { - // Remove GV when it is replaced by NewEltGV and is not a base GV. - GV->removeDeadConstantUsers(); - GV->eraseFromParent(); + if (GV != NewEltGV) { + GVDbgOffsetMap[NewEltGV] = dbgOffset; + // Remove GV from GVDbgOffsetMap. + GVDbgOffsetMap.erase(GV); + if (GV != dbgOffset.base) { + // Remove GV when it is replaced by NewEltGV and is not a base GV. + GV->removeDeadConstantUsers(); + GV->eraseFromParent(); + } + GV = NewEltGV; } - GV = NewEltGV; + } else { + // SROA_Parameter_HLSL has no access to a domtree, if one is needed, + // it'll be generated + SROAed = SROA_Helper::DoScalarReplacement( + GV, Elts, Builder, bFlatVector, + // TODO: set precise. + /*hasPrecise*/ false, typeSys, DL, DeadInsts, /*DT*/ nullptr); } - } else { - // SROA_Parameter_HLSL has no access to a domtree, if one is needed, - // it'll be generated - SROAed = SROA_Helper::DoScalarReplacement( - GV, Elts, Builder, bFlatVector, - // TODO: set precise. - /*hasPrecise*/ false, typeSys, DL, DeadInsts, /*DT*/ nullptr); } if (SROAed) { @@ -2321,9 +2328,8 @@ void SROA_Helper::RewriteForStore(StoreInst *SI) { } } } - } else { - llvm_unreachable("other type don't need rewrite"); - } + } // Note: other data types we can just eliminate the store, so we don't need + // to rewrite anything here. // Remove the use so that the caller can keep iterating over its other users SI->setOperand(SI->getPointerOperandIndex(), UndefValue::get(SI->getPointerOperand()->getType())); diff --git a/tools/clang/include/clang/AST/DataRecursiveASTVisitor.h b/tools/clang/include/clang/AST/DataRecursiveASTVisitor.h index 50955d8ec3..6e037cbe7a 100644 --- a/tools/clang/include/clang/AST/DataRecursiveASTVisitor.h +++ b/tools/clang/include/clang/AST/DataRecursiveASTVisitor.h @@ -2146,6 +2146,10 @@ DEF_TRAVERSE_STMT(ExpressionTraitExpr, DEF_TRAVERSE_STMT(ExtMatrixElementExpr, {}) DEF_TRAVERSE_STMT(HLSLVectorElementExpr, {}) + +DEF_TRAVERSE_STMT(HLSLOutParamExpr, {}) + +DEF_TRAVERSE_STMT(HLSLArrayTemporaryExpr, {}) // HLSL Change end. DEF_TRAVERSE_STMT(VAArgExpr, { diff --git a/tools/clang/include/clang/AST/Expr.h b/tools/clang/include/clang/AST/Expr.h index 26eff309f7..ccc5f68e8f 100644 --- a/tools/clang/include/clang/AST/Expr.h +++ b/tools/clang/include/clang/AST/Expr.h @@ -824,6 +824,7 @@ class OpaqueValueExpr : public Expr { friend class ASTStmtReader; Expr *SourceExpr; SourceLocation Loc; + bool SourceIsParent; // HLSL Change public: OpaqueValueExpr(SourceLocation Loc, QualType T, ExprValueKind VK, @@ -835,8 +836,7 @@ class OpaqueValueExpr : public Expr { (SourceExpr && SourceExpr->isValueDependent()), T->isInstantiationDependentType(), false), - SourceExpr(SourceExpr), Loc(Loc) { - } + SourceExpr(SourceExpr), Loc(Loc), SourceIsParent(false) {} // HLSL Change /// Given an expression which invokes a copy constructor --- i.e. a /// CXXConstructExpr, possibly wrapped in an ExprWithCleanups --- @@ -875,6 +875,11 @@ class OpaqueValueExpr : public Expr { static bool classof(const Stmt *T) { return T->getStmtClass() == OpaqueValueExprClass; } + + // HLSL Change start + bool sourceIsParent() const { return SourceIsParent; } + void setSourceIsParent(bool B = true) { SourceIsParent = B; } + // HLSL Change end }; /// \brief A reference to a declared variable, function, enum, etc. @@ -4752,6 +4757,94 @@ class HLSLVectorElementExpr : public Expr { // Iterators child_range children() { return child_range(&Base, &Base + 1); } }; + +class HLSLOutParamExpr : public Expr { + Expr *Base; + Expr *Writeback; + Expr *SrcLV; + OpaqueValueExpr *OpaqueVal; + bool IsInOut; + bool CanElide; + + HLSLOutParamExpr(QualType Ty, Expr *Base, bool IsInOut, bool CanElide) + : Expr(HLSLOutParamExprClass, Ty, VK_LValue, OK_Ordinary, + Base->isTypeDependent(), Base->isValueDependent(), + Base->isInstantiationDependent(), + Base->containsUnexpandedParameterPack()), + Base(Base), Writeback(nullptr), SrcLV(nullptr), OpaqueVal(nullptr), + IsInOut(IsInOut), CanElide(CanElide) {} + +public: + static HLSLOutParamExpr *Create(const ASTContext &C, QualType Ty, Expr *Base, + bool IsInOut = false, bool CanElide = false); + + const Expr *getBase() const { return Base; } + Expr *getBase() { return Base; } + void setBase(Expr *E) { Base = E; } + + const Expr *getWriteback() const { return Writeback; } + Expr *getWriteback() { return Writeback; } + void setWriteback(Expr *E) { Writeback = E; } + + const Expr *getSrcLV() const { return SrcLV ? SrcLV : Base; } + Expr *getSrcLV() { return SrcLV ? SrcLV : Base; } + void setSrcLV(Expr *E) { SrcLV = E; } + + const OpaqueValueExpr *getOpaqueValue() const { return OpaqueVal; } + OpaqueValueExpr *getOpaqueValue() { return OpaqueVal; } + void setOpaqueValue(OpaqueValueExpr *E) { OpaqueVal = E; } + + bool isInOut() const { return IsInOut; } + bool canElide() const { return CanElide; } + + SourceLocation getLocStart() const LLVM_READONLY { + return Base->getLocStart(); + } + + SourceLocation getLocEnd() const LLVM_READONLY { return Base->getLocEnd(); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == HLSLOutParamExprClass; + } + + // Iterators + child_range children() { + return child_range((Stmt **)&Base, (Stmt **)&Base + 1); + } +}; + +class HLSLArrayTemporaryExpr : public Expr { + Expr *Base; + + HLSLArrayTemporaryExpr(Expr *Base) + : Expr(HLSLArrayTemporaryExprClass, Base->getType(), VK_RValue, + OK_Ordinary, Base->isTypeDependent(), Base->isValueDependent(), + Base->isInstantiationDependent(), + Base->containsUnexpandedParameterPack()), + Base(Base) {} + +public: + static HLSLArrayTemporaryExpr *Create(const ASTContext &C, Expr *Base); + + const Expr *getBase() const { return Base; } + Expr *getBase() { return Base; } + void setBase(Expr *E) { Base = E; } + + SourceLocation getLocStart() const LLVM_READONLY { + return Base->getLocStart(); + } + + SourceLocation getLocEnd() const LLVM_READONLY { return Base->getLocEnd(); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == HLSLArrayTemporaryExprClass; + } + + // Iterators + child_range children() { + return child_range((Stmt **)&Base, (Stmt **)&Base + 1); + } +}; // HLSL Change Ends /// BlockExpr - Adaptor class for mixing a BlockDecl with expressions. diff --git a/tools/clang/include/clang/AST/RecursiveASTVisitor.h b/tools/clang/include/clang/AST/RecursiveASTVisitor.h index c8c79664c8..1689e9e4ec 100644 --- a/tools/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/tools/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2269,6 +2269,8 @@ DEF_TRAVERSE_STMT(DesignatedInitUpdateExpr, {}) DEF_TRAVERSE_STMT(ExtVectorElementExpr, {}) DEF_TRAVERSE_STMT(ExtMatrixElementExpr, {}) // HLSL Change DEF_TRAVERSE_STMT(HLSLVectorElementExpr, {}) // HLSL Change +DEF_TRAVERSE_STMT(HLSLOutParamExpr, {}) // HLSL Change +DEF_TRAVERSE_STMT(HLSLArrayTemporaryExpr, {}) // HLSL Change DEF_TRAVERSE_STMT(GNUNullExpr, {}) DEF_TRAVERSE_STMT(ImplicitValueInitExpr, {}) DEF_TRAVERSE_STMT(NoInitExpr, {}) diff --git a/tools/clang/include/clang/Basic/StmtNodes.td b/tools/clang/include/clang/Basic/StmtNodes.td index 71f3829c97..8860002729 100644 --- a/tools/clang/include/clang/Basic/StmtNodes.td +++ b/tools/clang/include/clang/Basic/StmtNodes.td @@ -89,6 +89,8 @@ def PseudoObjectExpr : DStmt; def ExtMatrixElementExpr : DStmt; def HLSLVectorElementExpr : DStmt; def DiscardStmt : Stmt; +def HLSLOutParamExpr : DStmt; +def HLSLArrayTemporaryExpr : DStmt; // HLSL Change Ends // Atomic expressions diff --git a/tools/clang/include/clang/SPIRV/SpirvBuilder.h b/tools/clang/include/clang/SPIRV/SpirvBuilder.h index c2f20ce4a4..b70e54204f 100644 --- a/tools/clang/include/clang/SPIRV/SpirvBuilder.h +++ b/tools/clang/include/clang/SPIRV/SpirvBuilder.h @@ -186,6 +186,9 @@ class SpirvBuilder { SpirvCopyObject *createCopyObject(QualType resultType, SpirvInstruction *pointer, SourceLocation); + SpirvCopyMemory *createCopyMemory(QualType resultType, SpirvInstruction *src, + SpirvInstruction *dst, SourceLocation loc); + /// \brief Creates a store sequence storing the given value into the given /// address. Returns the instruction pointer for the store instruction. /// This function handles storing to bitfields. diff --git a/tools/clang/include/clang/SPIRV/SpirvInstruction.h b/tools/clang/include/clang/SPIRV/SpirvInstruction.h index ce2ff2814c..0b6f50a093 100644 --- a/tools/clang/include/clang/SPIRV/SpirvInstruction.h +++ b/tools/clang/include/clang/SPIRV/SpirvInstruction.h @@ -101,6 +101,7 @@ class SpirvInstruction { IK_CompositeExtract, // OpCompositeExtract IK_CompositeInsert, // OpCompositeInsert IK_CopyObject, // OpCopyObject + IK_CopyMemory, // OpCopyObject IK_DemoteToHelperInvocation, // OpDemoteToHelperInvocation IK_IsHelperInvocationEXT, // OpIsHelperInvocationEXT IK_ExtInst, // OpExtInst @@ -1726,6 +1727,30 @@ class SpirvCopyObject : public SpirvInstruction { SpirvInstruction *pointer; }; +/// \brief OpCopyMemory instruction +class SpirvCopyMemory : public SpirvInstruction { +public: + SpirvCopyMemory(QualType resultType, SourceLocation loc, + SpirvInstruction *src, SpirvInstruction *dst); + + DEFINE_RELEASE_MEMORY_FOR_CLASS(SpirvCopyMemory) + + // For LLVM-style RTTI + static bool classof(const SpirvInstruction *inst) { + return inst->getKind() == IK_CopyMemory; + } + + bool invokeVisitor(Visitor *v) override; + + SpirvInstruction *getSource() const { return source; } + + SpirvInstruction *getDestination() const { return destination; } + +private: + SpirvInstruction *source; + SpirvInstruction *destination; +}; + /// \brief OpSampledImage instruction /// Result Type must be the OpTypeSampledImage type whose Image Type operand is /// the type of Image. We store the QualType for the underlying image as result diff --git a/tools/clang/include/clang/SPIRV/SpirvVisitor.h b/tools/clang/include/clang/SPIRV/SpirvVisitor.h index 59b2af13d9..fe4896b64f 100644 --- a/tools/clang/include/clang/SPIRV/SpirvVisitor.h +++ b/tools/clang/include/clang/SPIRV/SpirvVisitor.h @@ -105,6 +105,7 @@ class Visitor { DEFINE_VISIT_METHOD(SpirvImageTexelPointer) DEFINE_VISIT_METHOD(SpirvLoad) DEFINE_VISIT_METHOD(SpirvCopyObject) + DEFINE_VISIT_METHOD(SpirvCopyMemory) DEFINE_VISIT_METHOD(SpirvSampledImage) DEFINE_VISIT_METHOD(SpirvSelect) DEFINE_VISIT_METHOD(SpirvSpecConstantBinaryOp) diff --git a/tools/clang/lib/AST/ASTDumper.cpp b/tools/clang/lib/AST/ASTDumper.cpp index a29b7dea6b..3edc1ccd2f 100644 --- a/tools/clang/lib/AST/ASTDumper.cpp +++ b/tools/clang/lib/AST/ASTDumper.cpp @@ -509,6 +509,8 @@ namespace { void VisitExtVectorElementExpr(const ExtVectorElementExpr *Node); void VisitExtMatrixElementExpr(const ExtMatrixElementExpr *Node); // HLSL Change void VisitHLSLVectorElementExpr(const HLSLVectorElementExpr *Node); // HLSL Change + void VisitHLSLOutParamExpr(const HLSLOutParamExpr *Node); // HLSL Change + void VisitHLSLArrayTemporaryExpr(const HLSLArrayTemporaryExpr *Node); void VisitBinaryOperator(const BinaryOperator *Node); void VisitCompoundAssignOperator(const CompoundAssignOperator *Node); void VisitAddrLabelExpr(const AddrLabelExpr *Node); @@ -2027,6 +2029,19 @@ void ASTDumper::VisitHLSLVectorElementExpr(const HLSLVectorElementExpr *Node) { VisitExpr(Node); OS << " " << Node->getAccessor().getNameStart(); } + +void ASTDumper::VisitHLSLOutParamExpr(const HLSLOutParamExpr *Node) { + VisitExpr(Node); + OS << (Node->isInOut() ? " inout" : " out"); + if (Node->canElide()) + OS << " can elide"; + if (const auto *WB = Node->getWriteback()) + dumpStmt(WB); +} + +void ASTDumper::VisitHLSLArrayTemporaryExpr(const HLSLArrayTemporaryExpr *Node) { + VisitExpr(Node); +} // HLSL Change Ends void ASTDumper::VisitBinaryOperator(const BinaryOperator *Node) { @@ -2052,6 +2067,11 @@ void ASTDumper::VisitBlockExpr(const BlockExpr *Node) { void ASTDumper::VisitOpaqueValueExpr(const OpaqueValueExpr *Node) { VisitExpr(Node); + // HLSL Change start + if (Node->sourceIsParent()) + return; + // HLSL Change end + if (Expr *Source = Node->getSourceExpr()) dumpStmt(Source); } diff --git a/tools/clang/lib/AST/Decl.cpp b/tools/clang/lib/AST/Decl.cpp index 39b3ac768d..c8cdc265ff 100644 --- a/tools/clang/lib/AST/Decl.cpp +++ b/tools/clang/lib/AST/Decl.cpp @@ -2372,9 +2372,7 @@ unsigned ParmVarDecl::getParameterIndexLarge() const { void ParmVarDecl::updateOutParamToRefType(ASTContext &C) { // Aggregate type will be indirect param convert to pointer type. // So don't update to ReferenceType. - if ((!getType()->isArrayType() && !getType()->isRecordType()) || - hlsl::IsHLSLVecMatType(getType())) - setType(C.getLValueReferenceType(getType(), false)); + setType(C.getLValueReferenceType(getType(), false)); // Add restrict to out param. QualType QT = getType(); QT.addRestrict(); diff --git a/tools/clang/lib/AST/Expr.cpp b/tools/clang/lib/AST/Expr.cpp index 0e2ec8c6c2..c04ee3e00b 100644 --- a/tools/clang/lib/AST/Expr.cpp +++ b/tools/clang/lib/AST/Expr.cpp @@ -3079,6 +3079,11 @@ bool Expr::HasSideEffects(const ASTContext &Ctx, return true; break; + // HLSL Change - Begin + case HLSLOutParamExprClass: + return true; + // HLSL Change - End + case InitListExprClass: // FIXME: The children for an InitListExpr doesn't include the array filler. if (const Expr *E = cast(this)->getArrayFiller()) @@ -4357,6 +4362,20 @@ ObjCSubscriptRefExpr *ObjCSubscriptRefExpr::Create(const ASTContext &C, getMethod, setMethod, RB); } +// HLSL Change begin + +HLSLOutParamExpr *HLSLOutParamExpr::Create(const ASTContext &C, QualType Ty, + Expr *Base, bool IsInOut, + bool CanElide) { + return new (C) HLSLOutParamExpr(Ty, Base, IsInOut, CanElide); +} + +HLSLArrayTemporaryExpr * +HLSLArrayTemporaryExpr::Create(const ASTContext &C, Expr *Base) { + return new (C) HLSLArrayTemporaryExpr(Base); +} +// HLSL Change end + AtomicExpr::AtomicExpr(SourceLocation BLoc, ArrayRef args, QualType t, AtomicOp op, SourceLocation RP) : Expr(AtomicExprClass, t, VK_RValue, OK_Ordinary, diff --git a/tools/clang/lib/AST/ExprClassification.cpp b/tools/clang/lib/AST/ExprClassification.cpp index e50a01e21a..69948f61fe 100644 --- a/tools/clang/lib/AST/ExprClassification.cpp +++ b/tools/clang/lib/AST/ExprClassification.cpp @@ -321,6 +321,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { if (cast(E)->isArrow()) return Cl::CL_LValue; return ClassifyInternal(Ctx, cast(E)->getBase()); + case Expr::HLSLOutParamExprClass: + return Cl::CL_LValue; + case Expr::HLSLArrayTemporaryExprClass: + return Cl::CL_PRValue; // HLSL Change Ends // Simply look at the actual default argument. diff --git a/tools/clang/lib/AST/ExprConstant.cpp b/tools/clang/lib/AST/ExprConstant.cpp index 5e8d4700bd..b307016950 100644 --- a/tools/clang/lib/AST/ExprConstant.cpp +++ b/tools/clang/lib/AST/ExprConstant.cpp @@ -9026,6 +9026,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::ExtVectorElementExprClass: case Expr::ExtMatrixElementExprClass: // HLSL Change case Expr::HLSLVectorElementExprClass: // HLSL Change + case Expr::HLSLOutParamExprClass: // HLSL Change + case Expr::HLSLArrayTemporaryExprClass: // HLSL Change case Expr::DesignatedInitExprClass: case Expr::NoInitExprClass: case Expr::DesignatedInitUpdateExprClass: diff --git a/tools/clang/lib/AST/HlslTypes.cpp b/tools/clang/lib/AST/HlslTypes.cpp index 76e2db9719..1a64db4b34 100644 --- a/tools/clang/lib/AST/HlslTypes.cpp +++ b/tools/clang/lib/AST/HlslTypes.cpp @@ -52,7 +52,7 @@ ConvertHLSLVecMatTypeToExtVectorType(const clang::ASTContext &context, } bool IsHLSLVecMatType(clang::QualType type) { - const Type *Ty = type.getCanonicalType().getTypePtr(); + const Type *Ty = type.getNonReferenceType().getCanonicalType().getTypePtr(); if (const RecordType *RT = dyn_cast(Ty)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast(RT->getDecl())) { @@ -67,7 +67,8 @@ bool IsHLSLVecMatType(clang::QualType type) { } bool IsHLSLMatType(clang::QualType type) { - const clang::Type *Ty = type.getCanonicalType().getTypePtr(); + const clang::Type *Ty = + type.getNonReferenceType().getCanonicalType().getTypePtr(); if (const RecordType *RT = dyn_cast(Ty)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast(RT->getDecl())) { @@ -80,7 +81,8 @@ bool IsHLSLMatType(clang::QualType type) { } bool IsHLSLVecType(clang::QualType type) { - const clang::Type *Ty = type.getCanonicalType().getTypePtr(); + const clang::Type *Ty = + type.getNonReferenceType().getCanonicalType().getTypePtr(); if (const RecordType *RT = dyn_cast(Ty)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast(RT->getDecl())) { @@ -93,7 +95,8 @@ bool IsHLSLVecType(clang::QualType type) { } bool IsHLSLNumericOrAggregateOfNumericType(clang::QualType type) { - const clang::Type *Ty = type.getCanonicalType().getTypePtr(); + const clang::Type *Ty = + type.getNonReferenceType().getCanonicalType().getTypePtr(); if (isa(Ty)) { if (IsHLSLVecMatType(type)) return true; @@ -108,7 +111,8 @@ bool IsHLSLNumericOrAggregateOfNumericType(clang::QualType type) { } bool IsHLSLNumericUserDefinedType(clang::QualType type) { - const clang::Type *Ty = type.getCanonicalType().getTypePtr(); + const clang::Type *Ty = + type.getNonReferenceType().getCanonicalType().getTypePtr(); if (const RecordType *RT = dyn_cast(Ty)) { const RecordDecl *RD = RT->getDecl(); if (!IsUserDefinedRecordType(type)) @@ -133,7 +137,7 @@ bool IsHLSLCopyableAnnotatableRecord(clang::QualType QT) { } bool IsHLSLBuiltinRayAttributeStruct(clang::QualType QT) { - QT = QT.getCanonicalType(); + QT = QT.getNonReferenceType().getCanonicalType(); const clang::Type *Ty = QT.getTypePtr(); if (const RecordType *RT = dyn_cast(Ty)) { const RecordDecl *RD = RT->getDecl(); @@ -146,7 +150,7 @@ bool IsHLSLBuiltinRayAttributeStruct(clang::QualType QT) { // Aggregate types are arrays and user-defined structs bool IsHLSLAggregateType(clang::QualType type) { - type = type.getCanonicalType(); + type = type.getCanonicalType().getNonReferenceType(); if (isa(type)) return true; return IsUserDefinedRecordType(type); @@ -237,6 +241,7 @@ bool HasHLSLUNormSNorm(clang::QualType type, bool *pIsSNorm) { } bool HasHLSLGloballyCoherent(clang::QualType type) { + type = type.getNonReferenceType(); const AttributedType *AT = type->getAs(); while (AT) { AttributedType::Kind kind = AT->getAttrKind(); @@ -439,7 +444,7 @@ clang::QualType GetHLSLMatElementType(clang::QualType type) { } // TODO: Add type cache to ASTContext. bool IsHLSLInputPatchType(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = dyn_cast(type)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast( @@ -452,7 +457,7 @@ bool IsHLSLInputPatchType(QualType type) { return false; } bool IsHLSLOutputPatchType(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = dyn_cast(type)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast( @@ -465,7 +470,7 @@ bool IsHLSLOutputPatchType(QualType type) { return false; } bool IsHLSLPointStreamType(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = dyn_cast(type)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast( @@ -477,7 +482,7 @@ bool IsHLSLPointStreamType(QualType type) { return false; } bool IsHLSLLineStreamType(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = dyn_cast(type)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast( @@ -489,7 +494,7 @@ bool IsHLSLLineStreamType(QualType type) { return false; } bool IsHLSLTriangleStreamType(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = dyn_cast(type)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast( @@ -501,7 +506,7 @@ bool IsHLSLTriangleStreamType(QualType type) { return false; } bool IsHLSLStreamOutputType(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = dyn_cast(type)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast( @@ -517,6 +522,7 @@ bool IsHLSLStreamOutputType(QualType type) { return false; } bool IsHLSLResourceType(clang::QualType type) { + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = type->getAs()) { StringRef name = RT->getDecl()->getName(); if (name == "Texture1D" || name == "RWTexture1D") @@ -580,6 +586,7 @@ bool IsHLSLDynamicResourceType(clang::QualType type) { } bool IsHLSLBufferViewType(clang::QualType type) { + type = type.getNonReferenceType(); if (const RecordType *RT = type->getAs()) { StringRef name = RT->getDecl()->getName(); if (name == "ConstantBuffer" || name == "TextureBuffer") @@ -589,6 +596,7 @@ bool IsHLSLBufferViewType(clang::QualType type) { } bool IsHLSLStructuredBufferType(clang::QualType type) { + type = type.getNonReferenceType(); if (const RecordType *RT = type->getAs()) { StringRef name = RT->getDecl()->getName(); if (name == "StructuredBuffer" || name == "RWStructuredBuffer") @@ -607,6 +615,7 @@ bool IsHLSLSubobjectType(clang::QualType type) { } bool IsUserDefinedRecordType(clang::QualType QT) { + QT = QT.getNonReferenceType(); const clang::Type *Ty = QT.getCanonicalType().getTypePtr(); if (const RecordType *RT = dyn_cast(Ty)) { const RecordDecl *RD = RT->getDecl(); @@ -699,7 +708,7 @@ bool DoesTypeDefineOverloadedOperator(clang::QualType typeWithOperator, bool GetHLSLSubobjectKind(clang::QualType type, DXIL::SubobjectKind &subobjectKind, DXIL::HitGroupType &hgType) { hgType = (DXIL::HitGroupType)(-1); - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = type->getAs()) { StringRef name = RT->getDecl()->getName(); switch (name.size()) { @@ -737,7 +746,7 @@ bool GetHLSLSubobjectKind(clang::QualType type, DXIL::SubobjectKind &subobjectKi } bool IsHLSLRayQueryType(clang::QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); if (const RecordType *RT = dyn_cast(type)) { if (const ClassTemplateSpecializationDecl *templateDecl = dyn_cast( @@ -752,6 +761,7 @@ bool IsHLSLRayQueryType(clang::QualType type) { QualType GetHLSLResourceResultType(QualType type) { // Don't canonicalize the type as to not lose snorm in Buffer + type = type.getNonReferenceType(); const RecordType *RT = type->getAs(); const RecordDecl* RD = RT->getDecl(); @@ -795,6 +805,7 @@ unsigned GetHLSLResourceTemplateUInt(clang::QualType type) { bool IsIncompleteHLSLResourceArrayType(clang::ASTContext &context, clang::QualType type) { + type = type.getNonReferenceType(); if (type->isIncompleteArrayType()) { const IncompleteArrayType *IAT = context.getAsIncompleteArrayType(type); type = IAT->getElementType(); @@ -809,7 +820,7 @@ bool IsIncompleteHLSLResourceArrayType(clang::ASTContext &context, } QualType GetHLSLResourceTemplateParamType(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); const RecordType *RT = cast(type); const ClassTemplateSpecializationDecl *templateDecl = cast(RT->getAsCXXRecordDecl()); @@ -821,7 +832,7 @@ QualType GetHLSLInputPatchElementType(QualType type) { return GetHLSLResourceTemplateParamType(type); } unsigned GetHLSLInputPatchCount(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); const RecordType *RT = cast(type); const ClassTemplateSpecializationDecl *templateDecl = cast(RT->getAsCXXRecordDecl()); @@ -832,7 +843,7 @@ clang::QualType GetHLSLOutputPatchElementType(QualType type) { return GetHLSLResourceTemplateParamType(type); } unsigned GetHLSLOutputPatchCount(QualType type) { - type = type.getCanonicalType(); + type = type.getNonReferenceType().getCanonicalType(); const RecordType *RT = cast(type); const ClassTemplateSpecializationDecl *templateDecl = cast(RT->getAsCXXRecordDecl()); diff --git a/tools/clang/lib/AST/StmtPrinter.cpp b/tools/clang/lib/AST/StmtPrinter.cpp index c99aeafc18..4b5d801a61 100644 --- a/tools/clang/lib/AST/StmtPrinter.cpp +++ b/tools/clang/lib/AST/StmtPrinter.cpp @@ -1389,6 +1389,12 @@ void StmtPrinter::VisitHLSLVectorElementExpr(HLSLVectorElementExpr *Node) { OS << "."; OS << Node->getAccessor().getName(); } +void StmtPrinter::VisitHLSLOutParamExpr(HLSLOutParamExpr *Node) { + OS << (Node->isInOut() ? "inout " : "out "); + PrintExpr(Node->getBase()); +} + +void StmtPrinter::VisitHLSLArrayTemporaryExpr(HLSLArrayTemporaryExpr *Node) {} // HLSL Change Ends void StmtPrinter::VisitCStyleCastExpr(CStyleCastExpr *Node) { OS << '('; diff --git a/tools/clang/lib/AST/StmtProfile.cpp b/tools/clang/lib/AST/StmtProfile.cpp index f5331d6db5..3b9b74c5c5 100644 --- a/tools/clang/lib/AST/StmtProfile.cpp +++ b/tools/clang/lib/AST/StmtProfile.cpp @@ -788,7 +788,7 @@ void StmtProfiler::VisitExtVectorElementExpr(const ExtVectorElementExpr *S) { VisitName(&S->getAccessor()); } -// HLSL Change +// HLSL Change Begin void StmtProfiler::VisitExtMatrixElementExpr(const ExtMatrixElementExpr *S) { VisitExpr(S); VisitName(&S->getAccessor()); @@ -798,6 +798,16 @@ void StmtProfiler::VisitHLSLVectorElementExpr(const HLSLVectorElementExpr *S) { VisitName(&S->getAccessor()); } +void StmtProfiler::VisitHLSLOutParamExpr(const HLSLOutParamExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitHLSLArrayTemporaryExpr( + const HLSLArrayTemporaryExpr *S) { + VisitExpr(S); +} +// HLSL Change End + void StmtProfiler::VisitBlockExpr(const BlockExpr *S) { VisitExpr(S); VisitDecl(S->getBlockDecl()); diff --git a/tools/clang/lib/CodeGen/CGCall.cpp b/tools/clang/lib/CodeGen/CGCall.cpp index f781e66a0d..8a6725e05a 100644 --- a/tools/clang/lib/CodeGen/CGCall.cpp +++ b/tools/clang/lib/CodeGen/CGCall.cpp @@ -2579,7 +2579,6 @@ void CodeGenFunction::EmitDelegateCallArg(CallArgList &args, args.add(convertTempToRValue(local, type, loc), type); } -#if 0 // HLSL Change - no ObjC support static bool isProvablyNull(llvm::Value *addr) { return isa(addr); } @@ -2592,6 +2591,39 @@ static bool isProvablyNonNull(llvm::Value *addr) { static void emitWriteback(CodeGenFunction &CGF, const CallArgList::Writeback &writeback) { const LValue &srcLV = writeback.Source; + + if (CGF.getLangOpts().HLSL) { + if (writeback.CastExpr) { + RValue TmpVal = CGF.EmitAnyExprToTemp(writeback.CastExpr); + if (TmpVal.isScalar()) + CGF.EmitStoreThroughLValue(TmpVal, srcLV); + else + CGF.EmitAggregateCopy(srcLV.getAddress(), TmpVal.getAggregateAddr(), + srcLV.getType()); + } else { + if (srcLV.isSimple()) + CGF.EmitAggregateCopy(srcLV.getAddress(), writeback.Temporary, + srcLV.getType()); + else { + llvm::Value *value; + if (hlsl::IsHLSLMatType(srcLV.getType())) + value = CGF.CGM.getHLSLRuntime().EmitHLSLMatrixLoad( + CGF, writeback.Temporary, srcLV.getType()); + else + value = CGF.Builder.CreateLoad(writeback.Temporary); + RValue TmpVal = RValue::get(value); + CGF.EmitStoreThroughLValue(TmpVal, srcLV); + } + } + if (CGF.CGM.getCodeGenOpts().HLSLEnableLifetimeMarkers) { + uint64_t Sz = CGF.CGM.getDataLayout().getTypeAllocSize( + CGF.ConvertTypeForMem(srcLV.getType())); + CGF.EmitLifetimeEnd(llvm::ConstantInt::get(CGF.Int64Ty, Sz), + writeback.Temporary); + } + return; + } + llvm::Value *srcAddr = srcLV.getAddress(); assert(!isProvablyNull(srcAddr) && "shouldn't have writeback for provably null argument"); @@ -2659,7 +2691,6 @@ static void emitWritebacks(CodeGenFunction &CGF, for (const auto &I : args.writebacks()) emitWriteback(CGF, I); } -#endif // HLSL Change - no ObjC support static void deactivateArgCleanupsBeforeCall(CodeGenFunction &CGF, const CallArgList &CallArgs) { @@ -2902,6 +2933,12 @@ void CodeGenFunction::EmitCallArgs(CallArgList &Args, // Un-reverse the arguments we just evaluated so they match up with the LLVM // IR function. std::reverse(Args.begin() + CallArgsStart, Args.end()); + + // HLSL Change Begin + // We also need to un-reverse the writebacks so that they are emitted left + // to right since the args were emitted right to left. + Args.reverseWritebacks(); + // HLSL Change End return; } @@ -3000,6 +3037,29 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, return args.add(RV, type); } } + + // Add writeback for HLSLOutParamExpr. + if (const HLSLOutParamExpr *OE = dyn_cast(E)) { + LValue LV = EmitLValue(E); + llvm::Value *Addr, *BaseAddr; + if (LV.isExtVectorElt()) { + llvm::Constant *VecElts = LV.getExtVectorElts(); + BaseAddr = LV.getExtVectorAddr(); + Addr = Builder.CreateGEP( + BaseAddr, + {Builder.getInt32(0), VecElts->getAggregateElement((unsigned)0)}); + } else // LV.getAddress() will assert if this is not a simple LValue. + Addr = BaseAddr = LV.getAddress(); + + if (!OE->canElide()) { + uint64_t Sz = CGM.getDataLayout().getTypeAllocSize( + ConvertTypeForMem(LV.getType())); + EmitLifetimeStart(Sz, BaseAddr); + args.addWriteback(EmitLValue(OE->getSrcLV()), Addr, nullptr, + OE->getWriteback()); + } + return args.add(RValue::get(Addr), type); + } // HLSL Change Ends. assert(E->getObjectKind() == OK_Ordinary); return args.add(EmitReferenceBindingToExpr(E), type); @@ -3044,15 +3104,25 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, return; } + // HLSL Change begin + // This is a bit hacky... Ideally this would be part of the resource type's + // copy constructor, but we don't have constructors worked out so instead + // performing this on LValueToRValue conversion is the next best thing :(. + if (hlsl::IsHLSLResourceType(E->getType()) && isa(E) && + cast(E)->getCastKind() == CK_LValueToRValue) { + LValue Val = CGM.getHLSLRuntime().EmitResourceParamAnnotation( + *this, cast(E)); + args.add(Val.asAggregateRValue(), type); + return; + } + // HLSL Change end + if (HasAggregateEvalKind && isa(E) && cast(E)->getCastKind() == CK_LValueToRValue) { LValue L = EmitLValue(cast(E)->getSubExpr()); assert(L.isSimple()); if (L.getAlignment() >= getContext().getTypeAlignInChars(type)) { - // HLSL Change Begin - don't copy input arg. - // Copy for out param is done at CGMSHLSLRuntime::EmitHLSLOutParamConversion*. - args.add(L.asAggregateRValue(), type); // /*NeedsCopy*/true); - // HLSL Change End + args.add(L.asAggregateRValue(), type, /*NeedsCopy*/true); } else { // We can't represent a misaligned lvalue in the CallArgList, so copy // to an aligned temporary now. @@ -3064,16 +3134,6 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, return; } - // HLSL Change Begins. - // For DeclRefExpr of aggregate type, don't create temp. - if (HasAggregateEvalKind && LangOptions().HLSL && - isa(E)) { - LValue LV = EmitDeclRefLValue(cast(E)); - RValue RV = RValue::getAggregate(LV.getAddress()); - args.add(RV, type); - return; - } - // HLSL Change Ends. args.add(EmitAnyExprToTemp(E), type); } @@ -3670,14 +3730,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (Builder.isNamePreserving() && !CI->getType()->isVoidTy()) CI->setName("call"); -#if 0 // HLSL Change - no ObjC support // Emit any writebacks immediately. Arguably this should happen // after any return-value munging. if (CallArgs.hasWritebacks()) emitWritebacks(*this, CallArgs); -#else - assert(!CallArgs.hasWritebacks() && "writebacks are unavailable in HLSL"); -#endif // HLSL Change - no ObjC support // The stack cleanup for inalloca arguments has to run out of the normal // lexical order, so deactivate it and run it manually here. diff --git a/tools/clang/lib/CodeGen/CGCall.h b/tools/clang/lib/CodeGen/CGCall.h index 4524087358..34fd8db61d 100644 --- a/tools/clang/lib/CodeGen/CGCall.h +++ b/tools/clang/lib/CodeGen/CGCall.h @@ -68,6 +68,8 @@ namespace CodeGen { /// A value to "use" after the writeback, or null. llvm::Value *ToUse; + + const Expr *CastExpr; // HLSL Change }; struct CallArgCleanup { @@ -84,25 +86,18 @@ namespace CodeGen { void addFrom(const CallArgList &other) { insert(end(), other.begin(), other.end()); -#if 0 // HLSL Change - no ObjC support Writebacks.insert(Writebacks.end(), other.Writebacks.begin(), other.Writebacks.end()); -#else - assert(!other.hasWritebacks() && "writeback is unreachable in HLSL"); -#endif // HLSL Change - no ObjC support } void addWriteback(LValue srcLV, llvm::Value *temporary, - llvm::Value *toUse) { -#if 0 // HLSL Change - no ObjC support + llvm::Value *toUse, const Expr *castExpr = nullptr) { Writeback writeback; writeback.Source = srcLV; writeback.Temporary = temporary; writeback.ToUse = toUse; + writeback.CastExpr = castExpr; Writebacks.push_back(writeback); -#else - llvm_unreachable("addWriteback is unreachable in HLSL"); -#endif // HLSL Change - no ObjC support } bool hasWritebacks() const { return !Writebacks.empty(); } @@ -134,6 +129,13 @@ namespace CodeGen { /// memory. bool isUsingInAlloca() const { return StackBase; } + // HLSL Change Begin + // Support reversing writebacks for MSVC ABI. + void reverseWritebacks() { + std::reverse(Writebacks.begin(), Writebacks.end()); + } + // HLSL Change End + private: SmallVector Writebacks; diff --git a/tools/clang/lib/CodeGen/CGExpr.cpp b/tools/clang/lib/CodeGen/CGExpr.cpp index 85469ecfb9..edbf196e0c 100644 --- a/tools/clang/lib/CodeGen/CGExpr.cpp +++ b/tools/clang/lib/CodeGen/CGExpr.cpp @@ -920,6 +920,8 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { return EmitExtMatrixElementExpr(cast(E)); case Expr::HLSLVectorElementExprClass: return EmitHLSLVectorElementExpr(cast(E)); + case Expr::HLSLOutParamExprClass: + return EmitHLSLOutParamExpr(cast(E)); case Expr::CXXThisExprClass: return MakeAddrLValue(LoadCXXThis(), E->getType()); // HLSL Change Ends @@ -2695,12 +2697,6 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, if (Idx->getType() != IntPtrTy) Idx = Builder.CreateIntCast(Idx, IntPtrTy, IdxSigned, "idxprom"); - // HLSL Change Starts - const Expr *Array = isSimpleArrayDecayOperand(E->getBase()); - assert((!getLangOpts().HLSL || nullptr == Array) && - "else array decay snuck in AST for HLSL"); - // HLSL Change Ends - // We know that the pointer points to a type of the correct size, unless the // size is a VLA or Objective-C interface. llvm::Value *Address = nullptr; @@ -2748,7 +2744,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, Address = EmitCastToVoidPtr(Base); Address = Builder.CreateGEP(Address, Idx, "arrayidx"); Address = Builder.CreateBitCast(Address, Base->getType()); - } else if (!getLangOpts().HLSL && Array) { // HLSL Change - No Array to pointer decay for HLSL + } else if (const Expr *Array = isSimpleArrayDecayOperand(E->getBase())) { // If this is A[i] where A is an array, the frontend will have decayed the // base to be a ArrayToPointerDecay implicit cast. While correct, it is // inefficient at -O0 to emit a "gep A, 0, 0" when codegen'ing it, then a @@ -2774,38 +2770,12 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, else Address = Builder.CreateInBoundsGEP(ArrayPtr, Args, "arrayidx"); } else { - // HLSL Change Starts - const ArrayType *AT = dyn_cast(E->getBase()->getType()->getCanonicalTypeUnqualified()); - if (getContext().getLangOpts().HLSL && AT) { - LValue ArrayLV; - // For simple multidimensional array indexing, set the 'accessed' flag for - // better bounds-checking of the base expression. - if (const auto *ASE = dyn_cast(E->getBase())) - ArrayLV = EmitArraySubscriptExpr(ASE, /*Accessed*/ true); - else - ArrayLV = EmitLValue(E->getBase()); - llvm::Value *ArrayPtr = ArrayLV.getAddress(); - - llvm::Value *Zero = llvm::ConstantInt::get(Int32Ty, 0); - llvm::Value *Args[] = { Zero, Idx }; - - // Propagate the alignment from the array itself to the result. - ArrayAlignment = ArrayLV.getAlignment(); - - if (getLangOpts().isSignedOverflowDefined()) - Address = Builder.CreateGEP(ArrayPtr, Args, "arrayidx"); - else - Address = Builder.CreateInBoundsGEP(ArrayPtr, Args, "arrayidx"); - - } else { - // HLSL Change Ends - // The base must be a pointer, which is not an aggregate. Emit it. - llvm::Value *Base = EmitScalarExpr(E->getBase()); - if (getLangOpts().isSignedOverflowDefined()) - Address = Builder.CreateGEP(Base, Idx, "arrayidx"); - else - Address = Builder.CreateInBoundsGEP(Base, Idx, "arrayidx"); - } // HLSL Change + // The base must be a pointer, which is not an aggregate. Emit it. + llvm::Value *Base = EmitScalarExpr(E->getBase()); + if (getLangOpts().isSignedOverflowDefined()) + Address = Builder.CreateGEP(Base, Idx, "arrayidx"); + else + Address = Builder.CreateInBoundsGEP(Base, Idx, "arrayidx"); } QualType T = E->getBase()->getType()->getPointeeType(); @@ -2961,6 +2931,40 @@ CodeGenFunction::EmitExtMatrixElementExpr(const ExtMatrixElementExpr *E) { *this, ResultTy, {matBase, CV}, E->getBase()->getType()); return MakeAddrLValue(Result, E->getType()); } + +LValue CodeGenFunction::EmitHLSLOutParamExpr(const HLSLOutParamExpr *E) { + if (E->canElide()) + return EmitLValue(E->getBase()); + llvm::Type *Ty = ConvertTypeForMem(E->getType()); + // TODO: Use CreateAggTemp + llvm::Value *OutTemp; + + if (E->isInOut()) { + RValue InVal = EmitAnyExprToTemp(E->getBase()); + if (!InVal.isScalar()) { + OutTemp = InVal.getAggregateAddr(); + } else { + OutTemp = CreateTempAlloca(Ty, "hlsl.out"); + if (hlsl::IsHLSLMatType(E->getType())) { + // Use matrix store to keep major info. + CGM.getHLSLRuntime().EmitHLSLMatrixStore(*this, InVal.getScalarVal(), + OutTemp, E->getType()); + } + else { + llvm::Value *V = InVal.getScalarVal(); + if (V->getType()->getScalarType()->isIntegerTy(1)) + V = Builder.CreateZExt(V, Ty, "frombool"); + (void)Builder.CreateStore(V, OutTemp); + } + } + } else + OutTemp = CreateTempAlloca(Ty, "hlsl.out"); + LValue Result = MakeAddrLValue(OutTemp, E->getType()); + if (auto *OpV = E->getOpaqueValue()) + OpaqueValueMappingData::bind(*this, OpV, Result); + return Result; +} + LValue CodeGenFunction::EmitHLSLVectorElementExpr(const HLSLVectorElementExpr *E) { // Emit the base vector as an l-value. @@ -3535,7 +3539,8 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { llvm::Value *bitcast = Builder.CreateBitCast(LV.getAddress(), ResultType); return MakeAddrLValue(bitcast, ToType); } - case CK_FlatConversion: { + case CK_FlatConversion: + case CK_HLSLDerivedToBase:{ // Just bitcast. QualType ToType = getContext().getLValueReferenceType(E->getType()); @@ -3551,34 +3556,6 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { Builder.AllowFolding = originAllowFolding; return MakeAddrLValue(bitcast, ToType); } - case CK_HLSLDerivedToBase: { - // HLSL only single inheritance. - // Just GEP. - QualType ToType = getContext().getLValueReferenceType(E->getType()); - - LValue LV = EmitLValue(E->getSubExpr()); - llvm::Value *This = LV.getAddress(); - - // gep to target type - llvm::Type *ResultType = ConvertType(ToType); - unsigned level = 0; - llvm::Type *ToTy = ResultType->getPointerElementType(); - llvm::Type *FromTy = This->getType()->getPointerElementType(); - // For empty struct, just bitcast. - if (FromTy->getStructNumElements()== 0) { - llvm::Value *bitcast = Builder.CreateBitCast(This, ResultType); - return MakeAddrLValue(bitcast, ToType); - } - - while (ToTy != FromTy) { - FromTy = FromTy->getStructElementType(0); - ++level; - } - llvm::Value *zeroIdx = Builder.getInt32(0); - SmallVector IdxList(level + 1, zeroIdx); - llvm::Value *GEP = Builder.CreateInBoundsGEP(This, IdxList); - return MakeAddrLValue(GEP, ToType); - } case CK_HLSLMatrixSplat: case CK_HLSLMatrixToScalarCast: case CK_HLSLMatrixTruncationCast: @@ -3940,28 +3917,16 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee, // HLSL Change Begins llvm::SmallVector castArgList; llvm::SmallVector lifetimeCleanupList; - // The argList of the CallExpr, may be update for out parameter - llvm::SmallVector argList(E->arg_begin(), E->arg_end()); - ConstExprIterator argBegin = argList.data(); - ConstExprIterator argEnd = argList.data() + E->getNumArgs(); // out param conversion CodeGenFunction::HLSLOutParamScope OutParamScope(*this); - auto MapTemp = [&](const VarDecl *LocalVD, llvm::Value *TmpArg) { - OutParamScope.addTemp(LocalVD, TmpArg); - }; - if (getLangOpts().HLSL) { - if (const FunctionDecl *FD = E->getDirectCallee()) - CGM.getHLSLRuntime().EmitHLSLOutParamConversionInit(*this, FD, E, - castArgList, argList, lifetimeCleanupList, MapTemp); - } // HLSL Change Ends CallArgList Args; if (Chain) Args.add(RValue::get(Builder.CreateBitCast(Chain, CGM.VoidPtrTy)), CGM.getContext().VoidPtrTy); - EmitCallArgs(Args, dyn_cast(FnType), argBegin, argEnd, // HLSL Change - use updated argList - E->getDirectCallee(), /*ParamsToSkip*/ 0); + EmitCallArgs(Args, dyn_cast(FnType), E->arg_begin(), + E->arg_end(), E->getDirectCallee(), /*ParamsToSkip*/ 0); const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionCall( Args, FnType, /*isChainCall=*/Chain); @@ -3993,13 +3958,6 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee, } RValue CallVal = EmitCall(FnInfo, Callee, ReturnValue, Args, TargetDecl); - // HLSL Change Begins - // out param conversion - // conversion and copy back after the call - if (getLangOpts().HLSL) - CGM.getHLSLRuntime().EmitHLSLOutParamConversionCopyBack(*this, castArgList, lifetimeCleanupList); - // HLSL Change Ends - return CallVal; } diff --git a/tools/clang/lib/CodeGen/CGExprAgg.cpp b/tools/clang/lib/CodeGen/CGExprAgg.cpp index ac44ad3d68..7ab735a2ce 100644 --- a/tools/clang/lib/CodeGen/CGExprAgg.cpp +++ b/tools/clang/lib/CodeGen/CGExprAgg.cpp @@ -202,6 +202,8 @@ class AggExprEmitter : public StmtVisitor { void VisitAtomicExpr(AtomicExpr *E) { CGF.EmitAtomicExpr(E, EnsureSlot(E->getType()).getAddr()); } + + void VisitHLSLArrayTemporaryExpr(HLSLArrayTemporaryExpr *E); // HLSL Change }; } // end anonymous namespace. @@ -541,6 +543,12 @@ void AggExprEmitter::EmitArrayInit(llvm::Value *DestPtr, llvm::ArrayType *AType, // Visitor Methods //===----------------------------------------------------------------------===// +// HLSL Change Begin +void AggExprEmitter::VisitHLSLArrayTemporaryExpr(HLSLArrayTemporaryExpr *E) { + Visit(E->getBase()); +} +// HLSL Change End + void AggExprEmitter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E){ Visit(E->GetTemporaryExpr()); } diff --git a/tools/clang/lib/CodeGen/CGExprCXX.cpp b/tools/clang/lib/CodeGen/CGExprCXX.cpp index 2efde7c30f..39ad63ddc7 100644 --- a/tools/clang/lib/CodeGen/CGExprCXX.cpp +++ b/tools/clang/lib/CodeGen/CGExprCXX.cpp @@ -28,9 +28,7 @@ using namespace CodeGen; static RequiredArgs commonEmitCXXMemberOrOperatorCall( CodeGenFunction &CGF, const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue, llvm::Value *This, llvm::Value *ImplicitParam, - QualType ImplicitParamTy, const CallExpr *CE, CallArgList &Args, - ArrayRef argList// HLSL Change - use updated argList for out parameter. - ) { + QualType ImplicitParamTy, const CallExpr *CE, CallArgList &Args) { assert(CE == nullptr || isa(CE) || isa(CE)); assert(MD->isInstance() && @@ -62,9 +60,7 @@ static RequiredArgs commonEmitCXXMemberOrOperatorCall( if (CE) { // Special case: skip first argument of CXXOperatorCall (it is "this"). unsigned ArgsToSkip = isa(CE) ? 1 : 0; - CGF.EmitCallArgs(Args, FPT, - argList.begin() + ArgsToSkip, // HLSL Change - use updated argList for out parameter. - argList.end(), // HLSL Change - use updated argList for out parameter. + CGF.EmitCallArgs(Args, FPT, CE->arg_begin() + ArgsToSkip, CE->arg_end(), CE->getDirectCallee()); } else { assert( @@ -80,36 +76,11 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall( const CallExpr *CE) { const FunctionProtoType *FPT = MD->getType()->castAs(); CallArgList Args; - - // HLSL Change Begins - llvm::SmallVector castArgList; - llvm::SmallVector lifetimeCleanupList; - // The argList of the CallExpr, may be update for out parameter - llvm::SmallVector argList(CE->arg_begin(), CE->arg_end()); - // out param conversion - CodeGenFunction::HLSLOutParamScope OutParamScope(*this); - auto MapTemp = [&](const VarDecl *LocalVD, llvm::Value *TmpArg) { - OutParamScope.addTemp(LocalVD, TmpArg); - }; - if (getLangOpts().HLSL) { - if (const FunctionDecl *FD = CE->getDirectCallee()) - CGM.getHLSLRuntime().EmitHLSLOutParamConversionInit(*this, FD, CE, - castArgList, argList, lifetimeCleanupList, MapTemp); - } - // HLSL Change Ends - RequiredArgs required = commonEmitCXXMemberOrOperatorCall( *this, MD, Callee, ReturnValue, This, ImplicitParam, ImplicitParamTy, CE, - Args, argList); // HLSL Change - use updated argList. - RValue CallVal = EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), + Args); + return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), Callee, ReturnValue, Args, MD); - // HLSL Change Begins - // out param conversion - // conversion and copy back after the call - if (getLangOpts().HLSL) - CGM.getHLSLRuntime().EmitHLSLOutParamConversionCopyBack(*this, castArgList, lifetimeCleanupList); - // HLSL Change Ends - return CallVal; } RValue CodeGenFunction::EmitCXXStructorCall( @@ -117,33 +88,10 @@ RValue CodeGenFunction::EmitCXXStructorCall( llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy, const CallExpr *CE, StructorType Type) { CallArgList Args; - // HLSL Change Begins - llvm::SmallVector castArgList; - llvm::SmallVector lifetimeCleanupList; - // The argList of the CallExpr, may be update for out parameter - llvm::SmallVector argList(CE->arg_begin(), CE->arg_end()); - // out param conversion - CodeGenFunction::HLSLOutParamScope OutParamScope(*this); - auto MapTemp = [&](const VarDecl *LocalVD, llvm::Value *TmpArg) { - OutParamScope.addTemp(LocalVD, TmpArg); - }; - if (getLangOpts().HLSL) { - if (const FunctionDecl *FD = CE->getDirectCallee()) - CGM.getHLSLRuntime().EmitHLSLOutParamConversionInit(*this, FD, CE, - castArgList, argList, lifetimeCleanupList, MapTemp); - } - // HLSL Change Ends commonEmitCXXMemberOrOperatorCall(*this, MD, Callee, ReturnValue, This, - ImplicitParam, ImplicitParamTy, CE, Args, - argList); // HLSL Change - use updated argList. + ImplicitParam, ImplicitParamTy, CE, Args); RValue CallVal = EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(MD, Type), Callee, ReturnValue, Args, MD); - // HLSL Change Begins - // out param conversion - // conversion and copy back after the call - if (getLangOpts().HLSL) - CGM.getHLSLRuntime().EmitHLSLOutParamConversionCopyBack(*this, castArgList, lifetimeCleanupList); - // HLSL Change Ends return CallVal; } diff --git a/tools/clang/lib/CodeGen/CGExprScalar.cpp b/tools/clang/lib/CodeGen/CGExprScalar.cpp index 0cb993e6f4..2a832b4de4 100644 --- a/tools/clang/lib/CodeGen/CGExprScalar.cpp +++ b/tools/clang/lib/CodeGen/CGExprScalar.cpp @@ -334,8 +334,11 @@ class ScalarExprEmitter Value *VisitConvertVectorExpr(ConvertVectorExpr *E); Value *VisitMemberExpr(MemberExpr *E); Value *VisitExtVectorElementExpr(Expr *E) { return EmitLoadOfLValue(E); } - Value *VisitExtMatrixElementExpr(Expr *E) { return EmitLoadOfLValue(E); } // HLSL Change - Value *VisitHLSLVectorElementExpr(Expr *E) { return EmitLoadOfLValue(E); } // HLSL Change + + // HLSL Change Begin + Value *VisitExtMatrixElementExpr(Expr *E) { return EmitLoadOfLValue(E); } + Value *VisitHLSLVectorElementExpr(Expr *E) { return EmitLoadOfLValue(E); } + // HLSL Change end Value *VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { return EmitLoadOfLValue(E); } diff --git a/tools/clang/lib/CodeGen/CGHLSLMS.cpp b/tools/clang/lib/CodeGen/CGHLSLMS.cpp index bfd2aa8022..55b50155b6 100644 --- a/tools/clang/lib/CodeGen/CGHLSLMS.cpp +++ b/tools/clang/lib/CodeGen/CGHLSLMS.cpp @@ -213,6 +213,7 @@ class CGMSHLSLRuntime : public CGHLSLRuntime { void ConstructFieldAttributedAnnotation(DxilFieldAnnotation &fieldAnnotation, QualType fieldTy, bool bDefaultRowMajor); + LValue EmitResourceParamAnnotation(CodeGenFunction& CGF, const CastExpr *E) override; std::unordered_map m_ConstVarAnnotationMap; StringSet<> m_PreciseOutputSet; @@ -236,21 +237,9 @@ class CGMSHLSLRuntime : public CGHLSLRuntime { RValue EmitHLSLBuiltinCallExpr(CodeGenFunction &CGF, const FunctionDecl *FD, const CallExpr *E, ReturnValueSlot ReturnValue) override; - void EmitHLSLOutParamConversionInit( - CodeGenFunction &CGF, const FunctionDecl *FD, const CallExpr *E, - llvm::SmallVector &castArgList, - llvm::SmallVector &argList, - llvm::SmallVector &lifetimeCleanupList, - const std::function &TmpArgMap) - override; - void EmitHLSLOutParamConversionCopyBack( - CodeGenFunction &CGF, llvm::SmallVector &castArgList, - llvm::SmallVector &lifetimeCleanupList) override; - Value *EmitHLSLMatrixOperationCall(CodeGenFunction &CGF, const clang::Expr *E, llvm::Type *RetType, ArrayRef paramList) override; - void EmitHLSLDiscard(CodeGenFunction &CGF) override; BranchInst *EmitHLSLCondBreak(CodeGenFunction &CGF, llvm::Function *F, llvm::BasicBlock *DestBB, llvm::BasicBlock *AltBB) override; @@ -1213,9 +1202,7 @@ unsigned CGMSHLSLRuntime::AddTypeAnnotation(QualType Ty, if (Ty.isNull()) return 0; - QualType paramTy = Ty.getCanonicalType(); - if (const ReferenceType *RefType = dyn_cast(paramTy)) - paramTy = RefType->getPointeeType(); + QualType paramTy = Ty.getNonReferenceType().getCanonicalType(); // Get size. llvm::Type *Type = CGM.getTypes().ConvertType(paramTy); @@ -1266,6 +1253,7 @@ unsigned CGMSHLSLRuntime::AddTypeAnnotation(QualType Ty, return 0; } else { unsigned arraySize = 0; + Ty = Ty.getNonReferenceType(); QualType arrayElementTy = Ty; if (Ty->isConstantArrayType()) { const ConstantArrayType *arrayTy = @@ -1873,13 +1861,13 @@ void CGMSHLSLRuntime::AddHLSLFunctionInfo(Function *F, const FunctionDecl *FD) { const ParmVarDecl *parmDecl = FD->getParamDecl(ParmIdx); - QualType fieldTy = parmDecl->getType(); + QualType fieldTy = parmDecl->getType().getNonReferenceType(); // Save object properties for parameters. AddValToPropertyMap(ArgIt, fieldTy); // if parameter type is a typedef, try to desugar it first. - if (isa(fieldTy.getTypePtr())) - fieldTy = fieldTy.getDesugaredType(FD->getASTContext()); + if (auto T = fieldTy->getAs()) + fieldTy = T->desugar(); ConstructFieldAttributedAnnotation(paramAnnotation, fieldTy, bDefaultRowMajor); if (parmDecl->hasAttr()) @@ -5844,348 +5832,6 @@ void CGMSHLSLRuntime::EmitHLSLRootSignature(HLSLRootSignatureAttr *RSA, } } -void CGMSHLSLRuntime::EmitHLSLOutParamConversionInit( - CodeGenFunction &CGF, const FunctionDecl *FD, const CallExpr *E, - llvm::SmallVector &castArgList, - llvm::SmallVector &argList, - llvm::SmallVector &lifetimeCleanupList, - const std::function &TmpArgMap) { - // Special case: skip first argument of CXXOperatorCall (it is "this"). - unsigned ArgsToSkip = isa(E) ? 1 : 0; - llvm::SmallSet ArgVals; - for (uint32_t i = 0; i < FD->getNumParams(); i++) { - const ParmVarDecl *Param = FD->getParamDecl(i); - uint32_t ArgIdx = i+ArgsToSkip; - const Expr *Arg = E->getArg(ArgIdx); - QualType ParamTy = Param->getType().getNonReferenceType(); - bool isObject = dxilutil::IsHLSLObjectType(CGF.ConvertTypeForMem(ParamTy)); - bool bAnnotResource = false; - if (isObject) { - if (isGLCMismatch(Param->getType(), Arg->getType(), Arg, - Arg->getExprLoc(), CGM.getDiags())) { - // NOTE: if function is noinline, resource parameter is not allowed. - // Here assume function will be always inlined. - // This can only take care resource as parameter. When parameter is - // struct with resource member, glc cannot mismatch because the - // struct type will always match. - // Add annotate handle here. - bAnnotResource = true; - } - } - bool isVector = hlsl::IsHLSLVecType(ParamTy); - bool isArray = ParamTy->isArrayType(); - // Check for array of matrix - QualType ParamElTy = ParamTy; - while (ParamElTy->isArrayType()) - ParamElTy = ParamElTy->getAsArrayTypeUnsafe()->getElementType(); - bool isMatrix = hlsl::IsHLSLMatType(ParamElTy); - bool isAggregateType = !isObject && - (isArray || (ParamTy->isRecordType() && !(isMatrix || isVector))); - - bool EmitRValueAgg = false; - bool RValOnRef = false; - if (!Param->isModifierOut()) { - if (!isAggregateType && !isObject) { - if (Arg->isRValue() && Param->getType()->isReferenceType()) { - // RValue on a reference type. - if (const CStyleCastExpr *cCast = dyn_cast(Arg)) { - // TODO: Evolving this to warn then fail in future language versions. - // Allow special case like cast uint to uint for back-compat. - if (cCast->getCastKind() == CastKind::CK_NoOp) { - if (const ImplicitCastExpr *cast = - dyn_cast(cCast->getSubExpr())) { - if (cast->getCastKind() == CastKind::CK_LValueToRValue) { - // update the arg - argList[ArgIdx] = cast->getSubExpr(); - continue; - } - } - } - } - // EmitLValue will report error. - // Mark RValOnRef to create tmpArg for it. - RValOnRef = true; - } else { - continue; - } - } else if (isAggregateType) { - // aggregate in-only - emit RValue, unless LValueToRValue cast - EmitRValueAgg = true; - if (const ImplicitCastExpr *cast = - dyn_cast(Arg)) { - if (cast->getCastKind() == CastKind::CK_LValueToRValue) { - EmitRValueAgg = false; - } - } - } else { - // Must be object - DXASSERT(isObject, "otherwise, flow condition changed, breaking assumption"); - // in-only objects should be skipped to preserve previous behavior. - if (!bAnnotResource) - continue; - } - } - - // Skip unbounded array, since we cannot preserve copy-in copy-out - // semantics for these. - if (ParamTy->isIncompleteArrayType()) { - continue; - } - - if (!Param->isModifierOut() && !RValOnRef) { - // No need to copy arg to in-only param for hlsl intrinsic. - if (const FunctionDecl *Callee = E->getDirectCallee()) { - if (Callee->hasAttr()) - continue; - } - } - - - // get original arg - // FIXME: This will not emit in correct argument order with the other - // arguments. This should be integrated into - // CodeGenFunction::EmitCallArg if possible. - RValue argRV; // emit this if aggregate arg on in-only param - LValue argLV; // otherwise, we may emit this - llvm::Value *argAddr = nullptr; - QualType argType = Arg->getType(); - CharUnits argAlignment; - if (EmitRValueAgg) { - argRV = CGF.EmitAnyExprToTemp(Arg); - argAddr = argRV.getAggregateAddr(); // must be alloca - argAlignment = CharUnits::fromQuantity(cast(argAddr)->getAlignment()); - argLV = LValue::MakeAddr(argAddr, ParamTy, argAlignment, CGF.getContext()); - } else { - argLV = CGF.EmitLValue(Arg); - if (argLV.isSimple()) - argAddr = argLV.getAddress(); - - bool mustCopy = bAnnotResource; - - // If matrix orientation changes, we must copy here - // TODO: A high level intrinsic for matrix array copy with orientation - // change would be much easier to optimize/eliminate at high level - // after inline. - if (!mustCopy && isMatrix) { - mustCopy = !AreMatrixArrayOrientationMatching( - CGF.getContext(), *m_pHLModule, argType, ParamTy); - } - - if (!mustCopy) { - // When there's argument need to lower like buffer/cbuffer load, need to - // copy to let the lower not happen on argument when calle is noinline - // or extern functions. Will do it in HLLegalizeParameter after known - // which functions are extern but before inline. - Value *Ptr = argAddr; - while (GEPOperator *GEP = dyn_cast_or_null(Ptr)) { - Ptr = GEP->getPointerOperand(); - } - // Skip copy-in copy-out when safe. - // The unsafe case will be global variable alias with parameter. - // Then global variable is updated in the function, the parameter will - // be updated silently. For non global variable or constant global - // variable, it should be safe. - bool SafeToSkip = false; - if (GlobalVariable *GV = dyn_cast_or_null(Ptr)) { - SafeToSkip = ParamTy.isConstQualified() && (m_ConstVarAnnotationMap.count(GV) > 0 || GV->isConstant()); - } - if (Ptr) { - if (isa(Ptr) && 0 == ArgVals.count(Ptr)) - SafeToSkip = true; - else if (const auto *A = dyn_cast(Ptr)) - SafeToSkip = A->hasNoAliasAttr() && 0 == ArgVals.count(Ptr); - } - - if (argAddr && SafeToSkip) { - ArgVals.insert(Ptr); - llvm::Type *ToTy = CGF.ConvertType(ParamTy.getNonReferenceType()); - if (argAddr->getType()->getPointerElementType() == ToTy && - // Check clang Type for case like int cast to unsigned. - ParamTy.getNonReferenceType().getCanonicalType().getTypePtr() == - Arg->getType().getCanonicalType().getTypePtr()) - continue; - } - } - - argType = argLV.getType(); // TBD: Can this be different than Arg->getType()? - argAlignment = argLV.getAlignment(); - } - // After emit Arg, we must update the argList[i], - // otherwise we get double emit of the expression. - - // create temp Var - VarDecl *tmpArg = - VarDecl::Create(CGF.getContext(), const_cast(FD), - SourceLocation(), SourceLocation(), - /*IdentifierInfo*/ nullptr, ParamTy, - CGF.getContext().getTrivialTypeSourceInfo(ParamTy), - StorageClass::SC_Auto); - - // Aggregate type will be indirect param convert to pointer type. - // So don't update to ReferenceType, use RValue for it. - const DeclRefExpr *tmpRef = DeclRefExpr::Create( - CGF.getContext(), NestedNameSpecifierLoc(), SourceLocation(), tmpArg, - /*enclosing*/ false, tmpArg->getLocation(), ParamTy, - (isAggregateType || isObject) ? VK_RValue : VK_LValue); - - // must update the arg, since we did emit Arg, else we get double emit. - argList[ArgIdx] = tmpRef; - - // create alloc for the tmp arg - Value *tmpArgAddr = nullptr; - BasicBlock *InsertBlock = CGF.Builder.GetInsertBlock(); - Function *F = InsertBlock->getParent(); - - // Make sure the alloca is in entry block to stop inline create stacksave. - IRBuilder<> AllocaBuilder(dxilutil::FindAllocaInsertionPt(F)); - tmpArgAddr = AllocaBuilder.CreateAlloca(CGF.ConvertTypeForMem(ParamTy)); - - if (CGM.getCodeGenOpts().HLSLEnableLifetimeMarkers) { - const uint64_t AllocaSize = CGM.getDataLayout().getTypeAllocSize(CGF.ConvertTypeForMem(ParamTy)); - CGF.EmitLifetimeStart(AllocaSize, tmpArgAddr); - } - - // add it to local decl map - TmpArgMap(tmpArg, tmpArgAddr); - - LValue tmpLV = LValue::MakeAddr(tmpArgAddr, ParamTy, argAlignment, - CGF.getContext()); - - // save for cast after call - if (Param->isModifierOut()) { - castArgList.emplace_back(tmpLV); - castArgList.emplace_back(argLV); - if (isVector && !hlsl::IsHLSLVecType(argType)) { - // This assumes only implicit casts because explicit casts can only produce RValues - // currently and out parameters are LValues. - DiagnosticsEngine &Diags = CGM.getDiags(); - Diags.Report(Param->getLocation(), diag::warn_hlsl_implicit_vector_truncation); - } - } - - // save to generate lifetime end after call - if (CGM.getCodeGenOpts().HLSLEnableLifetimeMarkers) - lifetimeCleanupList.emplace_back(tmpLV); - - // cast before the call - if (Param->isModifierIn() && - // Don't copy object - !isObject) { - QualType ArgTy = Arg->getType(); - Value *outVal = nullptr; - if (!isAggregateType) { - if (!IsHLSLMatType(ParamTy)) { - RValue outRVal = CGF.EmitLoadOfLValue(argLV, SourceLocation()); - outVal = outRVal.getScalarVal(); - } else { - DXASSERT(argAddr, "should be RV or simple LV"); - outVal = EmitHLSLMatrixLoad(CGF, argAddr, ArgTy); - } - - llvm::Type *ToTy = tmpArgAddr->getType()->getPointerElementType(); - if (HLMatrixType::isa(ToTy)) { - Value *castVal = CGF.Builder.CreateBitCast(outVal, ToTy); - EmitHLSLMatrixStore(CGF, castVal, tmpArgAddr, ParamTy); - } - else { - if (outVal->getType()->isVectorTy()) { - Value *castVal = ConvertScalarOrVector(CGF, outVal, argType, ParamTy); - castVal = CGF.EmitToMemory(castVal, ParamTy); - CGF.Builder.CreateStore(castVal, tmpArgAddr); - } else { - // This allows for splatting, unlike the above. - SimpleFlatValCopy(CGF, outVal, argType, tmpArgAddr, ParamTy); - } - } - } else { - DXASSERT(argAddr, "should be RV or simple LV"); - SmallVector idxList; - EmitHLSLAggregateCopy(CGF, argAddr, tmpArgAddr, - idxList, ArgTy, ParamTy, - argAddr->getType()); - } - } else if (bAnnotResource) { - DxilResourceProperties RP = BuildResourceProperty(Arg->getType()); - CopyAndAnnotateResourceArgument(argAddr, tmpArgAddr, RP, *m_pHLModule, - CGF); - mismatchGLCArgSet.insert(tmpArgAddr); - } - } -} - -void CGMSHLSLRuntime::EmitHLSLOutParamConversionCopyBack( - CodeGenFunction &CGF, llvm::SmallVector &castArgList, - llvm::SmallVector &lifetimeCleanupList) { - for (uint32_t i = 0; i < castArgList.size(); i += 2) { - // cast after the call - LValue tmpLV = castArgList[i]; - LValue argLV = castArgList[i + 1]; - QualType ArgTy = argLV.getType().getNonReferenceType(); - QualType ParamTy = tmpLV.getType().getNonReferenceType(); - - Value *tmpArgAddr = tmpLV.getAddress(); - - Value *outVal = nullptr; - - bool isAggregateTy = hlsl::IsHLSLAggregateType(ArgTy); - - bool isObject = dxilutil::IsHLSLObjectType( - tmpArgAddr->getType()->getPointerElementType()); - if (!isObject) { - if (!isAggregateTy) { - if (!IsHLSLMatType(ParamTy)) - outVal = CGF.Builder.CreateLoad(tmpArgAddr); - else - outVal = EmitHLSLMatrixLoad(CGF, tmpArgAddr, ParamTy); - - outVal = CGF.EmitFromMemory(outVal, ParamTy); - - llvm::Type *ToTy = CGF.ConvertType(ArgTy); - llvm::Type *FromTy = outVal->getType(); - Value *castVal = outVal; - if (ToTy == FromTy) { - // Don't need cast. - } else if (ToTy->getScalarType() == FromTy->getScalarType()) { - if (ToTy->getScalarType() == ToTy) { - DXASSERT(FromTy->isVectorTy(), "must be vector"); - castVal = CGF.Builder.CreateExtractElement(outVal, (uint64_t)0); - } else { - DXASSERT(!FromTy->isVectorTy(), "must be scalar type"); - DXASSERT(ToTy->isVectorTy() && ToTy->getVectorNumElements() == 1, - "must be vector of 1 element"); - castVal = UndefValue::get(ToTy); - castVal = - CGF.Builder.CreateInsertElement(castVal, outVal, (uint64_t)0); - } - } else { - castVal = ConvertScalarOrVector(CGF, - outVal, tmpLV.getType(), argLV.getType()); - } - if (!HLMatrixType::isa(ToTy)) - CGF.EmitStoreThroughLValue(RValue::get(castVal), argLV); - else { - Value *destPtr = argLV.getAddress(); - EmitHLSLMatrixStore(CGF, castVal, destPtr, ArgTy); - } - } else { - SmallVector idxList; - EmitHLSLAggregateCopy(CGF, tmpLV.getAddress(), argLV.getAddress(), - idxList, ParamTy, ArgTy, - argLV.getAddress()->getType()); - } - } else if (mismatchGLCArgSet.find(tmpArgAddr) == mismatchGLCArgSet.end()) { - tmpArgAddr->replaceAllUsesWith(argLV.getAddress()); - } - } - - for (LValue &tmpLV : lifetimeCleanupList) { - QualType ParamTy = tmpLV.getType().getNonReferenceType(); - Value *tmpArgAddr = tmpLV.getAddress(); - const uint64_t AllocaSize = CGM.getDataLayout().getTypeAllocSize(CGF.ConvertTypeForMem(ParamTy)); - CGF.EmitLifetimeEnd(CGF.Builder.getInt64(AllocaSize), tmpArgAddr); - } -} - ScopeInfo *CGMSHLSLRuntime::GetScopeInfo(Function *F) { auto it = m_ScopeMap.find(F); if (it == m_ScopeMap.end()) @@ -6233,6 +5879,20 @@ Scope *CGMSHLSLRuntime::MarkScopeEnd(CodeGenFunction &CGF) { return nullptr; } +LValue CGMSHLSLRuntime::EmitResourceParamAnnotation(CodeGenFunction &CGF, + const CastExpr *E) { + LValue LV = CGF.EmitLValue(E->getSubExpr()); + IRBuilder<> Builder(dxilutil::FindAllocaInsertionPt( + CGF.Builder.GetInsertBlock()->getParent())); + llvm::Value *TmpAddr = + Builder.CreateAlloca(CGF.ConvertTypeForMem(E->getType())); + DxilResourceProperties RP = BuildResourceProperty(E->getType()); + CopyAndAnnotateResourceArgument(LV.getAddress(), TmpAddr, RP, *m_pHLModule, + CGF); + return LValue::MakeAddr(TmpAddr, E->getType(), LV.getAlignment(), + CGF.getContext()); +} + CGHLSLRuntime *CodeGen::CreateMSHLSLRuntime(CodeGenModule &CGM) { return new CGMSHLSLRuntime(CGM); } diff --git a/tools/clang/lib/CodeGen/CGHLSLRuntime.h b/tools/clang/lib/CodeGen/CGHLSLRuntime.h index a5450f561e..cb99188377 100644 --- a/tools/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/tools/clang/lib/CodeGen/CGHLSLRuntime.h @@ -46,6 +46,7 @@ class ReturnStmt; class Attr; class VarDecl; class HLSLRootSignatureAttr; +class CastExpr; namespace CodeGen { class CodeGenModule; @@ -78,15 +79,6 @@ class CGHLSLRuntime { llvm::Value *DestPtr) = 0; virtual llvm::Constant *EmitHLSLConstInitListExpr(CodeGenModule &CGM, InitListExpr *E) = 0; - virtual void EmitHLSLOutParamConversionInit( - CodeGenFunction &CGF, const FunctionDecl *FD, const CallExpr *E, - llvm::SmallVector &castArgList, - llvm::SmallVector &argList, - llvm::SmallVector &lifetimeCleanupList, - const std::function &TmpArgMap) = 0; - virtual void EmitHLSLOutParamConversionCopyBack( - CodeGenFunction &CGF, llvm::SmallVector &castArgList, - llvm::SmallVector &lifetimeCleanupList) = 0; virtual void MarkPotentialResourceTemp(CodeGenFunction &CGF, llvm::Value *V, clang::QualType QaulTy) = 0; virtual llvm::Value *EmitHLSLMatrixOperationCall(CodeGenFunction &CGF, const clang::Expr *E, llvm::Type *RetType, @@ -155,6 +147,7 @@ class CGHLSLRuntime { virtual void EmitHLSLMartrixCastForStoreOp(CodeGenFunction& CGF, llvm::SmallVector& IRCallArgs, llvm::SmallVector& ArgTys) = 0; + virtual LValue EmitResourceParamAnnotation(CodeGenFunction& CGF, const CastExpr *E) = 0; }; /// Create an instance of a HLSL runtime class. diff --git a/tools/clang/lib/CodeGen/CodeGenFunction.h b/tools/clang/lib/CodeGen/CodeGenFunction.h index be9c2389ef..bcec980bff 100644 --- a/tools/clang/lib/CodeGen/CodeGenFunction.h +++ b/tools/clang/lib/CodeGen/CodeGenFunction.h @@ -2464,8 +2464,11 @@ class CodeGenFunction : public CodeGenTypeCache { LValue EmitArraySubscriptExpr(const ArraySubscriptExpr *E, bool Accessed = false); LValue EmitExtVectorElementExpr(const ExtVectorElementExpr *E); - LValue EmitExtMatrixElementExpr(const ExtMatrixElementExpr *E); // HLSL Change - LValue EmitHLSLVectorElementExpr(const HLSLVectorElementExpr *E); // HLSL Change + // HLSL Change begin + LValue EmitExtMatrixElementExpr(const ExtMatrixElementExpr *E); + LValue EmitHLSLVectorElementExpr(const HLSLVectorElementExpr *E); + LValue EmitHLSLOutParamExpr(const HLSLOutParamExpr *E); + // HLSL Change end LValue EmitMemberExpr(const MemberExpr *E); LValue EmitObjCIsaExpr(const ObjCIsaExpr *E); LValue EmitCompoundLiteralLValue(const CompoundLiteralExpr *E); diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 3edce78b91..d9e50de937 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -754,7 +754,7 @@ SemanticInfo DeclResultIdMapper::getStageVarSemantic(const NamedDecl *decl) { bool DeclResultIdMapper::createStageOutputVar(const DeclaratorDecl *decl, SpirvInstruction *storedValue, bool forPCF) { - QualType type = getTypeOrFnRetType(decl); + QualType type = getTypeOrFnRetType(decl).getNonReferenceType(); uint32_t arraySize = 0; // Output stream types (PointStream, LineStream, TriangleStream) are @@ -2506,6 +2506,10 @@ bool DeclResultIdMapper::createStageVars( QualType type, uint32_t arraySize, const llvm::StringRef namePrefix, llvm::Optional invocationId, SpirvInstruction **value, bool noWriteBack, SemanticInfo *inheritSemantic) { + + // Look through references for stage variables. + type = type.getNonReferenceType(); + assert(value); // invocationId should only be used for handling HS per-vertex output. if (invocationId.hasValue()) { diff --git a/tools/clang/lib/SPIRV/EmitVisitor.cpp b/tools/clang/lib/SPIRV/EmitVisitor.cpp index 971811ca9a..b76067fc16 100644 --- a/tools/clang/lib/SPIRV/EmitVisitor.cpp +++ b/tools/clang/lib/SPIRV/EmitVisitor.cpp @@ -1262,6 +1262,17 @@ bool EmitVisitor::visit(SpirvCopyObject *inst) { return true; } +bool EmitVisitor::visit(SpirvCopyMemory *inst) { + initInstruction(inst); + curInst.push_back( + getOrAssignResultId(inst->getDestination())); + curInst.push_back(getOrAssignResultId(inst->getSource())); + finalizeInstruction(&mainBinary); + emitDebugNameForInstruction(getOrAssignResultId(inst), + inst->getDebugName()); + return true; +} + bool EmitVisitor::visit(SpirvSampledImage *inst) { initInstruction(inst); curInst.push_back(inst->getResultTypeId()); diff --git a/tools/clang/lib/SPIRV/EmitVisitor.h b/tools/clang/lib/SPIRV/EmitVisitor.h index 04afd3d129..6f0fb3790c 100644 --- a/tools/clang/lib/SPIRV/EmitVisitor.h +++ b/tools/clang/lib/SPIRV/EmitVisitor.h @@ -264,6 +264,7 @@ class EmitVisitor : public Visitor { bool visit(SpirvImageTexelPointer *) override; bool visit(SpirvLoad *) override; bool visit(SpirvCopyObject *) override; + bool visit(SpirvCopyMemory *) override; bool visit(SpirvSampledImage *) override; bool visit(SpirvSelect *) override; bool visit(SpirvSpecConstantBinaryOp *) override; diff --git a/tools/clang/lib/SPIRV/GlPerVertex.cpp b/tools/clang/lib/SPIRV/GlPerVertex.cpp index 09b09236b4..759a491caa 100644 --- a/tools/clang/lib/SPIRV/GlPerVertex.cpp +++ b/tools/clang/lib/SPIRV/GlPerVertex.cpp @@ -112,7 +112,7 @@ llvm::SmallVector GlPerVertex::getStageOutVars() const { bool GlPerVertex::recordGlPerVertexDeclFacts(const DeclaratorDecl *decl, bool asInput) { - const QualType type = getTypeOrFnRetType(decl); + const QualType type = getTypeOrFnRetType(decl).getNonReferenceType(); if (type->isVoidType()) return true; diff --git a/tools/clang/lib/SPIRV/SpirvBuilder.cpp b/tools/clang/lib/SPIRV/SpirvBuilder.cpp index 9f37b059e4..6b14b755dd 100644 --- a/tools/clang/lib/SPIRV/SpirvBuilder.cpp +++ b/tools/clang/lib/SPIRV/SpirvBuilder.cpp @@ -227,6 +227,19 @@ SpirvInstruction *SpirvBuilder::createLoad(QualType resultType, pointer->getAstResultType()->isSignedIntegerOrEnumerationType(), loc); } +SpirvCopyMemory *SpirvBuilder::createCopyMemory(QualType resultType, + SpirvInstruction *src, + SpirvInstruction *dst, + SourceLocation loc) { + auto instruction = new (context) SpirvCopyMemory(resultType, loc, src, dst); + instruction->setStorageClass(dst->getStorageClass()); + instruction->setLayoutRule(dst->getLayoutRule()); + // The result of OpCopyMemory is always an rvalue. + instruction->setRValue(dst->isRValue()); + insertPoint->addInstruction(instruction); + return instruction; +} + SpirvCopyObject *SpirvBuilder::createCopyObject(QualType resultType, SpirvInstruction *pointer, SourceLocation loc) { diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 76657018f4..2642c25f83 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -1132,6 +1132,12 @@ SpirvInstruction *SpirvEmitter::doExpr(const Expr *expr, } else if (const auto *tmplParamExpr = dyn_cast(expr)) { result = doExpr(tmplParamExpr->getReplacement()); + } else if (const auto *outParamExpr = dyn_cast(expr)) { + result = doHLSLOutParamExpr(outParamExpr); + } else if (const auto *arrayTmpExpr = dyn_cast(expr)) { + result = doHLSLArrayTemporaryExpr(arrayTmpExpr); + } else if (const auto *opaqueValExpr = dyn_cast(expr)) { + result = doOpaqueValueExpr(opaqueValExpr); } else { emitError("expression class '%0' unimplemented", expr->getExprLoc()) << expr->getStmtClassName() << expr->getSourceRange(); @@ -2771,6 +2777,7 @@ SpirvInstruction *SpirvEmitter::processCall(const CallExpr *callExpr) { llvm::SmallVector vars; // Variables for function call llvm::SmallVector isTempVar; // Temporary variable or not llvm::SmallVector args; // Evaluated arguments + llvm::SmallVector, 4> writebacks; if (const auto *memberCall = dyn_cast(callExpr)) { const auto *memberFn = cast(memberCall->getCalleeDecl()); @@ -2857,6 +2864,9 @@ SpirvInstruction *SpirvEmitter::processCall(const CallExpr *callExpr) { auto *argInst = doExpr(arg); + if (const auto *outParamExpr = dyn_cast(arg)) + writebacks.push_back(std::make_pair(argInst, outParamExpr)); + bool isArgGlobalVarWithResourceType = argInfo && argInfo->getStorageClass() != spv::StorageClass::Function && isResourceType(paramType); @@ -2946,38 +2956,15 @@ SpirvInstruction *SpirvEmitter::processCall(const CallExpr *callExpr) { callExpr->getSourceRange()); // Go through all parameters and write those marked as out/inout - for (uint32_t i = 0; i < numParams; ++i) { - const auto *param = callee->getParamDecl(i); - const auto paramType = param->getType(); - // If it calls a non-static member function, the object itself is argument - // 0, and therefore all other argument positions are shifted by 1. - const uint32_t index = i + isNonStaticMemberCall; - // Using a resouce as a function parameter is never passed-by-copy. As a - // result, even if the function parameter is marked as 'out' or 'inout', - // there is no reason to copy back the results after the function call into - // the resource. - if (isTempVar[index] && canActAsOutParmVar(param) && - !isResourceType(paramType)) { - // Arguments for the overloaded operator includes the object itself. The - // actual argument starts from the second one. - const uint32_t argIndex = i + isOperatorOverloading; - - const auto *arg = callExpr->getArg(argIndex); - SpirvInstruction *value = - spvBuilder.createLoad(paramType, vars[index], arg->getLocStart()); - - // Now we want to assign 'value' to arg. But first, in rare cases when - // using 'out' or 'inout' where the parameter and argument have a type - // mismatch, we need to first cast 'value' to the type of 'arg' because - // the AST will not include a cast node. - if (!paramTypeMatchesArgType(paramType, arg->getType())) { - if (const auto *refType = paramType->getAs()) - value = castToType(value, refType->getPointeeType(), arg->getType(), - arg->getLocStart()); - } - - processAssignment(arg, value, false, args[index]); + for (auto wb : writebacks) { + SpirvInstruction *ArgResult = nullptr; + if (const auto wbExpr = wb.second->getWriteback()) { + ArgResult = doExpr(wbExpr); + } else { + ArgResult = spvBuilder.createLoad( + wb.second->getType(), wb.first, wb.second->getLocStart()); } + processAssignment(wb.second->getSrcLV(), ArgResult, false); } return retVal; @@ -3331,15 +3318,7 @@ SpirvInstruction *SpirvEmitter::doCastExpr(const CastExpr *expr, subExpr->getExprLoc(), range); } case CastKind::CK_ArrayToPointerDecay: { - // Literal string to const string conversion falls under this category. - if (hlsl::IsStringLiteralType(subExprType) && hlsl::IsStringType(toType)) { - return doExpr(subExpr, range); - } else { - emitError("implicit cast kind '%0' unimplemented", expr->getExprLoc()) - << expr->getCastKindName() << expr->getSourceRange(); - expr->dump(); - return 0; - } + return doExpr(subExpr, range); } default: emitError("implicit cast kind '%0' unimplemented", expr->getExprLoc()) @@ -7850,7 +7829,8 @@ const Expr *SpirvEmitter::collectArrayStructIndices( // `-ImplicitCastExpr 'const T' lvalue // `-ArraySubscriptExpr 'ConstantBuffer':'ConstantBuffer' lvalue if (auto *castExpr = dyn_cast(expr)) { - if (castExpr->getCastKind() == CK_FlatConversion) { + if (castExpr->getCastKind() == CK_FlatConversion || + castExpr->getCastKind() == CK_ArrayToPointerDecay) { const auto *subExpr = castExpr->getSubExpr(); const QualType subExprType = subExpr->getType(); if (isConstantTextureBuffer(subExprType)) { @@ -11283,8 +11263,8 @@ void SpirvEmitter::processCallShader(const CallExpr *callExpr) { // HLSL Func : // template // void CallShader(in int sbtIndex, inout CallData arg) - if (const auto *implCastExpr = dyn_cast(args[1])) { - if (const auto *arg = dyn_cast(implCastExpr->getSubExpr())) { + if (const auto *outParamExpr = dyn_cast(args[1])) { + if (const auto *arg = dyn_cast(outParamExpr->getBase())) { if (const auto *varDecl = dyn_cast(arg->getDecl())) { callDataType = varDecl->getType(); callDataArg = varDecl; @@ -11369,8 +11349,8 @@ void SpirvEmitter::processTraceRay(const CallExpr *callExpr) { // inout RayPayload p) // where RayDesc = {float3 origin, float tMin, float3 direction, float tMax} - if (const auto *implCastExpr = dyn_cast(args[7])) { - if (const auto *arg = dyn_cast(implCastExpr->getSubExpr())) { + if (const auto *outExpr = dyn_cast(args[7])) { + if (const auto *arg = dyn_cast(outExpr->getBase())) { if (const auto *varDecl = dyn_cast(arg->getDecl())) { rayPayloadType = varDecl->getType(); rayPayloadArg = varDecl; @@ -12298,6 +12278,7 @@ bool SpirvEmitter::emitEntryFunctionWrapperForRayTracing( bool SpirvEmitter::processMeshOrAmplificationShaderAttributes( const FunctionDecl *decl, uint32_t *outVerticesArraySize) { + // TODO: All the `emitError` calls here should be moved to AST-based analysis. if (auto *numThreadsAttr = decl->getAttr()) { uint32_t x, y, z; x = static_cast(numThreadsAttr->getX()); @@ -12337,7 +12318,7 @@ bool SpirvEmitter::processMeshOrAmplificationShaderAttributes( for (uint32_t i = 0; i < decl->getNumParams(); i++) { const auto param = decl->getParamDecl(i); - const auto paramType = param->getType(); + const auto paramType = param->getType().getNonReferenceType(); const auto paramLoc = param->getLocation(); if (param->hasAttr() || param->hasAttr() || @@ -12602,7 +12583,7 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, // Create temporary variables for holding function call arguments llvm::SmallVector params; for (const auto *param : decl->params()) { - const auto paramType = param->getType(); + const auto paramType = param->getType().getNonReferenceType(); std::string tempVarName = "param.var." + param->getNameAsString(); auto *tempVar = spvBuilder.addFnVar(paramType, param->getLocation(), tempVarName, @@ -12684,8 +12665,9 @@ bool SpirvEmitter::emitEntryFunctionWrapper(const FunctionDecl *decl, // .Append() intrinsic method. No need to load the parameter since we // won't need to write back here. if (param->isUsed() && !spvContext.isGS()) - loadedParam = spvBuilder.createLoad(param->getType(), params[i], - param->getLocStart()); + loadedParam = + spvBuilder.createLoad(param->getType().getNonReferenceType(), + params[i], param->getLocStart()); if (!declIdMapper.createStageOutputVar(param, loadedParam, false)) return false; @@ -13883,5 +13865,56 @@ SpirvEmitter::doUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *expr) { return sizeConst; } +SpirvInstruction * +SpirvEmitter::doHLSLOutParamExpr(const HLSLOutParamExpr *Expr) { + if (Expr->canElide()) + return doExpr(Expr->getBase()); + SpirvVariable *TmpVar = nullptr; + if (Expr->isInOut()) { + SpirvInstruction *InitVal = doExpr(Expr->getBase()); + if (!InitVal->isRValue()) + InitVal = + spvBuilder.createLoad(Expr->getType(), InitVal, Expr->getLocStart()); + + TmpVar = createTemporaryVar(Expr->getType(), "hlsl.inout", InitVal, + Expr->getLocStart()); + } else { + TmpVar = + spvBuilder.addFnVar(Expr->getType(), Expr->getLocStart(), "hlsl.out"); + } + + if (const auto *OpaqueVal = Expr->getOpaqueValue()) + bindOpaqueValue(TmpVar, OpaqueVal); + + return TmpVar; +} + +SpirvInstruction * +SpirvEmitter::doHLSLArrayTemporaryExpr(const HLSLArrayTemporaryExpr *expr) { + auto *InitVal = doExpr(expr->getBase()); + auto *TmpVar = spvBuilder.addFnVar(expr->getType(), expr->getLocStart(), "tmp.hlsl.array"); + (void)spvBuilder.createCopyMemory(TmpVar->getAstResultType(), InitVal, + TmpVar, expr->getLocStart()); + return TmpVar; +} + +SpirvInstruction *SpirvEmitter::doOpaqueValueExpr(const OpaqueValueExpr *expr) { + return getLValueForOpaqueValue(expr); +} + +void SpirvEmitter::bindOpaqueValue(SpirvVariable *lvalue, + const OpaqueValueExpr *opaqueVal) { + assert(opaqueValueBindings.count(opaqueVal) == 0 && + "Opaque values cannot map to multiple lvalues."); + opaqueValueBindings.insert(std::make_pair(opaqueVal, lvalue)); +} + +SpirvVariable * +SpirvEmitter::getLValueForOpaqueValue(const OpaqueValueExpr *opaqueVal) { + assert(opaqueValueBindings.count(opaqueVal) == 1 && + "Looking for an unbound opaque value."); + return opaqueValueBindings.find(opaqueVal)->second; +} + } // end namespace spirv } // end namespace clang diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.h b/tools/clang/lib/SPIRV/SpirvEmitter.h index 0513d9381d..5c67feaca5 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.h +++ b/tools/clang/lib/SPIRV/SpirvEmitter.h @@ -139,6 +139,18 @@ class SpirvEmitter : public ASTConsumer { SpirvInstruction *doUnaryOperator(const UnaryOperator *expr); SpirvInstruction * doUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *expr); + + SpirvInstruction *doHLSLOutParamExpr(const HLSLOutParamExpr *expr); + SpirvInstruction * + doHLSLArrayTemporaryExpr(const HLSLArrayTemporaryExpr *expr); + SpirvInstruction *doOpaqueValueExpr(const OpaqueValueExpr *expr); + + + /// Binds an lvalue to an opaque value expression. + void bindOpaqueValue(SpirvVariable *lvalue, const OpaqueValueExpr *opaqueVal); + + /// Gets the lvalue for an opaque value. + SpirvVariable *getLValueForOpaqueValue(const OpaqueValueExpr *opaqueVal); /// Overload with pre computed SpirvEvalInfo. /// @@ -1325,6 +1337,11 @@ class SpirvEmitter : public ASTConsumer { /// The of the OpString containing the main source file's path. SpirvString *mainSourceFile; + + /// Stores the mapping of OpaqueValueExprs to SpirvVariables. + /// + /// Note: 16 is an arbitrarily chosen value. + llvm::DenseMap opaqueValueBindings; }; void SpirvEmitter::doDeclStmt(const DeclStmt *declStmt) { diff --git a/tools/clang/lib/SPIRV/SpirvInstruction.cpp b/tools/clang/lib/SPIRV/SpirvInstruction.cpp index e20385b7c5..d6b2ad5925 100644 --- a/tools/clang/lib/SPIRV/SpirvInstruction.cpp +++ b/tools/clang/lib/SPIRV/SpirvInstruction.cpp @@ -73,6 +73,7 @@ DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvImageSparseTexelsResident) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvImageTexelPointer) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvLoad) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvCopyObject) +DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvCopyMemory) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvSampledImage) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvSelect) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvSpecConstantBinaryOp) @@ -797,6 +798,11 @@ SpirvCopyObject::SpirvCopyObject(QualType resultType, SourceLocation loc, : SpirvInstruction(IK_CopyObject, spv::Op::OpCopyObject, resultType, loc), pointer(pointerInst) {} +SpirvCopyMemory::SpirvCopyMemory(QualType resultType, SourceLocation loc, + SpirvInstruction *src, SpirvInstruction *dst) + : SpirvInstruction(IK_CopyMemory, spv::Op::OpCopyMemory, resultType, loc), + source(src), destination(dst) {} + SpirvSampledImage::SpirvSampledImage(QualType resultType, SourceLocation loc, SpirvInstruction *imageInst, SpirvInstruction *samplerInst, diff --git a/tools/clang/lib/Sema/HLSLOutParamBuilder.h b/tools/clang/lib/Sema/HLSLOutParamBuilder.h new file mode 100644 index 0000000000..fd75506df3 --- /dev/null +++ b/tools/clang/lib/Sema/HLSLOutParamBuilder.h @@ -0,0 +1,141 @@ +//===--- HLSLOutParamBuilder.h - Helper for HLSLOutParamExpr ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_SEMA_HLSLOUTPARAMBUILDER_H +#define CLANG_SEMA_HLSLOUTPARAMBUILDER_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaHLSL.h" +#include "llvm/ADT/DenseSet.h" + +namespace clang { + +class HLSLOutParamBuilder { + llvm::DenseSet SeenVars; + + // not copyable + HLSLOutParamBuilder(const HLSLOutParamBuilder &) = delete; + HLSLOutParamBuilder &operator=(const HLSLOutParamBuilder &) = delete; + + class DeclFinder : public StmtVisitor { + public: + VarDecl *Decl = nullptr; + + // TODO: For correctness, when multiple decls are found all decls should be + // added to the Seen list. + bool MultipleFound = false; + + DeclFinder() = default; + + void VisitStmt(Stmt *S) { + for (Stmt *Child : S->children()) + if (Child) + Visit(Child); + } + + void VisitDeclRefExpr(DeclRefExpr *DRE) { + if (MultipleFound) + return; + if (Decl) + MultipleFound = true; + Decl = dyn_cast(DRE->getFoundDecl()); + return; + } + }; + + ExprResult CreateCasted(Sema &Sema, ParmVarDecl *P, Expr *Base, QualType Ty) { + ASTContext &Ctx = Sema.getASTContext(); + ExprResult Res = + Sema.PerformImplicitConversion(Base, Ty, Sema::AA_Passing); + if (Res.isInvalid()) + return ExprError(); + // After the cast, drop the reference type when creating the exprs. + Ty = Ty.getNonLValueExprType(Ctx); + HLSLOutParamExpr *OutExpr = + HLSLOutParamExpr::Create(Ctx, Ty, Res.get(), + P->hasAttr()); + auto *OpV = new (Ctx) OpaqueValueExpr(P->getLocStart(), Ty, VK_LValue, + OK_Ordinary, OutExpr); + Res = Sema.PerformImplicitConversion(OpV, Base->getType(), + Sema::AA_Passing); + if (Res.isInvalid()) + return ExprError(); + OutExpr->setWriteback(Res.get()); + OutExpr->setSrcLV(Base); + OutExpr->setOpaqueValue(OpV); + OpV->setSourceIsParent(); + return ExprResult(OutExpr); + } + +public: + HLSLOutParamBuilder() = default; + + ExprResult Create(Sema &Sema, ParmVarDecl *P, Expr *Base) { + ASTContext &Ctx = Sema.getASTContext(); + + QualType Ty = P->getType().getNonLValueExprType(Ctx); + + if(hlsl::IsHLSLVecMatType(Base->getType()) && Ty->isScalarType()) { + Sema.Diag(Base->getLocStart(), diag::err_hlsl_unsupported_lvalue_cast_op); + return ExprError(); + } + + bool PossibleFlatConv = + hlsl::IsConversionToLessOrEqualElements(&Sema, Base, Ty, true); + bool RequiresConversion = Ty.getUnqualifiedType() != Base->getType().getUnqualifiedType(); + + // If the unqualified types mismatch we may have some casting. Since this + // results in a copy we can ignore qualifiers. + if (!PossibleFlatConv && RequiresConversion) + return CreateCasted(Sema, P, Base, Ty); + + DeclFinder DF; + DF.Visit(Base); + + // If the analysis returned multiple possible decls, or no decl, or we've + // seen the decl before, generate a HLSLOutParamExpr that can't be elided. + // Note: The DXIL IR passes are fragile to a bunch of cases when vectors are + // retained as parameter types to user-defined function calls. To work + // around that we always emit copies for vector arguments. This is not a + // regression because the HLParameterLegalization pass used to do the same + // thing. + if (DF.MultipleFound || DF.Decl == nullptr || + DF.Decl->getType().getQualifiers().hasAddressSpace() || + hlsl::IsHLSLVecType(Ty) || // This is a hack, see note above. + SeenVars.count(DF.Decl) > 0 || !DF.Decl->hasLocalStorage()) { + if (RequiresConversion) + return CreateCasted(Sema, P, Base, P->getType()); + return ExprResult( + HLSLOutParamExpr::Create(Ctx, Ty, Base, P->hasAttr())); + } + + if (RequiresConversion) { + ExprResult Res = + Sema.PerformImplicitConversion(Base, P->getType(), Sema::AA_Passing); + if (Res.isInvalid()) + return ExprError(); + Base = Res.get(); + } + // Add the decl to the seen list, and generate a HLSLOutParamExpr that can + // be elided. + SeenVars.insert(DF.Decl); + return ExprResult(HLSLOutParamExpr::Create( + Ctx, Ty, Base, P->hasAttr(), true)); + } +}; + +} // namespace clang + +#endif // CLANG_SEMA_HLSLOUTPARAMBUILDER_H diff --git a/tools/clang/lib/Sema/Sema.cpp b/tools/clang/lib/Sema/Sema.cpp index ab2d023dbb..4af5e994b7 100644 --- a/tools/clang/lib/Sema/Sema.cpp +++ b/tools/clang/lib/Sema/Sema.cpp @@ -360,13 +360,6 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, assert((VK == VK_RValue || !E->isRValue()) && "can't cast rvalue to lvalue"); #endif - if (VK == VK_LValue) { - if (Kind == CastKind::CK_HLSLVectorTruncationCast || - Kind == CastKind::CK_HLSLMatrixTruncationCast) { - Diag(E->getLocStart(), diag::err_hlsl_unsupported_lvalue_cast_op); - } - } - // Check whether we're implicitly casting from a nullable type to a nonnull // type. if (auto exprNullability = E->getType()->getNullability(Context)) { diff --git a/tools/clang/lib/Sema/SemaCast.cpp b/tools/clang/lib/Sema/SemaCast.cpp index 10668dc388..6a3c6d2673 100644 --- a/tools/clang/lib/Sema/SemaCast.cpp +++ b/tools/clang/lib/Sema/SemaCast.cpp @@ -80,6 +80,9 @@ namespace { void CheckReinterpretCast(); void CheckStaticCast(); void CheckDynamicCast(); + // HLSL Change begin + void CheckHLSLCStyleCast(bool FunctionalCast, bool ListInitialization); + // HLSL Change end void CheckCXXCStyleCast(bool FunctionalCast, bool ListInitialization); void CheckCStyleCast(); @@ -2058,7 +2061,65 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, // So we finish by allowing everything that remains - it's got to be two // object pointers. return TC_Success; -} +} + +// HLSL Change Begin +void CastOperation::CheckHLSLCStyleCast(bool FunctionalStyle, + bool ListInitialization) { + if (DestType->isDependentType() || SrcExpr.get()->isTypeDependent() || + SrcExpr.get()->isValueDependent()) { + assert(Kind == CK_Dependent); + return; + } + + QualType SrcType = SrcExpr.get()->getType(); + if (SrcType.getCanonicalType() == DestType.getCanonicalType()) { + ValueKind = VK_LValue; + Kind = CK_NoOp; + ResultType = DestType; + return; + } + if (isa(*DestType) && isa(*SrcType)) { + CXXBasePaths Paths; + if (Self.IsDerivedFrom(SrcType, DestType, Paths)) { + ResultType = DestType; + if (SrcType.getQualifiers().hasAddressSpace()) { + Qualifiers Q = DestType.getQualifiers(); + Q.addAddressSpace(SrcType.getQualifiers().getAddressSpace()); + ResultType = Self.getASTContext().getQualifiedType(DestType, Q); + } + Self.BuildBasePathArray(Paths, BasePath); + ValueKind = VK_LValue; + Kind = CK_HLSLDerivedToBase; + return; + } + } + + bool PossibleFlatConv = + hlsl::IsConversionToLessOrEqualElements(&Self, SrcExpr, DestType, true); + + // Check for HLSL vector/matrix/array/struct shrinking. + if (ValueKind == VK_RValue && + !FunctionalStyle && + !isPlaceholder(BuiltinType::Overload) && + SrcExpr.get()->isLValue() && + // Cannot use casts on basic type l-values + !SrcType.getCanonicalType()->isBuiltinType() && + PossibleFlatConv) { + ValueKind = VK_LValue; + DestType = Self.getASTContext().getLValueReferenceType(DestType); + } + + // If the source is an Array and this is a possible flat conversion we should + // handle it to prevent array to pointer decay. + if (SrcType->isArrayType() && PossibleFlatConv) { + Kind = clang::CK_FlatConversion; + return; + } + + CheckCXXCStyleCast(FunctionalStyle, ListInitialization); +} +// HLSL Change End void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, bool ListInitialization) { @@ -2103,20 +2164,6 @@ void CastOperation::CheckCXXCStyleCast(bool FunctionalStyle, return; } - // HLSL Change Starts - // Check for HLSL vector/matrix/array/struct shrinking. - if (ValueKind == VK_RValue && - !FunctionalStyle && - !isPlaceholder(BuiltinType::Overload) && - Self.getLangOpts().HLSL && - SrcExpr.get()->isLValue() && - // Cannot use casts on basic type l-values - !SrcExpr.get()->getType().getCanonicalType()->isBuiltinType() && - hlsl::IsConversionToLessOrEqualElements(&Self, SrcExpr, DestType, true)) { - ValueKind = VK_LValue; - } - // HLSL Change Ends - if (ValueKind == VK_RValue && !DestType->isRecordType() && !isPlaceholder(BuiltinType::Overload)) { SrcExpr = Self.DefaultFunctionArrayLvalueConversion(SrcExpr.get()); @@ -2477,16 +2524,21 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc, TypeSourceInfo *CastTypeInfo, SourceLocation RPLoc, Expr *CastExpr) { - CastOperation Op(*this, CastTypeInfo->getType(), CastExpr); + CastOperation Op(*this, CastTypeInfo->getType(), CastExpr); Op.DestRange = CastTypeInfo->getTypeLoc().getSourceRange(); Op.OpRange = SourceRange(LPLoc, CastExpr->getLocEnd()); - if (getLangOpts().CPlusPlus) { + // HLSL Change begin + if (getLangOpts().HLSL) { + Op.CheckHLSLCStyleCast(/*FunctionalStyle=*/ false, + isa(CastExpr)); + } else if (getLangOpts().CPlusPlus) { Op.CheckCXXCStyleCast(/*FunctionalStyle=*/ false, isa(CastExpr)); } else { Op.CheckCStyleCast(); } + // HLSL Change if (Op.SrcExpr.isInvalid()) return ExprError(); diff --git a/tools/clang/lib/Sema/SemaDXR.cpp b/tools/clang/lib/Sema/SemaDXR.cpp index 89fa578ba8..1cf9389105 100644 --- a/tools/clang/lib/Sema/SemaDXR.cpp +++ b/tools/clang/lib/Sema/SemaDXR.cpp @@ -319,7 +319,7 @@ GetAllReadsReachedFromEntry(CFG &ShaderCFG, ArrayRef PayloadReads) { // Returns the record type of a payload declaration. CXXRecordDecl *GetPayloadType(const VarDecl *Payload) { - auto PayloadType = Payload->getType(); + QualType PayloadType = Payload->getType().getNonReferenceType(); if (PayloadType->isStructureOrClassType()) { return PayloadType->getAsCXXRecordDecl(); } @@ -1010,6 +1010,9 @@ DiagnosePayloadAccess(Sema &S, DxrShaderDiagnoseInfo &Info, const Stmt *IgnoreParensAndDecay(const Stmt *S) { for (;;) { switch (S->getStmtClass()) { + case Expr::HLSLOutParamExprClass: + S = cast(S)->getBase(); + break; case Stmt::ParenExprClass: S = cast(S)->getSubExpr(); break; @@ -1069,7 +1072,8 @@ bool DiagnosePayloadParameter(Sema &S, ParmVarDecl *Payload, FunctionDecl *FD, return false; } - CXXRecordDecl *Decl = Payload->getType()->getAsCXXRecordDecl(); + CXXRecordDecl *Decl = + Payload->getType().getNonReferenceType()->getAsCXXRecordDecl(); if (!Decl || Decl->isImplicit()) { // error: not a user defined type decl return false; diff --git a/tools/clang/lib/Sema/SemaDecl.cpp b/tools/clang/lib/Sema/SemaDecl.cpp index 1d287503a6..af35865ae9 100644 --- a/tools/clang/lib/Sema/SemaDecl.cpp +++ b/tools/clang/lib/Sema/SemaDecl.cpp @@ -8399,6 +8399,34 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, Diag(NewFD->getLocation(), diag::warn_return_value_udt) << NewFD << R; } } + + // HLSL Change begin + // This is a hack... it forces that implicit types used as parameters are + // always completed. This is needed because some diagnostics that should be + // emitted in Sema (probably here-ish), are instead implemented in CodeGen + // (see: CGHLSLMS.cpp). This requires that we have a valid enough AST to + // CodeGen even to just get correct diagnostics emitted. See the test: + // + // test/HLSLFileCheck/shader_targets/raytracing/builtin-ray-types-anyhit.hlsl + if (getLangOpts().HLSL) { + for (auto Param : NewFD->params()) { + QualType PT = Param->getType().getNonReferenceType(); + if (!PT->isRecordType()) + continue; + if (const RecordType *RT = PT->getAs()) { + RecordDecl *RD = RT->getDecl(); + // The diagnostic here doesn't really matter because it should never be + // emitted. + if (RD->isImplicit() && + RequireCompleteType(Param->getLocStart(), PT, + diag::err_call_incomplete_argument)) { + llvm_unreachable("Implicit types should never fail to complete."); + NewFD->setInvalidDecl(); + } + } + } + } + // HLSL Change end return Redeclaration; } diff --git a/tools/clang/lib/Sema/SemaExpr.cpp b/tools/clang/lib/Sema/SemaExpr.cpp index 370c5efcd5..84ffbd257c 100644 --- a/tools/clang/lib/Sema/SemaExpr.cpp +++ b/tools/clang/lib/Sema/SemaExpr.cpp @@ -45,6 +45,7 @@ #include "clang/Sema/Template.h" #include "llvm/Support/ConvertUTF.h" #include "clang/Sema/SemaHLSL.h" // HLSL Change +#include "HLSLOutParamBuilder.h" // HLSL Change using namespace clang; using namespace sema; @@ -515,7 +516,7 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E) { } E = ImpCastExprToType(E, Context.getPointerType(Ty), CK_FunctionToPointerDecay).get(); - } else if (Ty->isArrayType() && !getLangOpts().HLSL) { // HLSL Change - HLSL does not have pointers; do not decay arrays + } else if (Ty->isArrayType()) { // In C90 mode, arrays only promote to pointers if the array expression is // an lvalue. The relevant legalese is C90 6.2.2.1p3: "an lvalue that has // type 'array of type' is converted to an expression that has type 'pointer @@ -4277,27 +4278,15 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, // wasn't promoted because of the C90 rule that doesn't // allow promoting non-lvalue arrays. Warn, then // force the promotion here. - // HLSL Change Starts - arrays won't decay - if (getLangOpts().HLSL) { - BaseExpr = LHSExp; - IndexExpr = RHSExp; - ResultType = LHSTy->getAsArrayTypeUnsafe()->getElementType(); - // We need to make sure to preserve qualifiers on array types, since these - // are in effect references. - if (LHSTy.hasQualifiers()) - ResultType.setLocalFastQualifiers(LHSTy.getQualifiers().getFastQualifiers()); - } else { - // HLSL Change Ends - Diag(LHSExp->getLocStart(), diag::ext_subscript_non_lvalue) << - LHSExp->getSourceRange(); - LHSExp = ImpCastExprToType(LHSExp, Context.getArrayDecayedType(LHSTy), - CK_ArrayToPointerDecay).get(); - LHSTy = LHSExp->getType(); - - BaseExpr = LHSExp; - IndexExpr = RHSExp; - ResultType = LHSTy->getAs()->getPointeeType(); - } // HLSL Change - end else block + Diag(LHSExp->getLocStart(), diag::ext_subscript_non_lvalue) << + LHSExp->getSourceRange(); + LHSExp = ImpCastExprToType(LHSExp, Context.getArrayDecayedType(LHSTy), + CK_ArrayToPointerDecay).get(); + LHSTy = LHSExp->getType(); + + BaseExpr = LHSExp; + IndexExpr = RHSExp; + ResultType = LHSTy->getAs()->getPointeeType(); } else if (RHSTy->isArrayType()) { // Same as previous, except for 123[f().a] case Diag(RHSExp->getLocStart(), diag::ext_subscript_non_lvalue) << @@ -4667,6 +4656,7 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl, unsigned NumParams = Proto->getNumParams(); bool Invalid = false; unsigned ArgIx = 0; + HLSLOutParamBuilder HLSLBuilder; // HLSL Change // Continue to check argument types (even if we have too few/many args). for (unsigned i = FirstParam; i < NumParams; i++) { QualType ProtoArgType = Proto->getParamType(i); @@ -4703,12 +4693,27 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, FunctionDecl *FDecl, if (CFAudited) Entity.setParameterCFAudited(); - ExprResult ArgE = PerformCopyInitialization( - Entity, SourceLocation(), Arg, IsListInitialization, AllowExplicit); - if (ArgE.isInvalid()) - return true; + // HLSL Change begin + // If this is a constant sized array and not an oputput, generate an array + // temporary expression here rather than an RValue cast. + if (ProtoArgType->isConstantArrayType() && !Param->isModifierOut()) + Arg = HLSLArrayTemporaryExpr::Create(getASTContext(), Arg); + // HLSL Change end - Arg = ArgE.getAs(); + // HLSL Change Begin - Optimize out parameters. + if (Param->isModifierOut()) { + ExprResult ArgE = HLSLBuilder.Create(*this, Param, Arg); + if (ArgE.isInvalid()) + return true; // TODO: Emit an error! + Arg = ArgE.getAs(); + } else { + ExprResult ArgE = PerformCopyInitialization( + Entity, SourceLocation(), Arg, IsListInitialization, AllowExplicit); + if (ArgE.isInvalid()) + return true; + Arg = ArgE.getAs(); + } + // HLSL Change End - Optimize out paramters. } else { assert(Param && "can't use default arguments without a known callee"); diff --git a/tools/clang/lib/Sema/SemaExprCXX.cpp b/tools/clang/lib/Sema/SemaExprCXX.cpp index 42887ca866..639915aa9e 100644 --- a/tools/clang/lib/Sema/SemaExprCXX.cpp +++ b/tools/clang/lib/Sema/SemaExprCXX.cpp @@ -3447,15 +3447,23 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, } // HLSL Change Starts + case ICK_HLSLVector_Truncation: + if(CCK == CCK_ImplicitConversion) + Diag(From->getLocStart(), diag::warn_hlsl_implicit_vector_truncation); + LLVM_FALLTHROUGH; case ICK_Flat_Conversion: case ICK_HLSL_Derived_To_Base: case ICK_HLSLVector_Splat: case ICK_HLSLVector_Scalar: - case ICK_HLSLVector_Truncation: - case ICK_HLSLVector_Conversion: - From = hlsl::PerformHLSLConversion(this, From, ToType.getUnqualifiedType(), SCS, CCK).get(); + case ICK_HLSLVector_Conversion: { + ExprResult FromRes = + hlsl::PerformHLSLConversion(this, From, ToType, SCS, CCK); + if (FromRes.isInvalid()) + return ExprError(); + From = FromRes.get(); break; - // HLSL Change Ends + } + // HLSL Change Ends case ICK_TransparentUnionConversion: { ExprResult FromRes = From; diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 19cb805492..14cec578a0 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -1935,12 +1935,7 @@ FunctionDecl *AddHLSLIntrinsicFunction( if (paramMods[i - 1].isAnyOut() || paramMods[i - 1].GetKind() == hlsl::ParameterModifier::Kind::Ref) { QualType Ty = functionArgQualTypes[i]; - // Aggregate type will be indirect param convert to pointer type. - // Don't need add reference for it. - if ((!Ty->isArrayType() && !Ty->isRecordType()) || - hlsl::IsHLSLVecMatType(Ty)) { - functionArgQualTypes[i] = context.getLValueReferenceType(Ty); - } + functionArgQualTypes[i] = context.getLValueReferenceType(Ty); } } @@ -6887,6 +6882,7 @@ unsigned HLSLExternalSource::GetNumConvertCheckElts(QualType leftType, } QualType HLSLExternalSource::GetNthElementType(QualType type, unsigned index) { + type = type.getNonReferenceType(); if (type.isNull()) { return type; } @@ -8386,7 +8382,10 @@ clang::ExprResult HLSLExternalSource::PerformHLSLConversion( From->getSourceRange(), &BasePath, /*IgnoreAccess=*/true)) return ExprError(); - From = m_sema->ImpCastExprToType(From, targetType.getUnqualifiedType(), CK_HLSLDerivedToBase, From->getValueKind(), &BasePath, CCK).get(); + From = m_sema + ->ImpCastExprToType(From, targetType, CK_HLSLDerivedToBase, + From->getValueKind(), &BasePath, CCK) + .get(); break; } case ICK_HLSLVector_Splat: { @@ -8893,22 +8892,22 @@ bool HLSLExternalSource::CanConvert( if (source->isFunctionType()) return false; + // We cannot convert from non-lvalue to lvalue references. + if (target->isLValueReferenceType() && !sourceExpr->isLValue()) + return false; + // Convert to an r-value to begin with, with an exception for strings // since they are not first-class values and we want to preserve them as literals. bool needsLValueToRValue = sourceExpr->isLValue() && !target->isLValueReferenceType() && sourceExpr->getStmtClass() != Expr::StringLiteralClass; - bool targetRef = target->isReferenceType(); bool TargetIsAnonymous = false; // Initialize the output standard sequence if available. if (standard != nullptr) { // Set up a no-op conversion, other than lvalue to rvalue - HLSL does not support references. standard->setAsIdentityConversion(); - if (needsLValueToRValue) { - standard->First = ICK_Lvalue_To_Rvalue; - } - + standard->First = clang::ICK_Identity; standard->setFromType(source); standard->setAllToTypes(target); } @@ -8955,8 +8954,12 @@ bool HLSLExternalSource::CanConvert( if ((SourceInfo.EltKind == AR_OBJECT_CONSTANT_BUFFER || SourceInfo.EltKind == AR_OBJECT_TEXTURE_BUFFER) && TargetInfo.ShapeKind == AR_TOBJ_COMPOUND) { - if (standard) + if (standard) { standard->Second = ICK_Flat_Conversion; + if (needsLValueToRValue) { + standard->First = ICK_Lvalue_To_Rvalue; + } + } return hlsl::GetHLSLResourceResultType(source) == target; } @@ -9079,13 +9082,7 @@ bool HLSLExternalSource::CanConvert( if (sourceExpr->isLValue()) { if (needsLValueToRValue) { - // We don't need LValueToRValue cast before casting a derived object - // to its base. - if (Second == ICK_HLSL_Derived_To_Base) { - standard->First = ICK_Identity; - } else { - standard->First = ICK_Lvalue_To_Rvalue; - } + standard->First = ICK_Lvalue_To_Rvalue; } else { switch (Second) { @@ -9094,8 +9091,8 @@ bool HLSLExternalSource::CanConvert( case ICK_Vector_Splat: DXASSERT(false, "We shouldn't be producing these implicit conversion kinds"); break; - case ICK_Flat_Conversion: case ICK_HLSLVector_Splat: + case ICK_HLSLVector_Truncation: standard->First = ICK_Lvalue_To_Rvalue; break; default: @@ -9141,13 +9138,6 @@ bool HLSLExternalSource::CanConvert( standard->Second = Second; standard->ComponentConversion = ComponentConversion; - - // For conversion which change to RValue but targeting reference type - // Hold the conversion to codeGen - if (targetRef && standard->First == ICK_Lvalue_To_Rvalue) { - standard->First = ICK_Identity; - standard->Second = ICK_Identity; - } } AssignOpt(Remarks, remarks); @@ -10336,11 +10326,6 @@ bool HLSLExternalSource::ValidateCast( m_sema->Diag(OpLoc, diag::warn_hlsl_narrowing) << source << target; } } - - if ((remarks & TYPE_CONVERSION_ELT_TRUNCATION) != 0) - { - m_sema->Diag(OpLoc, diag::warn_hlsl_implicit_vector_truncation); - } } } diff --git a/tools/clang/lib/Sema/SemaOverload.cpp b/tools/clang/lib/Sema/SemaOverload.cpp index 7ed4bd0746..2c03785717 100644 --- a/tools/clang/lib/Sema/SemaOverload.cpp +++ b/tools/clang/lib/Sema/SemaOverload.cpp @@ -30,6 +30,7 @@ #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/SemaHLSL.h" // HLSL Change #include "clang/AST/HlslTypes.h" // HLSL Change +#include "HLSLOutParamBuilder.h" // HLSL Change #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallPtrSet.h" @@ -12255,13 +12256,20 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, TheCall->setArg(0, Object.get()); // Check the argument types. + HLSLOutParamBuilder HLSLBuilder; // HLSL Change for (unsigned i = 0; i != NumParams; i++) { Expr *Arg; if (i < Args.size()) { Arg = Args[i]; // Pass the argument. - + // HLSL Change begin - Convert Array parameters. + // If this is an array and not an oputput, generate an array temporary + // expression here rather than an RValue cast. + ParmVarDecl *Param = Method->getParamDecl(i); + if (Arg->getType()->isArrayType() && !Param->isModifierOut()) + Arg = HLSLArrayTemporaryExpr::Create(getASTContext(), Arg); + // HLSL Change end - Convert Array parameters. ExprResult InputInit = PerformCopyInitialization(InitializedEntity::InitializeParameter( Context, @@ -12270,6 +12278,13 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Obj, IsError |= InputInit.isInvalid(); Arg = InputInit.getAs(); + // HLSL Change Begin + if (!IsError && Param->isModifierOut()) { + ExprResult ArgE = HLSLBuilder.Create(*this, Param, Arg); + IsError |= ArgE.isInvalid(); + Arg = ArgE.getAs(); + } + // HLSL Change End } else { ExprResult DefArg = BuildCXXDefaultArgExpr(LParenLoc, Method, Method->getParamDecl(i)); diff --git a/tools/clang/lib/Sema/TreeTransform.h b/tools/clang/lib/Sema/TreeTransform.h index 5a91b60933..e3548ef750 100644 --- a/tools/clang/lib/Sema/TreeTransform.h +++ b/tools/clang/lib/Sema/TreeTransform.h @@ -8193,6 +8193,33 @@ TreeTransform::TransformHLSLVectorElementExpr(HLSLVectorElementExpr *E) E->getAccessorLoc(), E->getAccessor()); } + +template +ExprResult +TreeTransform::TransformHLSLOutParamExpr(HLSLOutParamExpr *E) { + ExprResult Base = getDerived().TransformExpr(E->getBase()); + if (Base.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && Base.get() == E->getBase()) + return E; + + return HLSLOutParamExpr::Create(getSema().Context, E->getType(), Base.get(), + E->isInOut(), E->canElide()); +} + +template +ExprResult TreeTransform::TransformHLSLArrayTemporaryExpr( + HLSLArrayTemporaryExpr *E) { + ExprResult Base = getDerived().TransformExpr(E->getBase()); + if (Base.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && Base.get() == E->getBase()) + return E; + + return HLSLArrayTemporaryExpr::Create(getSema().Context, Base.get()); +} // HLSL Change Ends template diff --git a/tools/clang/test/HLSL/inout_trunc.hlsl b/tools/clang/test/HLSL/inout_trunc.hlsl new file mode 100644 index 0000000000..45e6a981e7 --- /dev/null +++ b/tools/clang/test/HLSL/inout_trunc.hlsl @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -ffreestanding -verify %s + +// Test that no crashes result when a scalar is provided to an outvar +// and that the new warning is produced. + +// CHK_NOCRASH: NocrashMain + +float val1; +float val2; +float val3; + +float2 vec2; +float3 vec3; +float4 vec4; + +void TakeItOut(out float2 it) { + it = val1; +} + +void TakeItIn(inout float3 it) { + it = val2; +} + +void TakeItIn2(inout float4 it) { + it += val3; +} + +void TakeEmOut(out float2 em) { + em = vec2; +} + +void TakeEmIn(inout float3 em) { + em = vec3; +} + +void TakeEmIn2(inout float4 em) { + em += vec4; +} + + +float2 RunTest(float it, float em) +{ + float c = it; + TakeItOut(it); // expected-warning{{implicit truncation of vector type}} + TakeItIn(it); // expected-warning{{implicit truncation of vector type}} + TakeItIn2(it); // expected-warning{{implicit truncation of vector type}} + + TakeEmOut(em); // expected-warning{{implicit truncation of vector type}} + TakeEmIn(em); // expected-warning{{implicit truncation of vector type}} + TakeEmIn2(em); // expected-warning{{implicit truncation of vector type}} + return float2(it, em); +} + +float2 main(float it: A, float em: B) : SV_Target +{ + return RunTest(it, em); +} + diff --git a/tools/clang/test/HLSLFileCheck/hlsl/operators/binary/vector-matrix-binops.hlsl b/tools/clang/test/HLSL/vector-matrix-binops.hlsl similarity index 54% rename from tools/clang/test/HLSLFileCheck/hlsl/operators/binary/vector-matrix-binops.hlsl rename to tools/clang/test/HLSL/vector-matrix-binops.hlsl index fdf3ad6ef3..b4d2850490 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/operators/binary/vector-matrix-binops.hlsl +++ b/tools/clang/test/HLSL/vector-matrix-binops.hlsl @@ -1,18 +1,4 @@ -// RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s - -// CHECK: vector-matrix-binops.hlsl:29:26: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:30:21: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:30:14: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:35:23: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:36:29: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:37:23: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:37:16: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:42:26: error: cannot convert from 'float4x4' to 'float4' -// CHECK: vector-matrix-binops.hlsl:43:29: error: cannot convert from 'float4' to 'float4x4' -// CHECK: vector-matrix-binops.hlsl:44:26: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:45:21: warning: implicit truncation of vector type -// CHECK: vector-matrix-binops.hlsl:58:29: error: cannot convert from 'float3x2' to 'float2x3' -// CHECK: vector-matrix-binops.hlsl:59:29: error: cannot convert from 'float2x3' to 'float1x4' +// RUN: %clang_cc1 -fsyntax-only -ffreestanding -verify % void main() { @@ -26,8 +12,8 @@ void main() { // vector truncation { - float2 res1 = v2 * v4; // expected-warning {{implicit truncation of vector type}} - float2 res2 = v4 - v3; // expected-warning {{implicit truncation of vector type}} + float2 res1 = v2 * v4; // expected-warning {{implicit truncation of vector type}} expected-warning {{implicit truncation of vector type}} + float2 res2 = v4 - v3; // expected-warning {{implicit truncation of vector type}} expected-warning {{implicit truncation of vector type}} } // matrix truncation @@ -55,7 +41,7 @@ void main() { // matrix mismatched dimensions { float2x3 m23 = float2x3(1, 2, 3, 4, 5, 6); - float3x2 res1 = m23 - m32; // expected-error {{error: cannot convert from 'float3x2' to 'float2x3'}} + float3x2 res1 = m23 - m32; // expected-error {{cannot convert from 'float3x2' to 'float2x3'}} float1x4 res2 = m14 / m23; // expected-error {{cannot convert from 'float2x3' to 'float1x4'}} } -} \ No newline at end of file +} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/classes/class_method_overload.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/classes/class_method_overload.hlsl index d89d8edd69..35914ba87c 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/classes/class_method_overload.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/classes/class_method_overload.hlsl @@ -1,11 +1,10 @@ // RUN: %dxc -T vs_6_0 -E main %s -ast-dump | FileCheck %s -// CHECK: FunctionDecl {{.*}} used CallMethod 'uint (__restrict MyClass)' +// CHECK: FunctionDecl {{.*}} used CallMethod 'uint (MyClass &__restrict)' // CHECK: CXXMemberCallExpr // CHECK-NEXT: MemberExpr {{.*}} .Method -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'MyClass' lvalue // CHECK-NEXT: DeclRefExpr -// CHECK-SAME: '__restrict MyClass' lvalue ParmVar 0x{{[0-9a-zA-Z]+}} 'c' '__restrict MyClass' +// CHECK-SAME: 'MyClass' lvalue ParmVar 0x{{[0-9a-zA-Z]+}} 'c' 'MyClass &__restrict' class MyClass { uint Method(uint2 u2) { return u2.y * 2; } diff --git a/tools/clang/test/HLSLFileCheck/hlsl/classes/derived-to-base-casting.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/classes/derived-to-base-casting.hlsl new file mode 100644 index 0000000000..3da7749f84 --- /dev/null +++ b/tools/clang/test/HLSLFileCheck/hlsl/classes/derived-to-base-casting.hlsl @@ -0,0 +1,82 @@ +// RUN: %dxc -T lib_6_4 -ast-dump %s | FileCheck %s + + +struct Base { + int b; +}; + +struct Derived : Base { + int d; +}; + +struct DerivedAgain : Derived { + int a; +}; + +void DerivedToBase() { + DerivedAgain da1, da2; + + (Base)da1 = (Base)da2; + (Derived)da1 = (Derived)da2; + (DerivedAgain)da1 = (DerivedAgain)da2; +} + +// CHECK: FunctionDecl {{0x[0-9a-fA-F]+}} line:16:6 DerivedToBase 'void ()' +// CHECK-NEXT: CompoundStmt {{0x[0-9a-fA-F]+}} +// CHECK-NEXT: DeclStmt {{0x[0-9a-fA-F]+}} +// CHECK-NEXT: VarDecl {{0x[0-9a-fA-F]+}} col:16 used da1 'DerivedAgain' +// CHECK-NEXT: VarDecl {{0x[0-9a-fA-F]+}} col:21 used da2 'DerivedAgain' +// CHECK-NEXT: BinaryOperator {{0x[0-9a-fA-F]+}} 'Base' '=' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'Base' lvalue Base)> +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da1' 'DerivedAgain' +// CHECK-NEXT: ImplicitCastExpr {{0x[0-9a-fA-F]+}} 'Base' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'Base' lvalue Base)> +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da2' 'DerivedAgain' +// CHECK-NEXT: BinaryOperator {{0x[0-9a-fA-F]+}} 'Derived' '=' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'Derived' lvalue +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da1' 'DerivedAgain' +// CHECK-NEXT: ImplicitCastExpr {{0x[0-9a-fA-F]+}} 'Derived' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'Derived' lvalue +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da2' 'DerivedAgain' +// CHECK-NEXT: BinaryOperator {{0x[0-9a-fA-F]+}} 'DerivedAgain' '=' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da1' 'DerivedAgain' +// CHECK-NEXT: ImplicitCastExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da2' 'DerivedAgain' + +groupshared DerivedAgain daGS; + +// CHECK-NEXT: VarDecl {{0x[0-9a-fA-F]+}} col:26 used daGS '__attribute__((address_space(3))) DerivedAgain' +// CHECK-NEXT: HLSLGroupSharedAttr {{0x[0-9a-fA-F]+}} + +void DerivedToBaseGS() { + DerivedAgain da1; + + (Base)da1 = (Base)daGS; + (Derived)da1 = (Derived)daGS; + (DerivedAgain)da1 = (DerivedAgain)daGS; +} + +// CHECK: FunctionDecl {{0x[0-9a-fA-F]+}} line:53:6 DerivedToBaseGS 'void ()' +// CHECK-NEXT: CompoundStmt {{0x[0-9a-fA-F]+}} +// CHECK-NEXT: DeclStmt {{0x[0-9a-fA-F]+}} +// CHECK-NEXT: VarDecl {{0x[0-9a-fA-F]+}} col:16 used da1 'DerivedAgain' +// CHECK-NEXT: BinaryOperator {{0x[0-9a-fA-F]+}} 'Base' '=' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'Base' lvalue Base)> +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da1' 'DerivedAgain' +// CHECK-NEXT: ImplicitCastExpr {{0x[0-9a-fA-F]+}} 'Base' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} '__attribute__((address_space(3))) Base' lvalue Base)> +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} '__attribute__((address_space(3))) DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'daGS' '__attribute__((address_space(3))) DerivedAgain' +// CHECK-NEXT: BinaryOperator {{0x[0-9a-fA-F]+}} 'Derived' '=' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'Derived' lvalue +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da1' 'DerivedAgain' +// CHECK-NEXT: ImplicitCastExpr {{0x[0-9a-fA-F]+}} 'Derived' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} '__attribute__((address_space(3))) Derived' lvalue +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} '__attribute__((address_space(3))) DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'daGS' '__attribute__((address_space(3))) DerivedAgain' +// CHECK-NEXT: BinaryOperator {{0x[0-9a-fA-F]+}} 'DerivedAgain' '=' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'da1' 'DerivedAgain' +// CHECK-NEXT: ImplicitCastExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' +// CHECK-NEXT: CStyleCastExpr {{0x[0-9a-fA-F]+}} 'DerivedAgain' lvalue +// CHECK-NEXT: DeclRefExpr {{0x[0-9a-fA-F]+}} '__attribute__((address_space(3))) DerivedAgain' lvalue Var {{0x[0-9a-fA-F]+}} 'daGS' '__attribute__((address_space(3))) DerivedAgain' diff --git a/tools/clang/test/HLSLFileCheck/hlsl/classes/this_cast_to_base_class.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/classes/this_cast_to_base_class.hlsl index ce91e35179..9de3ea34f6 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/classes/this_cast_to_base_class.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/classes/this_cast_to_base_class.hlsl @@ -1,52 +1,71 @@ // RUN: %dxc -T lib_6_6 -HV 2021 -disable-lifetime-markers -fcgl %s | FileCheck %s -// CHECK-LABEL: define linkonce_odr void @"\01?foo{{[@$?.A-Za-z0-9_]+}}"(%class.Child* %this) -// CHECK: %[[OutArg:.+]] = alloca %class.Parent -// CHECK: %[[OutThisPtr:.+]] = getelementptr inbounds %class.Child, %class.Child* %this, i32 0, i32 0 -// CHECK: call void @"\01?lib_func2{{[@$?.A-Za-z0-9_]+}}"(%class.Parent* %[[OutArg]]) -// Make sure copy-out. -// CHECK: %[[OutThisCpyPtr:.+]] = bitcast %class.Parent* %[[OutThisPtr]] to i8* -// CHECK: %[[OutArgCpyPtr:.+]] = bitcast %class.Parent* %[[OutArg]] to i8* -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[OutThisCpyPtr]], i8* %[[OutArgCpyPtr]], i64 8, i32 1, i1 false) - -// CHECK-LABEL: define linkonce_odr void @"\01?bar{{[@$?.A-Za-z0-9_]+}}"(%class.Child* %this) -// CHECK: %[[InOutArg:.+]] = alloca %class.Parent -// CHECK: %[[InOutThisPtr:.+]] = getelementptr inbounds %class.Child, %class.Child* %this, i32 0, i32 0 - -// Make sure copy-in. -// CHECK: %[[InOutArgCpyPtr:.+]] = bitcast %class.Parent* %[[InOutArg]] to i8* -// CHECK: %[[InOutThisCpyPtr:.+]] = bitcast %class.Parent* %[[InOutThisPtr]] to i8* -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[InOutArgCpyPtr]], i8* %[[InOutThisCpyPtr]], i64 8, i32 1, i1 false) - -// The call. -// CHECK: call void @"\01?lib_func3{{[@$?.A-Za-z0-9_]+}}"(%class.Parent* %[[InOutArg]]) - -// Make sure copy-out. -// CHECK: %[[InOutThisCpyOutPtr:.+]] = bitcast %class.Parent* %[[InOutThisPtr]] to i8* -// CHECK: %[[InOutArgCpyOutPtr:.+]] = bitcast %class.Parent* %[[InOutArg]] to i8* -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[InOutThisCpyOutPtr]], i8* %[[InOutArgCpyOutPtr]], i64 8, i32 1, i1 false) - -// CHECK-LABEL: define linkonce_odr i32 @"\01?foo{{[@$?.A-Za-z0-9_]+}}"(%class.Child* %this, i32 %a, i32 %b) +// `foo` calls `lib_func2` and copies out the temporary. Derived to base copies +// should probably always happen since HLSL doesn't have any real support for +// dynamic typing. + +// CHECK-LABEL: define linkonce_odr void @"\01?foo@ +// CHECK-SAME: (%class.Child* [[this:%.+]]) +// Create a temporary. +// CHECK: [[OutArg:%.+]] = alloca %class.Parent +// CHECK: [[OutThisPtr:%.+]] = bitcast %class.Child* [[this]] to %class.Parent* + +// Call lib_func2 with the temporary. +// CHECK: call void @"\01?lib_func2 +// CHECK-SAME: (%class.Parent* dereferenceable(8) [[OutArg]]) + +// Copy the temporary back to the `this` object. +// CHECK: [[OutThisCpyPtr:%.+]] = bitcast %class.Parent* [[OutThisPtr]] to i8* +// CHECK: [[OutArgCpyPtr:%.+]] = bitcast %class.Parent* [[OutArg]] to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[OutThisCpyPtr]], i8* [[OutArgCpyPtr]], i64 8, i32 1, i1 false) + +// `bar` calls `lib_func_3` with `this` as an `inout` parameter, so it needs to +// be initialized first, then copied back after the call. + +// CHECK-LABEL: define linkonce_odr void @"\01?bar@ +// CHEKC-SAME: (%class.Child* [[this:%.+]]) +// CHECK: [[InOutArg:%.+]] = alloca %class.Parent + +// Initialize the temporary from `this`. +// CHECK-DAG: [[ThisIPtr:%.+]] = getelementptr inbounds %class.Child, %class.Child* [[this]], i32 0, i32 0, i32 0 +// CHECK-DAG: [[ThisFPtr:%.+]] = getelementptr inbounds %class.Child, %class.Child* [[this]], i32 0, i32 0, i32 1 +// CHECK-DAG: [[ThisI:%.+]] = load i32, i32* [[ThisIPtr]] +// CHECK-DAG: [[ThisF:%.+]] = load float, float* [[ThisFPtr]] +// CHECK-DAG: [[TmpIPtr:%.+]] = getelementptr inbounds %class.Parent, %class.Parent* [[InOutArg]], i32 0, i32 0 +// CHECK-DAG: [[TmpFPtr:%.+]] = getelementptr inbounds %class.Parent, %class.Parent* [[InOutArg]], i32 0, i32 1 +// CHECK-DAG: store i32 [[ThisI]], i32* [[TmpIPtr]] +// CHECK-DAG: store float [[ThisF]], float* [[TmpFPtr]] + +// Call lib_func3 with the temporary. +// CHECK-DAG: call void @"\01?lib_func3@{{[@$?.A-Za-z0-9_]+}}"(%class.Parent* dereferenceable(8) [[InOutArg]]) + +// Copy back the temporary to `this`. There is a redundant bitcast here due to +// the aggregate copy trying to match the target type before the memcpy is +// generated. This could be removed in the future. + +// CHECK-DAG: [[ThisCastParent:%.+]] = bitcast %class.Child* [[this]] to %class.Parent* +// CHECK-DAG: [[ThisCastI8:%.+]] = bitcast %class.Parent* [[ThisCastParent]] to i8* +// CHECK-DAG: [[TmpCastI8:%.+]] = bitcast %class.Parent* [[InOutArg]] to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[ThisCastI8]], i8* [[TmpCastI8]], i64 8, i32 1, i1 false) + + +// CHECK-LABEL: define linkonce_odr i32 @"\01?foo@ +// CHECK-SAME: (%class.Child* [[this:%.+]], i32 [[a:%.+]], i32 [[b:%.+]]) // CHECK: %[[Arg:.+]] = alloca %class.Parent -// CHECK: %[[Tmp:.+]] = alloca %class.Parent, align 4 - -// Make sure this is copy to agg tmp. -// CHECK: %[[PtrI:.+]] = getelementptr inbounds %class.Child, %class.Child* %this, i32 0, i32 0, i32 0 -// CHECK: %[[PtrJ:.+]] = getelementptr inbounds %class.Child, %class.Child* %this, i32 0, i32 0, i32 1 -// CHECK: %[[I:.+]] = load i32, i32* %[[PtrI]] -// CHECK: %[[J:.+]] = load float, float* %[[PtrJ]] -// CHECK: %[[TmpPtrI:.+]] = getelementptr inbounds %class.Parent, %class.Parent* %[[Tmp]], i32 0, i32 0 -// CHECK: %[[TmpPtrJ:.+]] = getelementptr inbounds %class.Parent, %class.Parent* %[[Tmp]], i32 0, i32 1 -// CHECK: store i32 %[[I]], i32* %[[TmpPtrI]] -// CHECK: store float %[[J]], float* %[[TmpPtrJ]] - -// Make sure Tmp copy to Arg. -// CHECK: %[[ArgPtr:.+]] = bitcast %class.Parent* %[[Arg]] to i8* -// CHECK: %[[TmpPtr:.+]] = bitcast %class.Parent* %[[Tmp]] to i8* -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %[[ArgPtr]], i8* %[[TmpPtr]], i64 8, i32 1, i1 false) - -// Use Arg to call lib_func. -// CHECK: call i32 @"\01?lib_func{{[@$?.A-Za-z0-9_]+}}"(%class.Parent* %[[Arg]], i32 %{{.*}}, i32 %{{.*}}) + +// Initialize the temporary from `this`. +// CHECK-DAG: [[ThisIPtr:%.+]] = getelementptr inbounds %class.Child, %class.Child* [[this]], i32 0, i32 0, i32 0 +// CHECK-DAG: [[ThisFPtr:%.+]] = getelementptr inbounds %class.Child, %class.Child* [[this]], i32 0, i32 0, i32 1 +// CHECK-DAG: [[ThisI:%.+]] = load i32, i32* [[ThisIPtr]] +// CHECK-DAG: [[ThisF:%.+]] = load float, float* [[ThisFPtr]] +// CHECK-DAG: [[TmpIPtr:%.+]] = getelementptr inbounds %class.Parent, %class.Parent* [[InOutArg]], i32 0, i32 0 +// CHECK-DAG: [[TmpFPtr:%.+]] = getelementptr inbounds %class.Parent, %class.Parent* [[InOutArg]], i32 0, i32 1 +// CHECK-DAG: store i32 [[ThisI]], i32* [[TmpIPtr]] +// CHECK-DAG: store float [[ThisF]], float* [[TmpFPtr]] + +// Use the temporary to call lib_func. +// CHECK: call i32 @"\01?lib_func@ +// CHECK-SAME: (%class.Parent* %[[Arg]], i32 %{{.*}}, i32 %{{.*}}) class Parent { diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/lib_2d_array_call_export.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/lib_2d_array_call_export.hlsl index 96cced8ca7..ad097296e3 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/lib_2d_array_call_export.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/lib_2d_array_call_export.hlsl @@ -7,28 +7,60 @@ // arrays, then all that code, being dead, getting deleted, leaving just the // passing of uninitialized arrays to the function calls. -// CHECK-DAG: %[[h0:.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf0 -// CHECK-DAG: %[[h1:.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf1 -// CHECK-DAG: %[[h2:.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf2 -// CHECK-DAG: %[[h3:.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf3 -// CHECK: %[[buffers:.+]] = alloca [2 x [2 x %dx.types.Handle]] -// CHECK-DAG: %[[gep0:.+]] = getelementptr inbounds [2 x [2 x %dx.types.Handle]], [2 x [2 x %dx.types.Handle]]* %[[buffers]], i32 0, i32 0, i32 0 -// CHECK: store %dx.types.Handle %[[h0]], %dx.types.Handle* %[[gep0]], align 8 -// CHECK-DAG: %[[gep1:.+]] = getelementptr inbounds [2 x [2 x %dx.types.Handle]], [2 x [2 x %dx.types.Handle]]* %[[buffers]], i32 0, i32 0, i32 1 -// CHECK: store %dx.types.Handle %[[h1]], %dx.types.Handle* %[[gep1]], align 8 -// CHECK-DAG: %[[gep2:.+]] = getelementptr inbounds [2 x [2 x %dx.types.Handle]], [2 x [2 x %dx.types.Handle]]* %[[buffers]], i32 0, i32 1, i32 0 -// CHECK: store %dx.types.Handle %[[h2]], %dx.types.Handle* %[[gep2]], align 8 -// CHECK-DAG: %[[gep3:.+]] = getelementptr inbounds [2 x [2 x %dx.types.Handle]], [2 x [2 x %dx.types.Handle]]* %[[buffers]], i32 0, i32 1, i32 1 -// CHECK: store %dx.types.Handle %[[h3]], %dx.types.Handle* %[[gep3]], align 8 -// CHECK: call void @{{.*}}useArray{{.*}}([2 x [2 x %dx.types.Handle]]* nonnull %[[buffers]]) -// CHECK: call void @{{.*}}useArray{{.*}}([2 x [2 x %dx.types.Handle]]* nonnull %[[buffers]]) -// CHECK: %[[h10:.+]] = load %dx.types.Handle, %dx.types.Handle* %[[gep2]], align 8 -// CHECK: store %dx.types.Handle %[[h10]], %dx.types.Handle* %[[gep1]], align 8 -// CHECK: call void @{{.*}}useArray{{.*}}([2 x [2 x %dx.types.Handle]]* nonnull %[[buffers]]) -// CHECK: %[[h01:.+]] = load %dx.types.Handle, %dx.types.Handle* %[[gep1]], align 8 -// CHECK: store %dx.types.Handle %[[h01]], %dx.types.Handle* %[[gep2]], align 8 -// CHECK: call void @{{.*}}useArray{{.*}}([2 x [2 x %dx.types.Handle]]* nonnull %[[buffers]]) +// Because the array could be modified in the call, it needs to be +// re-initialized before each call. The tests below verify the initialization of +// the array between each call. +// CHECK-DAG: [[h0:%.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf0 +// CHECK-DAG: [[h1:%.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf1 +// CHECK-DAG: [[h2:%.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf2 +// CHECK-DAG: [[h3:%.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf3 +// CHECK: [[tmp:%.+]] = alloca [2 x [2 x %dx.types.Handle]] + +// CHECK-DAG: [[gep0:%.+]] = getelementptr inbounds [2 x [2 x %dx.types.Handle]], [2 x [2 x %dx.types.Handle]]* [[tmp]], i32 0, i32 0, i32 0 +// CHECK-DAG: [[gep1:%.+]] = getelementptr inbounds [2 x [2 x %dx.types.Handle]], [2 x [2 x %dx.types.Handle]]* [[tmp]], i32 0, i32 0, i32 1 +// CHECK-DAG: [[gep2:%.+]] = getelementptr inbounds [2 x [2 x %dx.types.Handle]], [2 x [2 x %dx.types.Handle]]* [[tmp]], i32 0, i32 1, i32 0 +// CHECK-DAG: [[gep3:%.+]] = getelementptr inbounds [2 x [2 x %dx.types.Handle]], [2 x [2 x %dx.types.Handle]]* [[tmp]], i32 0, i32 1, i32 1 + +// Store: {0, 1, 2, 3} +// CHECK-DAG: store %dx.types.Handle [[h0]], %dx.types.Handle* [[gep0]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h1]], %dx.types.Handle* [[gep1]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h2]], %dx.types.Handle* [[gep2]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h3]], %dx.types.Handle* [[gep3]], align 8 + +// CHECK: call void @{{.*}}useArray{{.*}}([2 x [2 x %dx.types.Handle]]* nonnull [[tmp]]) + +// Store: {0, 1, 2, 3} +// CHECK-DAG: store %dx.types.Handle [[h0]], %dx.types.Handle* [[gep0]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h1]], %dx.types.Handle* [[gep1]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h2]], %dx.types.Handle* [[gep2]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h3]], %dx.types.Handle* [[gep3]], align 8 + +// CHECK: call void @{{.*}}useArray{{.*}}([2 x [2 x %dx.types.Handle]]* nonnull [[tmp]]) + +// Store: {0, (1 | 2), 2, 3} + +// CHECK-DAG: [[cmp:%.*]] = icmp eq i32 {{%.*}}, 1 +// CHECK-DAG: [[h4:%.*]] = select i1 [[cmp]], %dx.types.Handle [[h2]], %dx.types.Handle [[h1]] + +// CHECK-DAG: store %dx.types.Handle [[h0]], %dx.types.Handle* [[gep0]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h4]], %dx.types.Handle* [[gep1]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h2]], %dx.types.Handle* [[gep2]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h3]], %dx.types.Handle* [[gep3]], align 8 + +// CHECK: call void @{{.*}}useArray{{.*}}([2 x [2 x %dx.types.Handle]]* nonnull [[tmp]]) + +// Store: {0, (1 | 2), ((1 | 2) | 2), 3} + +// CHECK-DAG: [[cmp2:%.*]] = icmp eq i32 {{%.*}}, 2 +// CHECK-DAG: [[h5:%.*]] = select i1 [[cmp2]], %dx.types.Handle [[h4]], %dx.types.Handle [[h2]] + +// CHECK-DAG: store %dx.types.Handle [[h0]], %dx.types.Handle* [[gep0]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h4]], %dx.types.Handle* [[gep1]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h5]], %dx.types.Handle* [[gep2]], align 8 +// CHECK-DAG: store %dx.types.Handle [[h3]], %dx.types.Handle* [[gep3]], align 8 + +// CHECK: call void @{{.*}}useArray{{.*}}([2 x [2 x %dx.types.Handle]]* nonnull [[tmp]]) void useArray(RWStructuredBuffer buffers[2][2]); RWStructuredBuffer buf0; diff --git a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/lib_2d_array_call_export_subarray.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/lib_2d_array_call_export_subarray.hlsl index e7cda73918..a60fbf670c 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/lib_2d_array_call_export_subarray.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/control_flow/attributes/unroll/lib_2d_array_call_export_subarray.hlsl @@ -3,28 +3,40 @@ // Ensure 2d array is broken down into 2 1d arrays, properly initialized, // modified, and passed to function calls -// CHECK-DAG: %[[h0:.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf0 -// CHECK-DAG: %[[h1:.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf1 -// CHECK-DAG: %[[h2:.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf2 -// CHECK-DAG: %[[h3:.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf3 -// CHECK: %[[a1:.+]] = alloca [2 x %dx.types.Handle] -// CHECK: %[[a0:.+]] = alloca [2 x %dx.types.Handle] -// CHECK-DAG: %[[gep0:.+]] = getelementptr [2 x %dx.types.Handle], [2 x %dx.types.Handle]* %[[a0]], i32 0, i32 0 -// CHECK-DAG: %[[gep1:.+]] = getelementptr [2 x %dx.types.Handle], [2 x %dx.types.Handle]* %[[a0]], i32 0, i32 1 -// CHECK-DAG: %[[gep2:.+]] = getelementptr [2 x %dx.types.Handle], [2 x %dx.types.Handle]* %[[a1]], i32 0, i32 0 -// CHECK-DAG: %[[gep3:.+]] = getelementptr [2 x %dx.types.Handle], [2 x %dx.types.Handle]* %[[a1]], i32 0, i32 1 -// CHECK: store %dx.types.Handle %[[h0]], %dx.types.Handle* %[[gep0]] -// CHECK: store %dx.types.Handle %[[h1]], %dx.types.Handle* %[[gep1]] -// CHECK: store %dx.types.Handle %[[h2]], %dx.types.Handle* %[[gep2]] -// CHECK: store %dx.types.Handle %[[h3]], %dx.types.Handle* %[[gep3]] -// CHECK: call void @{{.*}}useArray{{.*}}([2 x %dx.types.Handle]* nonnull %[[a0]]) -// CHECK: call void @{{.*}}useArray{{.*}}([2 x %dx.types.Handle]* nonnull %[[a1]]) -// CHECK: %[[h10:.+]] = load %dx.types.Handle, %dx.types.Handle* %[[gep2]] -// CHECK: store %dx.types.Handle %[[h10]], %dx.types.Handle* %[[gep1]] -// CHECK: call void @{{.*}}useArray{{.*}}([2 x %dx.types.Handle]* nonnull %[[a0]]) -// CHECK: %[[h01:.+]] = load %dx.types.Handle, %dx.types.Handle* %[[gep1]] -// CHECK: store %dx.types.Handle %[[h01]], %dx.types.Handle* %[[gep2]] -// CHECK: call void @{{.*}}useArray{{.*}}([2 x %dx.types.Handle]* nonnull %[[a1]]) +// CHECK-DAG: [[h0:%.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf0 +// CHECK-DAG: [[h1:%.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf1 +// CHECK-DAG: [[h2:%.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf2 +// CHECK-DAG: [[h3:%.+]] = load %dx.types.Handle, %dx.types.Handle* @{{.*}}buf3 +// CHECK: [[tmp:%.+]] = alloca [2 x %dx.types.Handle] +// CHECK-DAG: [[gep0:%.+]] = getelementptr inbounds [2 x %dx.types.Handle], [2 x %dx.types.Handle]* [[tmp]], i32 0, i32 0 +// CHECK-DAG: [[gep1:%.+]] = getelementptr inbounds [2 x %dx.types.Handle], [2 x %dx.types.Handle]* [[tmp]], i32 0, i32 1 + +// CHECK-DAG: store %dx.types.Handle [[h0]], %dx.types.Handle* [[gep0]] +// CHECK-DAG: store %dx.types.Handle [[h1]], %dx.types.Handle* [[gep1]] + +// CHECK: call void @{{.*}}useArray{{.*}}([2 x %dx.types.Handle]* nonnull [[tmp]]) + +// CHECK-DAG: store %dx.types.Handle [[h2]], %dx.types.Handle* [[gep0]] +// CHECK-DAG: store %dx.types.Handle [[h3]], %dx.types.Handle* [[gep1]] + +// CHECK: call void @{{.*}}useArray{{.*}}([2 x %dx.types.Handle]* nonnull [[tmp]]) + + +// CHECK-DAG: [[cmp:%.*]] = icmp eq i32 {{%.*}}, 1 +// CHECK-DAG: [[h4:%.*]] = select i1 [[cmp]], %dx.types.Handle [[h2]], %dx.types.Handle [[h1]] +// CHECK-DAG: store %dx.types.Handle [[h0]], %dx.types.Handle* [[gep0]] +// CHECK-DAG: store %dx.types.Handle [[h4]], %dx.types.Handle* [[gep1]] + +// CHECK: call void @{{.*}}useArray{{.*}}([2 x %dx.types.Handle]* nonnull [[tmp]]) + + +// CHECK-DAG: [[cmp2:%.*]] = icmp eq i32 {{%.*}}, 2 +// CHECK-DAG: [[h5:%.*]] = select i1 [[cmp2]], %dx.types.Handle [[h4]], %dx.types.Handle [[h2]] +// CHECK-DAG: store %dx.types.Handle [[h5]], %dx.types.Handle* [[gep0]] +// CHECK-DAG: store %dx.types.Handle [[h3]], %dx.types.Handle* [[gep1]] + + +// CHECK: call void @{{.*}}useArray{{.*}}([2 x %dx.types.Handle]* nonnull [[tmp]]) void useArray(RWStructuredBuffer buffers[2]); diff --git a/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource2.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource2.hlsl index 4dcd19447c..d2c534dbf0 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource2.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource2.hlsl @@ -1,8 +1,8 @@ // RUN: %dxc -Zi -E main -Od -T ps_6_0 %s | FileCheck %s -check-prefix=CHK_DB // RUN: %dxc -E main -Od -T ps_6_0 %s | FileCheck %s -check-prefix=CHK_NODB -// CHK_DB: 9:10: error: local resource not guaranteed to map to unique global resource. -// CHK_NODB: 9:10: error: local resource not guaranteed to map to unique global resource. +// CHK_DB: 16:10: error: local resource not guaranteed to map to unique global resource. +// CHK_NODB: 16:10: error: local resource not guaranteed to map to unique global resource. float4 Tex2D(Texture2D t, SamplerState s, float2 c) { diff --git a/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource3.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource3.hlsl index 1e61dcbae5..cd75b934a4 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource3.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource3.hlsl @@ -1,8 +1,8 @@ // RUN: %dxc -Zi -E main -Od -T ps_6_0 %s | FileCheck %s -check-prefix=CHK_DB // RUN: %dxc -E main -Od -T ps_6_0 %s | FileCheck %s -check-prefix=CHK_NODB -// CHK_DB: 9:10: error: local resource not guaranteed to map to unique global resource. -// CHK_NODB: 9:10: error: local resource not guaranteed to map to unique global resource. +// CHK_DB: 26:10: error: local resource not guaranteed to map to unique global resource. +// CHK_NODB: 26:10: error: local resource not guaranteed to map to unique global resource. float4 Tex2D(Texture2D t, SamplerState s, float2 c) { diff --git a/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource6_dbg.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource6_dbg.hlsl index bd7e513ceb..5772ca0c2b 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource6_dbg.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/diagnostics/errors/local_resource6_dbg.hlsl @@ -1,8 +1,8 @@ // RUN: %dxc -Zi -E main -Od -T ps_6_0 %s | FileCheck %s -check-prefix=CHK_DB // RUN: %dxc -E main -Od -T ps_6_0 %s | FileCheck %s -check-prefix=CHK_NODB -// CHK_DB: 9:10: error: local resource not guaranteed to map to unique global resource. -// CHK_NODB: 9:10: error: local resource not guaranteed to map to unique global resource. +// CHK_DB: 23:10: error: local resource not guaranteed to map to unique global resource. +// CHK_NODB: 23:10: error: local resource not guaranteed to map to unique global resource. float4 Tex2D(Texture2D t, SamplerState s, float2 c) { diff --git a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout-operators.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout-operators.hlsl index 9018362226..ffd1c1d8ef 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout-operators.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout-operators.hlsl @@ -18,11 +18,11 @@ void fn() { } // CHECK: define internal void @"\01?fn{{[@$?.A-Za-z0-9_]+}}"() -// CHECK: [[Tmp1:%[0-9A-Z]+]] = alloca float // CHECK: [[X:%[0-9A-Z]+]] = alloca float, align 4 // CHECK: [[Z:%[0-9A-Z]+]] = alloca float, align 4 // CHECK: [[Y:%[0-9A-Z]+]] = alloca i32, align 4 // CHECK: [[D:%[0-9A-Z]+]] = alloca %struct.Doggo +// CHECK: [[Tmp1:%[0-9a-z.]+]] = alloca float // First call has no copy-in/copy out parameters since all parameters are unique. // CHECK: call void @"\01??RDoggo{{[@$?.A-Za-z0-9_]+}}"(%struct.Doggo* [[D]], float* dereferenceable(4) [[X]], i32* dereferenceable(4) [[Y]], float* dereferenceable(4) [[Z]]) diff --git a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout-struct.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout-struct.hlsl index 3397651105..1d8d8bbba1 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout-struct.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout-struct.hlsl @@ -18,15 +18,22 @@ void fn() { CalledFunction(P.X, P); } -// CHECK: [[TmpP:%[0-9A-Z]+]] = alloca %struct.Pup +// CHECK: define internal void @"\01?fn@ +// CHECK-DAG: [[P:%[0-9A-Z]+]] = alloca %struct.Pup +// CHECK-DAG: [[X:%[0-9A-Z]+]] = alloca float, align 4 +// CHECK-DAG: [[TmpP:%[0-9a-z.]+]] = alloca %struct.Pup -// CHECK: [[X:%[0-9A-Z]+]] = alloca float, align 4 -// CHECK: [[P:%[0-9A-Z]+]] = alloca %struct.Pup, align 4 -// CHECK: call void @"\01?CalledFunction{{[@$?.A-Za-z0-9_]+}}"(float* dereferenceable(4) [[X]], %struct.Pup* [[P]]) +// CHECK: call void @"\01?CalledFunction{{[@$?.A-Za-z0-9_]+}}"(float* dereferenceable(4) [[X]], %struct.Pup* dereferenceable(4) [[P]]) -// CHECK: [[TmpPPtr:%[0-9A-Z]+]] = bitcast %struct.Pup* [[TmpP]] to i8* -// CHECK: [[PPtr:%[0-9A-Z]+]] = bitcast %struct.Pup* [[P]] to i8* +// CHECK-DAG: [[TmpPPtr:%[0-9]+]] = bitcast %struct.Pup* [[TmpP]] to i8* +// CHECK-DAG: [[PPtr:%[0-9]+]] = bitcast %struct.Pup* [[P]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[TmpPPtr]], i8* [[PPtr]], i64 4, i32 1, i1 false) +// CHECK: [[PXPtr:%[0-9A-Z]+]] = getelementptr inbounds %struct.Pup, %struct.Pup* [[P]], i32 0, i32 0 + +// CHECK: call void @"\01?CalledFunction{{[@$?.A-Za-z0-9_]+}}" +// CHECK-SAME: (float* dereferenceable(4) [[PXPtr]], %struct.Pup* dereferenceable(4) [[TmpP]]) + +// CHECK-DAG: [[PPtr:%[0-9]+]] = bitcast %struct.Pup* [[P]] to i8* +// CHECK-DAG: [[TmpPPtr:%[0-9]+]] = bitcast %struct.Pup* [[TmpP]] to i8* +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[PPtr]], i8* [[TmpPPtr]], i64 4, i32 1, i1 false) -// CHECK: [[PDotX:%[0-9A-Z]+]] = getelementptr inbounds %struct.Pup, %struct.Pup* [[P]], i32 0, i32 0 -// CHECK: call void @"\01?CalledFunction{{[@$?.A-Za-z0-9_]+}}"(float* dereferenceable(4) [[PDotX]], %struct.Pup* [[TmpP]]) diff --git a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout.hlsl index 6693b1a3f9..411ff21321 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/copyin-copyout.hlsl @@ -16,56 +16,68 @@ void fn() { } // CHECK: define internal void @"\01?fn{{[@$?.A-Za-z0-9_]+}}"() -// CHECK: [[Tmp1:%[0-9A-Z]+]] = alloca float -// CHECK: [[Tmp2:%[0-9A-Z]+]] = alloca float // CHECK: [[X:%[0-9A-Z]+]] = alloca float, align 4 // CHECK: [[Y:%[0-9A-Z]+]] = alloca float, align 4 // CHECK: [[Z:%[0-9A-Z]+]] = alloca float, align 4 +// CHECK: [[Tmp1:%[0-9a-z.]+]] = alloca float +// CHECK: [[Tmp2:%[0-9a-z.]+]] = alloca float // First call has no copy-in/copy out parameters since all parameters are unique. -// CHECK: call void @"\01?CalledFunction{{[@$?.A-Za-z0-9_]+}}"(float* dereferenceable(4) [[X]], float* dereferenceable(4) [[Y]], float* dereferenceable(4) [[Z]]) +// CHECK: call void @"\01?CalledFunction +// CHECK-SAME: (float* dereferenceable(4) [[X]], float* dereferenceable(4) [[Y]], float* dereferenceable(4) [[Z]]) // Second call, copies X for parameter 2. // CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[X]], align 4 -// CHECK: store float [[TmpX]], float* [[Tmp2]] +// CHECK: store float [[TmpX]], float* [[Tmp1]] -// CHECK: call void @"\01?CalledFunction{{[@$?.A-Za-z0-9_]+}}"(float* dereferenceable(4) [[X]], float* dereferenceable(4) [[Tmp2]], float* dereferenceable(4) [[Z]]) +// CHECK: call void @"\01?CalledFunction +// CHECK-SAME: (float* dereferenceable(4) [[X]], float* dereferenceable(4) [[Tmp1]], float* dereferenceable(4) [[Z]]) // Second call, saves parameter 2 to X after the call. -// CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[Tmp2]] +// CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[Tmp1]] // CHECK: store float [[TmpX]], float* [[X]] // The third call copies X for the third parameter. // CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[X]], align 4 -// CHECK: store float [[TmpX]], float* [[Tmp1]] +// CHECK: store float [[TmpX]], float* [[Tmp2]] -// CHECK: call void @"\01?CalledFunction{{[@$?.A-Za-z0-9_]+}}"(float* dereferenceable(4) [[X]], float* dereferenceable(4) [[Y]], float* dereferenceable(4) [[Tmp1]]) +// CHECK: call void @"\01?CalledFunction +// CHECK-SAME: (float* dereferenceable(4) [[X]], float* dereferenceable(4) [[Y]], float* dereferenceable(4) [[Tmp2]]) // The third call stores parameter 3 to X after the call. -// CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[Tmp1]] +// CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[Tmp2]] // CHECK: store float [[TmpX]], float* [[X]] +// CHECK: ret + void fn2() { float X = 0.0; CalledFunction(X, X, X); } -// CHECK: [[Tmp1:%[0-9A-Z]+]] = alloca float -// CHECK: [[Tmp2:%[0-9A-Z]+]] = alloca float +// CHECK: define internal void @"\01?fn2 + // CHECK: [[X:%[0-9A-Z]+]] = alloca float, align 4 +// CHECK: [[Tmp1:%[0-9a-z.]+]] = alloca float +// CHECK: [[Tmp2:%[0-9a-z.]+]] = alloca float -// X gets copied in for both parameters two and three. -// CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[X]], align 4 -// CHECK: store float [[TmpX]], float* [[Tmp2]] +// X gets copied in for both parameters two and three. The MSVC ABI dictates +// right to left construction of arguments. // CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[X]], align 4 // CHECK: store float [[TmpX]], float* [[Tmp1]] +// CHECK: [[TmpX:%[0-9A-Z]+]] = load float, float* [[X]], align 4 +// CHECK: store float [[TmpX]], float* [[Tmp2]] -// CHECK: call void @"\01?CalledFunction{{[@$?.A-Za-z0-9_]+}}"(float* dereferenceable(4) [[X]], float* dereferenceable(4) [[Tmp2]], float* dereferenceable(4) [[Tmp1]]) +// CHECK: call void @"\01?CalledFunction +// CHECK-SAME: (float* dereferenceable(4) [[X]], float* dereferenceable(4) [[Tmp2]], float* dereferenceable(4) [[Tmp1]]) // X gets restored from parameter 2 _then_ parameter 3, so the last paramter is // the final value of X. -// CHECK: [[X2:%[0-9A-Z]+]] = load float, float* [[Tmp2]] + +// CHECK: [[X1:%[0-9A-Z]+]] = load float, float* [[Tmp2]] +// CHECK: store float [[X1]], float* [[X]] + +// CHECK: [[X2:%[0-9A-Z]+]] = load float, float* [[Tmp1]] // CHECK: store float [[X2]], float* [[X]] -// CHECK: [[X1:%[0-9A-Z]+]] = load float, float* [[Tmp1]] -// CHECK: store float [[X1]], float* [[X]], align 4 -// line:31 col:3 + +// CHECK: ret diff --git a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/gep_arg_nocopy.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/gep_arg_nocopy.hlsl index 7135da876f..94161a8d68 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/gep_arg_nocopy.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/gep_arg_nocopy.hlsl @@ -22,17 +22,16 @@ float4 main() : SV_Target { // bar should be called with a copy of st.a. // CHECK: define <4 x float> @main() -// CHECK: [[a:%[0-9A-Z]+]] = getelementptr inbounds %"$Globals", %"$Globals"* {{%[0-9A-Z]+}}, i32 0, i32 0, i32 0 -// CHECK: [[Tmpa:%[0-9A-Z]+]] = alloca [32 x <4 x float>] -// CHECK: [[TmpaPtr:%[0-9A-Z]+]] = bitcast [32 x <4 x float>]* [[Tmpa]] to i8* -// CHECK: [[aPtr:%[0-9A-Z]+]] = bitcast [32 x <4 x float>]* [[a]] to i8* +// CHECK: [[a:%.+]] = getelementptr inbounds %"$Globals", %"$Globals"* {{%[0-9A-Z]+}}, i32 0, i32 0, i32 0 +// CHECK: [[Tmpa:%.+]] = alloca [32 x <4 x float>] +// CHECK: [[TmpaPtr:%.+]] = bitcast [32 x <4 x float>]* [[Tmpa]] to i8* +// CHECK: [[aPtr:%.+]] = bitcast [32 x <4 x float>]* [[a]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[TmpaPtr]], i8* [[aPtr]], i64 512, i32 1, i1 false) // CHECK: call <4 x float> @"\01?bar{{[@$?.A-Za-z0-9_]+}}"([32 x <4 x float>]* [[Tmpa]]) -// Bug: Because a isn't marked noalias, we are generating copies for it. // CHECK: define internal <4 x float> @"\01?bar{{[@$?.A-Za-z0-9_]+}}"([32 x <4 x float>]* [[a:%[0-9a]+]]) #1 { -// CHECK: [[Tmpa:%[0-9A-Z]+]] = alloca [32 x <4 x float>] -// CHECK: [[TmpaPtr:%[0-9A-Z]+]] = bitcast [32 x <4 x float>]* [[Tmpa]] to i8* -// CHECK: [[aPtr:%[0-9A-Z]+]] = bitcast [32 x <4 x float>]* [[a]] to i8* +// CHECK: [[Tmpa:%.+]] = alloca [32 x <4 x float>] +// CHECK: [[TmpaPtr:%.+]] = bitcast [32 x <4 x float>]* [[Tmpa]] to i8* +// CHECK: [[aPtr:%.+]] = bitcast [32 x <4 x float>]* [[a]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[TmpaPtr]], i8* [[aPtr]], i64 512, i32 1, i1 false) // CHECK: call <4 x float> @"\01?foo{{[@$?.A-Za-z0-9_]+}}"([32 x <4 x float>]* [[Tmpa]], i32 {{%[0-9A-Z]+}}) diff --git a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/global_constant_const.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/global_constant_const.hlsl index f9cfeb34a7..e3226673ca 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/global_constant_const.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/global_constant_const.hlsl @@ -22,12 +22,15 @@ float4 main() : SV_Target { // bar should be called with a copy of st.a. // CHECK: define <4 x float> @main() -// CHECK: [[a:%[0-9A-Z]+]] = getelementptr inbounds %"$Globals", %"$Globals"* {{%[0-9A-Z]+}}, i32 0, i32 0, i32 0 -// CHECK: call <4 x float> @"\01?bar{{[@$?.A-Za-z0-9_]+}}"([32 x <4 x float>]* [[a]]) +// CHECK-DAG: [[tmp:%[0-9A-Za-z.]+]] = alloca [32 x <4 x float>], align 4 +// CHECK-DAG: [[global:%[0-9]+]] = getelementptr inbounds %"$Globals", %"$Globals"* %2, i32 0, i32 0, i32 0 +// CHECK-DAG: [[dstPtr:%[0-9]+]] = bitcast [32 x <4 x float>]* [[tmp]] to i8* +// CHECK-DAG: [[srcPtr:%[0-9]+]] = bitcast [32 x <4 x float>]* [[global]] to i8* +// CHECK-DAG: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[dstPtr]], i8* [[srcPtr]], i64 512, i32 1, i1 false) +// CHECK: call <4 x float> @"\01?bar{{[@$?.A-Za-z0-9_]+}}"([32 x <4 x float>]* [[tmp]]) -// Bug: Because a isn't marked noalias, we are generating copies for it. // CHECK: define internal <4 x float> @"\01?bar{{[@$?.A-Za-z0-9_]+}}"([32 x <4 x float>]* [[a:%[0-9a]+]]) #1 { -// CHECK: [[Tmpa:%[0-9A-Z]+]] = alloca [32 x <4 x float>] +// CHECK: [[Tmpa:%[0-9A-Za-z.]+]] = alloca [32 x <4 x float>] // CHECK: [[TmpaPtr:%[0-9A-Z]+]] = bitcast [32 x <4 x float>]* [[Tmpa]] to i8* // CHECK: [[aPtr:%[0-9A-Z]+]] = bitcast [32 x <4 x float>]* [[a]] to i8* // CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[TmpaPtr]], i8* [[aPtr]], i64 512, i32 1, i1 false) diff --git a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/inout_trunc.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/inout_trunc.hlsl deleted file mode 100644 index 261dd30b29..0000000000 --- a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/inout_trunc.hlsl +++ /dev/null @@ -1,72 +0,0 @@ -// RUN: %dxc -E NocrashMain -T ps_6_0 %s | FileCheck %s -check-prefix=CHK_NOCRASH -// RUN: %dxc -E WarnMain -T ps_6_0 %s | FileCheck %s -check-prefix=CHK_WARN - -// Test that no crashes result when a scalar is provided to an outvar -// and that the new warning is produced. - -// CHK_WARN: warning: implicit truncation of vector type -// CHK_WARN: warning: implicit truncation of vector type -// CHK_WARN: warning: implicit truncation of vector type -// CHK_WARN: warning: implicit truncation of vector type -// CHK_WARN: warning: implicit truncation of vector type -// CHK_WARN: warning: implicit truncation of vector type -// CHK_WARN-NOT: warning: implicit truncation of vector type -// CHK_NOCRASH: NocrashMain - -float val1; -float val2; -float val3; - -float2 vec2; -float3 vec3; -float4 vec4; - -void TakeItOut(out float2 it) { - it = val1; -} - -void TakeItIn(inout float3 it) { - it = val2; -} - -void TakeItIn2(inout float4 it) { - it += val3; -} - -void TakeEmOut(out float2 em) { - em = vec2; -} - -void TakeEmIn(inout float3 em) { - em = vec3; -} - -void TakeEmIn2(inout float4 em) { - em += vec4; -} - - -float2 RunTest(float it, float em) -{ - float c = it; - TakeItOut(it); - TakeItIn(it); - TakeItIn2(it); - - TakeEmOut(em); - TakeEmIn(em); - TakeEmIn2(em); - return float2(it, em); -} - -float2 NocrashMain(float it: A, float em: B) : SV_Target -{ - return RunTest(it, em); -} - -// Missing out semantic to force filecheck to read stderr and see the warnings. -float2 WarnMain(float it: A, float em: B) -{ - return RunTest(it, em); -} - diff --git a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/parameter_types.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/parameter_types.hlsl index 9f8bf9753d..aa5dd854e1 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/parameter_types.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/functions/arguments/parameter_types.hlsl @@ -1,7 +1,7 @@ // RUN: %dxc -E main -T cs_6_0 -fcgl %s | FileCheck %s // CHECK: float %a, <4 x float> %b, %struct.T* %t, %class.matrix.float.2.3 %m, [3 x <2 x float>]* %n -// CHECK: float* noalias dereferenceable(4) %a, <4 x float>* noalias dereferenceable(16) %b, %struct.T* noalias %t, %class.matrix.float.2.3* noalias dereferenceable(24) %m, [3 x <2 x float>]* noalias %n +// CHECK: float* noalias dereferenceable(4) %a, <4 x float>* noalias dereferenceable(16) %b, %struct.T* noalias dereferenceable(20) %t, %class.matrix.float.2.3* noalias dereferenceable(24) %m, [3 x <2 x float>]* noalias dereferenceable(24) %n struct T{ float a; diff --git a/tools/clang/test/HLSLFileCheck/hlsl/intrinsics/atomic/atomic_cast1.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/intrinsics/atomic/atomic_cast1.hlsl index 45ba62a770..c891c60626 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/intrinsics/atomic/atomic_cast1.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/intrinsics/atomic/atomic_cast1.hlsl @@ -1,7 +1,7 @@ // RUN: %dxc -E main -T cs_6_0 %s | FileCheck %s // Make sure cast lvalue not work. -// CHECK: cannot compile this unexpected cast lvalue +// CHECK: cannot initialize a parameter of type 'int &' with an rvalue of type 'int' RWBuffer buffer; @@ -10,4 +10,4 @@ void main() { float unused; InterlockedOr((int)buffer[0], 1, unused); -} \ No newline at end of file +} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/objects/Scenarios/unbounded-resource-arrays.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/objects/Scenarios/unbounded-resource-arrays.hlsl index 847d1f178e..7034504b2b 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/objects/Scenarios/unbounded-resource-arrays.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/objects/Scenarios/unbounded-resource-arrays.hlsl @@ -53,8 +53,8 @@ SamplerComparisonState SampCmp SUB(15) : register(t0, space15); // ConstantBuffer and TextureBuffer get created first -// CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 64, i1 false) -// CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 61, i1 false) +// CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 64, i1 false) +// CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 61, i1 false) ConstantBuffer CBuf SUB(16) : register(b0, space16); // 1*(3+16)*3 + 1*3 + 1 = 61 TextureBuffer TBuf SUB(17) : register(t0, space17); // 1*(3+17)*3 + 1*3 + 1 = 64 @@ -73,34 +73,34 @@ FeedbackTexture2DArray FBTex2DArr SUB(27) : register(u float4 main(int4 a : A, float4 f : F) : SV_Target { - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 13, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 16, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 19, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 22, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 25, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 13, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 16, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 19, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 22, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 25, i1 false) float4 res = Tex1D IDX(0).Load(a.x) // 1*(3+0)*3 + 1*3 + 1 = 13 * Tex1DArr IDX(1).Load(a.xyz) // 1*(3+1)*3 + 1*3 + 1 = 16 * Tex2D IDX(2).Load(a.xyz) // 1*(3+2)*3 + 1*3 + 1 = 19 * Tex2DArr IDX(3).Load(a.xyzw) // 1*(3+3)*3 + 1*3 + 1 = 22 * Tex3D IDX(4).Load(a.xyzw) // 1*(3+4)*3 + 1*3 + 1 = 25 - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 28, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 31, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 34, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 37, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 40, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 28, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 31, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 34, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 37, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 40, i1 false) * RWTex1D IDX(5).Load(a.x) // 1*(3+5)*3 + 1*3 + 1 = 28 * RWTex1DArr IDX(6).Load(a.xy) // 1*(3+6)*3 + 1*3 + 1 = 31 * RWTex2D IDX(7).Load(a.xy) // 1*(3+7)*3 + 1*3 + 1 = 34 * RWTex2DArr IDX(8).Load(a.xyz) // 1*(3+8)*3 + 1*3 + 1 = 37 * RWTex3D IDX(9).Load(a.xyz) // 1*(3+9)*3 + 1*3 + 1 = 40 - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 43, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 46, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 49, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 55, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 52, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 58, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 43, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 46, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 49, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 55, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 52, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 58, i1 false) * Tex2DMS IDX(10).Load(a.xy, a.w) // 1*(3+10)*3 + 1*3 + 1 = 43 * Tex2DMSArr IDX(11).Load(a.xyz, a.w) // 1*(3+11)*3 + 1*3 + 1 = 46 * TexCube IDX(12).Sample(Samp IDX(14), f.xyz) // 1*(3+12)*3 + 1*3 + 1 = 49; 1*(3+14)*3 + 1*3 + 1 = 55 @@ -110,29 +110,29 @@ float4 main(int4 a : A, float4 f : F) : SV_Target { * CBuf IDX(16).f * TBuf IDX(17).f - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 67, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 70, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 73, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 67, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 70, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 73, i1 false) * Buf IDX(18).Load(a.x) // 1*(3+18)*3 + 1*3 + 1 = 67 * SBuf IDX(19).Load(a.x) // 1*(3+19)*3 + 1*3 + 1 = 70 * BABuf IDX(20).Load(a.x) // 1*(3+20)*3 + 1*3 + 1 = 73 - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 76, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 79, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 82, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 85, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 76, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 79, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 82, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 85, i1 false) * RWBuf IDX(21).Load(a.x) // 1*(3+21)*3 + 1*3 + 1 = 76 * RWSBuf IDX(22).Load(a.x) // 1*(3+22)*3 + 1*3 + 1 = 79 * RWBABuf IDX(23).Load(a.x)// 1*(3+23)*3 + 1*3 + 1 = 82 * CSBuf IDX(24).Consume(); // 1*(3+24)*3 + 1*3 + 1 = 85 - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 88, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 91, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 19, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 55, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 94, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 22, i1 false) - // CHECK: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 55, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 88, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 91, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 19, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 55, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 94, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 22, i1 false) + // CHECK-DAG: call %dx.types.Handle @dx.op.createHandle{{.*}}i32 55, i1 false) ASBuf IDX(25).Append(3.0); // 1*(3+25)*3 + 1*3 + 1 = 88 FBTex2D IDX(26).WriteSamplerFeedback(Tex2D IDX(2), Samp IDX(14), f.xy); // 1*(3+26)*3 + 1*3 + 1 = 91; 1*(3+2)*3 + 1*3 + 1 = 19; 1*(3+14)*3 + 1*3 + 1 = 55 FBTex2DArr IDX(27).WriteSamplerFeedback(Tex2DArr IDX(3), Samp IDX(14), f.xyz); // 1*(3+27)*3 + 1*3 + 1 = 94; 1*(3+3)*3 + 1*3 + 1 = 22; 1*(3+14)*3 + 1*3 + 1 = 55 diff --git a/tools/clang/test/HLSLFileCheck/hlsl/resource_binding/res_in_cb2.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/resource_binding/res_in_cb2.hlsl index 14c04cb2a8..941927c4ca 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/resource_binding/res_in_cb2.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/resource_binding/res_in_cb2.hlsl @@ -2,8 +2,8 @@ // Make sure array of struct with resource works. -//CHECK: x.1.s sampler NA NA S0 s4 1 -//CHECK: x.0.s sampler NA NA S1 s3 1 +//CHECK: x.0.s sampler NA NA S0 s3 1 +//CHECK: x.1.s sampler NA NA S1 s4 1 //CHECK: x.1.x texture f32 2d T0 t5 1 //CHECK: x.0.y texture f32 2d T1 t4 1 //CHECK: m texture f32 2d T2 t9 1 @@ -23,4 +23,4 @@ cbuffer A { float4 main(float2 uv:UV, uint i:I) : SV_Target { return x[1].x.Sample(x[1].s,uv) + x[0].y.Sample(x[0].s, uv) + m.Sample(x[0].s, uv); -} \ No newline at end of file +} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/types/cast/cast7.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/types/cast/cast7.hlsl deleted file mode 100644 index bb7eeca662..0000000000 --- a/tools/clang/test/HLSLFileCheck/hlsl/types/cast/cast7.hlsl +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %dxc -E main -T ps_6_0 %s -fcgl | FileCheck %s - -// Make sure no bitcast to %struct.A*. -// CHECK-NOT: to %struct.A* - -struct A { - float a; -}; - -struct B : A { - float b; -}; - -A ga; -float2 ib; - -float main() : SV_Target -{ - B b = {ib}; - (A)b = ga; - return ((A)b).a + b.b; -} diff --git a/tools/clang/test/HLSLFileCheck/hlsl/types/vector/VectorIndexingAsArgument.hlsl b/tools/clang/test/HLSLFileCheck/hlsl/types/vector/VectorIndexingAsArgument.hlsl index 5cbaf6ce90..92affbf1b5 100644 --- a/tools/clang/test/HLSLFileCheck/hlsl/types/vector/VectorIndexingAsArgument.hlsl +++ b/tools/clang/test/HLSLFileCheck/hlsl/types/vector/VectorIndexingAsArgument.hlsl @@ -8,10 +8,12 @@ // CHECK:%[[A4:.*]] = alloca i32 // CHECK:%[[A5:.*]] = alloca i32 // CHECK:%[[A6:.*]] = alloca i32 +// CHECK:%[[A7:.*]] = alloca i32 +// CHECK:%[[A8:.*]] = alloca i32 -// CHECK:call void @"\01?foo{{[@$?.A-Za-z0-9_]+}}"(i32 0, i32* nonnull dereferenceable(4) %[[A0]], i32* nonnull dereferenceable(4) %[[A5]], i32* nonnull dereferenceable(4) %[[A6]]) -// CHECK:call void @"\01?foo{{[@$?.A-Za-z0-9_]+}}"(i32 0, i32* nonnull dereferenceable(4) %[[A4]], i32* nonnull dereferenceable(4) %[[A3]], i32* nonnull dereferenceable(4) %[[A6]]) -// CHECK:call void @"\01?foo{{[@$?.A-Za-z0-9_]+}}"(i32 0, i32* nonnull dereferenceable(4) %[[A2]], i32* nonnull dereferenceable(4) %[[A1]], i32* nonnull dereferenceable(4) %[[A6]]) +// CHECK:call void @"\01?foo{{[@$?.A-Za-z0-9_]+}}"(i32 0, i32* nonnull dereferenceable(4) %[[A2]], i32* nonnull dereferenceable(4) %[[A1]], i32* nonnull dereferenceable(4) %[[A0]]) +// CHECK:call void @"\01?foo{{[@$?.A-Za-z0-9_]+}}"(i32 0, i32* nonnull dereferenceable(4) %[[A5]], i32* nonnull dereferenceable(4) %[[A4]], i32* nonnull dereferenceable(4) %[[A3]]) +// CHECK:call void @"\01?foo{{[@$?.A-Za-z0-9_]+}}"(i32 0, i32* nonnull dereferenceable(4) %[[A8]], i32* nonnull dereferenceable(4) %[[A7]], i32* nonnull dereferenceable(4) %[[A6]]) struct DimStruct { uint2 Dims; diff --git a/tools/clang/test/HLSLFileCheck/passes/hl/hl_legalize_param/out_only_param.ll b/tools/clang/test/HLSLFileCheck/passes/hl/hl_legalize_param/out_only_param.ll deleted file mode 100644 index 3e7d0a6360..0000000000 --- a/tools/clang/test/HLSLFileCheck/passes/hl/hl_legalize_param/out_only_param.ll +++ /dev/null @@ -1,119 +0,0 @@ -; RUN: %opt %s -hl-legalize-parameter -S | FileCheck %s - -; Make sure no memcpy generated. -; CHECK-NOT:memcpy - -;struct ST { -; float4 a; -;}; -; -;ST CreateST(float a) { -; ST st; -; st.a = a; -; return st; -;} -; -;float a; -;ST GetST() { return CreateST(a); } -; -;float4 main() : SV_Target { -; -; return GetST().a; -;} - -target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" -target triple = "dxil-ms-dx" - -%"$Globals" = type { float } -%struct.ST = type { <4 x float> } -%dx.types.Handle = type { i8* } - -@"\01?a@@3MB" = external constant float, align 4 -@"$Globals" = external constant %"$Globals" - -; Function Attrs: nounwind -define <4 x float> @main() #0 { -entry: - %tmp = alloca %struct.ST, align 4 - call void @"\01?GetST@@YA?AUST@@XZ"(%struct.ST* sret %tmp) - %a = getelementptr inbounds %struct.ST, %struct.ST* %tmp, i32 0, i32 0 - %0 = load <4 x float>, <4 x float>* %a, align 4, !tbaa !27 - ret <4 x float> %0 -} - -; Function Attrs: alwaysinline nounwind -define internal void @"\01?GetST@@YA?AUST@@XZ"(%struct.ST* noalias sret %agg.result) #1 { -entry: - %0 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22$Globals\22*, i32)"(i32 0, %"$Globals"* @"$Globals", i32 0) - %1 = call %"$Globals"* @"dx.hl.subscript.cb.%\22$Globals\22* (i32, %dx.types.Handle, i32)"(i32 6, %dx.types.Handle %0, i32 0) - %2 = getelementptr inbounds %"$Globals", %"$Globals"* %1, i32 0, i32 0 - %3 = load float, float* %2, align 4, !tbaa !30 - call void @"\01?CreateST@@YA?AUST@@M@Z"(%struct.ST* sret %agg.result, float %3) - ret void -} - -; Function Attrs: alwaysinline nounwind -define internal void @"\01?CreateST@@YA?AUST@@M@Z"(%struct.ST* noalias sret %agg.result, float %a) #1 { -entry: - %a.addr = alloca float, align 4, !dx.temp !13 - store float %a, float* %a.addr, align 4, !tbaa !30 - %0 = load float, float* %a.addr, align 4, !tbaa !30 - %splat.splatinsert = insertelement <4 x float> undef, float %0, i32 0 - %splat.splat = shufflevector <4 x float> %splat.splatinsert, <4 x float> undef, <4 x i32> zeroinitializer - %a1 = getelementptr inbounds %struct.ST, %struct.ST* %agg.result, i32 0, i32 0 - store <4 x float> %splat.splat, <4 x float>* %a1, align 4, !tbaa !27 - ret void -} - -; Function Attrs: nounwind readnone -declare %"$Globals"* @"dx.hl.subscript.cb.%\22$Globals\22* (i32, %dx.types.Handle, i32)"(i32, %dx.types.Handle, i32) #2 - -; Function Attrs: nounwind readnone -declare %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22$Globals\22*, i32)"(i32, %"$Globals"*, i32) #2 - -attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="0" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { alwaysinline nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="0" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #2 = { nounwind readnone } - -!pauseresume = !{!0} -!llvm.ident = !{!1} -!dx.version = !{!2} -!dx.valver = !{!3} -!dx.shaderModel = !{!4} -!dx.typeAnnotations = !{!5, !9} -!dx.entryPoints = !{!20} -!dx.fnprops = !{!24} -!dx.options = !{!25, !26} - -!0 = !{!"hlsl-hlemit", !"hlsl-hlensure"} -!1 = !{!"clang version 3.7 (tags/RELEASE_370/final)"} -!2 = !{i32 1, i32 0} -!3 = !{i32 1, i32 6} -!4 = !{!"ps", i32 6, i32 0} -!5 = !{i32 0, %struct.ST undef, !6, %"$Globals" undef, !8} -!6 = !{i32 16, !7} -!7 = !{i32 6, !"a", i32 3, i32 0, i32 7, i32 9} -!8 = !{i32 4, !7} -!9 = !{i32 1, <4 x float> ()* @main, !10, void (%struct.ST*)* @"\01?GetST@@YA?AUST@@XZ", !14, void (%struct.ST*, float)* @"\01?CreateST@@YA?AUST@@M@Z", !17} -!10 = !{!11} -!11 = !{i32 1, !12, !13} -!12 = !{i32 4, !"SV_Target", i32 7, i32 9} -!13 = !{} -!14 = !{!15, !16} -!15 = !{i32 0, !13, !13} -!16 = !{i32 1, !13, !13} -!17 = !{!15, !16, !18} -!18 = !{i32 0, !19, !13} -!19 = !{i32 7, i32 9} -!20 = !{<4 x float> ()* @main, !"main", null, !21, null} -!21 = !{null, null, !22, null} -!22 = !{!23} -!23 = !{i32 0, %"$Globals"* @"$Globals", !"$Globals", i32 0, i32 -1, i32 1, i32 4, null} -!24 = !{<4 x float> ()* @main, i32 0, i1 false} -!25 = !{i32 144} -!26 = !{i32 -1} -!27 = !{!28, !28, i64 0} -!28 = !{!"omnipotent char", !29, i64 0} -!29 = !{!"Simple C/C++ TBAA"} -!30 = !{!31, !31, i64 0} -!31 = !{!"float", !28, i64 0} diff --git a/tools/clang/test/HLSLFileCheck/pix/AnnotateVirtualRegs-Raygen.hlsl b/tools/clang/test/HLSLFileCheck/pix/AnnotateVirtualRegs-Raygen.hlsl index 0e48f3043e..b8150a0fa8 100644 --- a/tools/clang/test/HLSLFileCheck/pix/AnnotateVirtualRegs-Raygen.hlsl +++ b/tools/clang/test/HLSLFileCheck/pix/AnnotateVirtualRegs-Raygen.hlsl @@ -21,14 +21,14 @@ void ENTRY() TraceRay(scene, 0 /*rayFlags*/, 0xFF /*rayMask*/, 0 /*sbtRecordOffset*/, 1 /*sbtRecordStride*/, 0 /*missIndex*/, ray, pld); } -// CHECK: {{.*}} = alloca %struct.RayDesc, align 4, !pix-dxil-inst-num {{.*}}, !pix-alloca-reg [[RDAlloca:![0-9]+]] -// CHECK: {{.*}} = alloca %struct.RayPayload, align 4, !pix-dxil-inst-num {{.*}}, !pix-alloca-reg [[RPAlloca:![0-9]+]] -// CHECK: {{.*}} = getelementptr inbounds %struct.RayDesc, %struct.RayDesc* {{.*}}, i32 0, i32 0, !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[RDGEP:![0-9]+]] -// CHECK: {{.*}} = load i32, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @dx.nothing.a, i32 0, i32 0), !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[NothGEP:![0-9]+]] -// CHECK: {{.*}} = getelementptr inbounds %struct.RayDesc, %struct.RayDesc* {{.*}}, i32 0, i32 1, !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[RDGEP2:![0-9]+]] -// CHECK: {{.*}} = load i32, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @dx.nothing.a, i32 0, i32 0), !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[NothGEP2:![0-9]+]] -// CHECK: {{.*}} = getelementptr inbounds %struct.RayDesc, %struct.RayDesc* {{.*}}, i32 0, i32 2, !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[RDGEP3:![0-9]+]] -// CHECK: {{.*}} = load i32, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @dx.nothing.a, i32 0, i32 0), !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[NothGEP3:![0-9]+]] +// CHECK-DAG: {{.*}} = alloca %struct.RayDesc, align 4, !pix-dxil-inst-num {{.*}}, !pix-alloca-reg [[RDAlloca:![0-9]+]] +// CHECK-DAG: {{.*}} = alloca %struct.RayPayload, align 4, !pix-dxil-inst-num {{.*}}, !pix-alloca-reg [[RPAlloca:![0-9]+]] +// CHECK-DAG: {{.*}} = getelementptr inbounds %struct.RayDesc, %struct.RayDesc* {{.*}}, i32 0, i32 0, !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[RDGEP:![0-9]+]] +// CHECK-DAG: {{.*}} = load i32, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @dx.nothing.a, i32 0, i32 0), !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[NothGEP:![0-9]+]] +// CHECK-DAG: {{.*}} = getelementptr inbounds %struct.RayDesc, %struct.RayDesc* {{.*}}, i32 0, i32 1, !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[RDGEP2:![0-9]+]] +// CHECK-DAG: {{.*}} = load i32, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @dx.nothing.a, i32 0, i32 0), !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[NothGEP2:![0-9]+]] +// CHECK-DAG: {{.*}} = getelementptr inbounds %struct.RayDesc, %struct.RayDesc* {{.*}}, i32 0, i32 2, !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[RDGEP3:![0-9]+]] +// CHECK-DAG: {{.*}} = load i32, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @dx.nothing.a, i32 0, i32 0), !pix-dxil-inst-num {{.*}}, !pix-dxil-reg [[NothGEP3:![0-9]+]] // CHECK-DAG: [[RDAlloca]] = !{i32 1, i32 0, i32 8} // CHECK-DAG: [[RPAlloca]] = !{i32 1, i32 8, i32 3} diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/library/inout_struct_mismatch-strictudt.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/library/inout_struct_mismatch-strictudt.hlsl index b52cd207aa..a4fd446881 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/library/inout_struct_mismatch-strictudt.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/library/inout_struct_mismatch-strictudt.hlsl @@ -2,13 +2,9 @@ // CHECK: define <4 x float> // CHECK-SAME: main -// CHECK-NOT: bitcast -// CHECK-NOT: CallStruct -// CHECK: ParamStruct -// CHECK: call void @llvm.lifetime.start -// CHECK-NOT: bitcast -// CHECK-NOT: CallStruct -// CHECK-LABEL: ret <4 x float> +// CHECK: [[local:%(local)|([0-9]+)]] = alloca %struct.CallStruct +// CHECK: [[param:%[0-9]+]] = bitcast %struct.CallStruct* [[local]] to %struct.ParamStruct* +// CHEKC: call void @"\01?modify_ext{{.*}}(%struct.ParamStruct* dereferenceable(8) [[param]]) struct ParamStruct { int i; diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/library/inout_struct_mismatch.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/library/inout_struct_mismatch.hlsl index 0dec1c0e35..4c02d04189 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/library/inout_struct_mismatch.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/library/inout_struct_mismatch.hlsl @@ -2,13 +2,9 @@ // CHECK: define <4 x float> // CHECK-SAME: main -// CHECK-NOT: bitcast -// CHECK-NOT: CallStruct -// CHECK: ParamStruct -// CHECK: call void @llvm.lifetime.start -// CHECK-NOT: bitcast -// CHECK-NOT: CallStruct -// CHECK-LABEL: ret <4 x float> +// CHECK: [[local:%(local)|([0-9]+)]] = alloca %struct.CallStruct +// CHECK: [[param:%[0-9]+]] = bitcast %struct.CallStruct* [[local]] to %struct.ParamStruct* +// CHEKC: call void @"\01?modify_ext{{.*}}(%struct.ParamStruct* dereferenceable(8) [[param]]) struct ParamStruct { int i; diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/library/lib_no_alias.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/library/lib_no_alias.hlsl index 04acbee74d..d3e046706c 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/library/lib_no_alias.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/library/lib_no_alias.hlsl @@ -1,7 +1,11 @@ // RUN: %dxc -T lib_6_3 -auto-binding-space 11 -default-linkage external %s | FileCheck %s // Make sure out param has no-alias. -// CHECK: float @"\01?test{{[@$?.A-Za-z0-9_]+}}"(float %a, %struct.T* noalias nocapture %t, %class.matrix.float.2.2* noalias nocapture dereferenceable(16) %m, float %b) +// CHECK: float @"\01?test{{[@$?.A-Za-z0-9_]+}}"( +// CHECK-SAME: float %a, +// CHECK-SAME: %struct.T* noalias nocapture dereferenceable(8) %t, +// CHECK-SAME: %class.matrix.float.2.2* noalias nocapture dereferenceable(16) %m, +// CHECK-SAME float %b struct T { float a; diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/library/lib_skip_copy_in.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/library/lib_skip_copy_in.hlsl deleted file mode 100644 index 416a35ca73..0000000000 --- a/tools/clang/test/HLSLFileCheck/shader_targets/library/lib_skip_copy_in.hlsl +++ /dev/null @@ -1,34 +0,0 @@ -// RUN: %dxc -T lib_6_3 -DTYPE=float -Od %s | FileCheck %s -check-prefixes=CHECK -// RUN: %dxc -T lib_6_3 -DTYPE=float2x2 -Od %s | FileCheck %s -check-prefixes=CHECK -// RUN: %dxc -T lib_6_3 -DTYPE=float2x2 -Od -Zpr %s | FileCheck %s -check-prefixes=CHECK -// RUN: %dxc -T lib_6_3 -DTYPE=float2x2 -DTYPEMOD=row_major -Od -Zpr %s | FileCheck %s -check-prefixes=CHECK -// These need to copy: -// RUN: %dxc -T lib_6_3 -DTYPE=float2x2 -DTYPEMOD=row_major -Od %s | FileCheck %s -check-prefixes=CHECK,COPY -// RUN: %dxc -T lib_6_3 -DTYPE=float2x2 -DTYPEMOD=column_major -Od -Zpr %s | FileCheck %s -check-prefixes=CHECK,COPY - -// Make sure array is not copied unless matrix orientation changed -// CHECK: alloca -// COPY: alloca -// CHECK-NOT: alloca -// CHECK: ret - -#ifndef TYPEMOD -#define TYPEMOD -#endif - -TYPE lookup(in TYPE arr[16], int index) { - return arr[index]; -} - -int idx; - -[shader("vertex")] -TYPE main() : OUT { - TYPEMOD TYPE arr[16]; - for (int i = 0; i < 16; i++) { - arr[i] = (TYPE)i; - } - return lookup(arr, idx); -} - - diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_anyhit.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_anyhit.hlsl index 52189aefb2..0236f31fb4 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_anyhit.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_anyhit.hlsl @@ -1,6 +1,6 @@ // RUN: %dxc -T lib_6_3 -auto-binding-space 11 %s | FileCheck %s -// CHECK: define void [[anyhit1:@"\\01\?anyhit1@[^\"]+"]](%struct.MyPayload* noalias nocapture %payload, %struct.MyAttributes* nocapture readonly %attr) #0 { +// CHECK: define void [[anyhit1:@"\\01\?anyhit1@[^\"]+"]]( // CHECK: call float @dx.op.objectRayOrigin.f32(i32 149, i8 2) // CHECK: call float @dx.op.objectRayDirection.f32(i32 150, i8 2) // CHECK: call float @dx.op.rayTCurrent.f32(i32 154) diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_anyhit_geometryIndex.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_anyhit_geometryIndex.hlsl index 2cb2ab4920..129009fb41 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_anyhit_geometryIndex.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_anyhit_geometryIndex.hlsl @@ -1,6 +1,6 @@ // RUN: %dxc -T lib_6_5 -auto-binding-space 11 %s | FileCheck %s -// CHECK: define void [[anyhit1:@"\\01\?anyhit1@[^\"]+"]](%struct.MyPayload* noalias nocapture %payload, %struct.MyAttributes* nocapture readonly %attr) #0 { +// CHECK: define void [[anyhit1:@"\\01\?anyhit1@[^\"]+"]]( // CHECK: call float @dx.op.objectRayOrigin.f32(i32 149, i8 2) // CHECK: call float @dx.op.objectRayDirection.f32(i32 150, i8 2) // CHECK: call float @dx.op.rayTCurrent.f32(i32 154) diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_callable.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_callable.hlsl index 298dc20c12..1f4b859ec5 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_callable.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_callable.hlsl @@ -6,11 +6,11 @@ // CHECK:@"\01?T{{[@$?.A-Za-z0-9_]+}}" = external constant %"class.Texture2D >", align 4 // CHECK:@"\01?S{{[@$?.A-Za-z0-9_]+}}" = external constant %struct.SamplerState, align 4 -// CHECK: define void [[callable1:@"\\01\?callable1@[^\"]+"]](%struct.MyParam* noalias nocapture %param) #0 { -// CHECK: %[[i_0:[0-9]+]] = load %struct.SamplerState, %struct.SamplerState* @"\01?S{{[@$?.A-Za-z0-9_]+}}", align 4 -// CHECK: %[[i_1:[0-9]+]] = load %"class.Texture2D >", %"class.Texture2D >"* @"\01?T{{[@$?.A-Za-z0-9_]+}}", align 4 -// CHECK: %[[i_3:[0-9]+]] = call %dx.types.Handle @"dx.op.createHandleForLib.class.Texture2D >"(i32 160, %"class.Texture2D >" %[[i_1]]) -// CHECK: %[[i_4:[0-9]+]] = call %dx.types.Handle @dx.op.createHandleForLib.struct.SamplerState(i32 160, %struct.SamplerState %[[i_0]]) +// CHECK: define void [[callable1:@"\\01\?callable1@[^\"]+"]]( +// CHECK-DAG: %[[i_0:[0-9]+]] = load %struct.SamplerState, %struct.SamplerState* @"\01?S{{[@$?.A-Za-z0-9_]+}}", align 4 +// CHECK-DAG: %[[i_1:[0-9]+]] = load %"class.Texture2D >", %"class.Texture2D >"* @"\01?T{{[@$?.A-Za-z0-9_]+}}", align 4 +// CHECK-DAG: %[[i_3:[0-9]+]] = call %dx.types.Handle @"dx.op.createHandleForLib.class.Texture2D >"(i32 160, %"class.Texture2D >" %[[i_1]]) +// CHECK-DAG: %[[i_4:[0-9]+]] = call %dx.types.Handle @dx.op.createHandleForLib.struct.SamplerState(i32 160, %struct.SamplerState %[[i_0]]) // CHECK: %[[i_7:[0-9]+]] = call %dx.types.ResRet.f32 @dx.op.sampleLevel.f32(i32 62, %dx.types.Handle %[[i_3]], %dx.types.Handle %[[i_4]], float %[[i_5:[0-9]+]], float %[[i_6:[0-9]+]], float undef, float undef, i32 0, i32 0, i32 undef, float 0.000000e+00) // CHECK: ret void diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_closesthit.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_closesthit.hlsl index 32dac74e2d..43a893b567 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_closesthit.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_closesthit.hlsl @@ -1,6 +1,6 @@ // RUN: %dxc -T lib_6_3 -auto-binding-space 11 %s | FileCheck %s -// CHECK: define void [[closesthit1:@"\\01\?closesthit1@[^\"]+"]](%struct.MyPayload* noalias nocapture %payload, %struct.BuiltInTriangleIntersectionAttributes* nocapture readonly %attr) #0 { +// CHECK: define void [[closesthit1:@"\\01\?closesthit1@[^\"]+"]]( // CHECK: call void @dx.op.callShader.struct.MyParam(i32 159, i32 {{.*}}, %struct.MyParam* nonnull {{.*}}) // CHECK: %[[color:[^ ]+]] = getelementptr inbounds %struct.MyPayload, %struct.MyPayload* %payload, i32 0, i32 0 // CHECK: store <4 x float> {{.*}}, <4 x float>* %[[color]], align 4 diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_closesthit_geometryIndex.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_closesthit_geometryIndex.hlsl index d5a46610ed..f2dcd0a144 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_closesthit_geometryIndex.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_closesthit_geometryIndex.hlsl @@ -1,6 +1,6 @@ // RUN: %dxc -T lib_6_5 -auto-binding-space 11 %s | FileCheck %s -// CHECK: define void [[closesthit1:@"\\01\?closesthit1@[^\"]+"]](%struct.MyPayload* noalias nocapture %payload, %struct.BuiltInTriangleIntersectionAttributes* nocapture readonly %attr) #0 { +// CHECK: define void [[closesthit1:@"\\01\?closesthit1@[^\"]+"]]( // CHECK: [[GeometryIndex:%[^ ]+]] = call i32 @dx.op.geometryIndex.i32(i32 213) // CHECK: icmp eq i32 [[GeometryIndex]], 0 // CHECK: call void @dx.op.callShader.struct.MyParam(i32 159, i32 {{.*}}, %struct.MyParam* nonnull {{.*}}) diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_intersection_geometryIndex.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_intersection_geometryIndex.hlsl index 12df1ecbcf..d29a09cc9b 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_intersection_geometryIndex.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_intersection_geometryIndex.hlsl @@ -1,9 +1,9 @@ // RUN: %dxc -T lib_6_5 -auto-binding-space 11 %s | FileCheck %s // CHECK: define void [[intersection1:@"\\01\?intersection1@[^\"]+"]]() #0 { -// CHECK: [[rayTCurrent:%[^ ]+]] = call float @dx.op.rayTCurrent.f32(i32 154) -// CHECK: [[GeometryIndex:%[^ ]+]] = call i32 @dx.op.geometryIndex.i32(i32 213) -// CHECK: icmp eq i32 [[GeometryIndex]], 0 +// CHECK-DAG: [[rayTCurrent:%[^ ]+]] = call float @dx.op.rayTCurrent.f32(i32 154) +// CHECK-DAG: [[GeometryIndex:%[^ ]+]] = call i32 @dx.op.geometryIndex.i32(i32 213) +// CHECK-DAG: icmp eq i32 [[GeometryIndex]], 0 // CHECK: call i1 @dx.op.reportHit.struct.MyAttributes(i32 158, float [[rayTCurrent]], i32 0, %struct.MyAttributes* nonnull {{.*}}) // CHECK: ret void diff --git a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_miss.hlsl b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_miss.hlsl index f1f3f81867..f6bbf6d3f8 100644 --- a/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_miss.hlsl +++ b/tools/clang/test/HLSLFileCheck/shader_targets/raytracing/raytracing_miss.hlsl @@ -1,6 +1,6 @@ // RUN: %dxc -T lib_6_3 -auto-binding-space 11 %s | FileCheck %s -// CHECK: define void [[miss1:@"\\01\?miss1@[^\"]+"]](%struct.MyPayload* noalias nocapture %payload) #0 { +// CHECK: define void [[miss1:@"\\01\?miss1@[^\"]+"]]( // CHECK: %[[result:[^ ]+]] = getelementptr inbounds %struct.MyPayload, %struct.MyPayload* %payload, i32 0, i32 0 // CHECK: store <4 x float> , <4 x float>* %[[result]], align 4 // CHECK: ret void diff --git a/tools/clang/unittests/HLSL/VerifierTest.cpp b/tools/clang/unittests/HLSL/VerifierTest.cpp index e8c0301956..f1d1aa7116 100644 --- a/tools/clang/unittests/HLSL/VerifierTest.cpp +++ b/tools/clang/unittests/HLSL/VerifierTest.cpp @@ -55,6 +55,7 @@ class VerifierTest TEST_CLASS_DERIVATION { TEST_METHOD(RunFunctions) TEST_METHOD(RunIncompleteType) TEST_METHOD(RunIndexingOperator) + TEST_METHOD(RunInOutTrunc) TEST_METHOD(RunIntrinsicExamples) TEST_METHOD(RunInvalidDeclTemplateArg) TEST_METHOD(RunMatrixAssignments) @@ -112,6 +113,7 @@ class VerifierTest TEST_CLASS_DERIVATION { TEST_METHOD(RunBitFieldAnnotations) TEST_METHOD(RunUDTByteAddressBufferLoad) TEST_METHOD(RunObjectTemplateDiagDeferred) + TEST_METHOD(RunVectorMatrixBinOp) void CheckVerifies(const wchar_t* path) { WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures); const char startMarker[] = "%clang_cc1"; @@ -266,6 +268,10 @@ TEST_F(VerifierTest, RunIndexingOperator) { CheckVerifiesHLSL(L"indexing-operator.hlsl"); } +TEST_F(VerifierTest, RunInOutTrunc) { + CheckVerifiesHLSL(L"inout_trunc.hlsl"); +} + TEST_F(VerifierTest, RunIntrinsicExamples) { CheckVerifiesHLSL(L"intrinsic-examples.hlsl"); } @@ -493,3 +499,7 @@ TEST_F(VerifierTest, RunUDTByteAddressBufferLoad) { TEST_F(VerifierTest, RunObjectTemplateDiagDeferred) { CheckVerifiesHLSL(L"object-template-diag-deferred.hlsl"); } + +TEST_F(VerifierTest, RunVectorMatrixBinOp) { + CheckVerifiesHLSL(L"vector-matrix-binops.hlsl"); +} diff --git a/utils/hct/gen_intrin_main.txt b/utils/hct/gen_intrin_main.txt index 8903d8191b..a005a8c4a0 100644 --- a/utils/hct/gen_intrin_main.txt +++ b/utils/hct/gen_intrin_main.txt @@ -356,7 +356,7 @@ p32u8 [[rn]] pack_clamp_u8(in sint16or32_only<4> v); void [[]] SetMeshOutputCounts(in uint numVertices, in uint numPrimitives); // Amplification shader intrinsics: -void [[]] DispatchMesh(in uint threadGroupCountX, in uint threadGroupCountY, in uint threadGroupCountZ, in udt meshPayload); +void [[]] DispatchMesh(in uint threadGroupCountX, in uint threadGroupCountY, in uint threadGroupCountZ, ref udt meshPayload); // Return true if the current lane is a helper lane bool [[ro]] IsHelperLane(); diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index 3cf8d8576c..ab295f398f 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -2147,7 +2147,6 @@ def add_pass(name, type_name, doc, opts): add_pass('hlsl-hlemit', 'HLEmitMetadata', 'HLSL High-Level Metadata Emit.', []) add_pass("hl-expand-store-intrinsics", "HLExpandStoreIntrinsics", "Expand HLSL store intrinsics", []) - add_pass("hl-legalize-parameter", "HLLegalizeParameter", "Legalize parameter", []) add_pass('scalarrepl-param-hlsl', 'SROA_Parameter_HLSL', 'Scalar Replacement of Aggregates HLSL (parameters)', []) add_pass('static-global-to-alloca', 'LowerStaticGlobalIntoAlloca', 'Lower static global into Alloca', []) add_pass('hlmatrixlower', 'HLMatrixLowerPass', 'HLSL High-Level Matrix Lower', []) From 25ed34a04ffb07de9457336ca49b23eb8792c763 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Thu, 15 Jun 2023 20:29:09 -0500 Subject: [PATCH 2/3] [SPIR-V] Fix pointer array decay --- tools/clang/lib/SPIRV/SpirvEmitter.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 2642c25f83..3f1f3cd6f2 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -7829,14 +7829,16 @@ const Expr *SpirvEmitter::collectArrayStructIndices( // `-ImplicitCastExpr 'const T' lvalue // `-ArraySubscriptExpr 'ConstantBuffer':'ConstantBuffer' lvalue if (auto *castExpr = dyn_cast(expr)) { - if (castExpr->getCastKind() == CK_FlatConversion || - castExpr->getCastKind() == CK_ArrayToPointerDecay) { - const auto *subExpr = castExpr->getSubExpr(); + const auto *subExpr = castExpr->getSubExpr(); + if (castExpr->getCastKind() == CK_FlatConversion) { const QualType subExprType = subExpr->getType(); if (isConstantTextureBuffer(subExprType)) { return collectArrayStructIndices(subExpr, rawIndex, rawIndices, indices, isMSOutAttribute); } + } else if (castExpr->getCastKind() == CK_ArrayToPointerDecay) { + return collectArrayStructIndices(subExpr, rawIndex, rawIndices, + indices, isMSOutAttribute); } } } From caa269565f28ff4a1ec9a1f103fb2e9a73b58677 Mon Sep 17 00:00:00 2001 From: Chris Bieneman Date: Thu, 15 Jun 2023 20:38:25 -0500 Subject: [PATCH 3/3] Need to get non-reference types --- tools/clang/lib/SPIRV/SpirvEmitter.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 3f1f3cd6f2..f45e7ee99c 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -7360,7 +7360,7 @@ SpirvInstruction *SpirvEmitter::tryToAssignToMSOutAttrsOrIndices( assignToMSOutIndices(varDecl, rhs, indices); } else { assert(isMSOutAttributeBlock); - QualType type = varDecl->getType(); + QualType type = varDecl->getType().getNonReferenceType(); assert(isa(type)); type = astContext.getAsConstantArrayType(type)->getElementType(); assert(type->isStructureType()); @@ -7425,7 +7425,8 @@ void SpirvEmitter::assignToMSOutIndices( vecComponent = indices.back(); } auto *var = declIdMapper.getStageVarInstruction(decl); - const auto *varTypeDecl = astContext.getAsConstantArrayType(decl->getType()); + QualType declType = decl->getType().getNonReferenceType(); + const auto *varTypeDecl = astContext.getAsConstantArrayType(declType); QualType varType = varTypeDecl->getElementType(); uint32_t numVertices = 1; if (!isVectorType(varType, nullptr, &numVertices)) {