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..f45e7ee99c 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()) @@ -7381,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()); @@ -7446,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)) { @@ -7850,13 +7830,16 @@ const Expr *SpirvEmitter::collectArrayStructIndices( // `-ImplicitCastExpr 'const T' lvalue // `-ArraySubscriptExpr 'ConstantBuffer':'ConstantBuffer' lvalue if (auto *castExpr = dyn_cast(expr)) { + const auto *subExpr = castExpr->getSubExpr(); if (castExpr->getCastKind() == CK_FlatConversion) { - const auto *subExpr = castExpr->getSubExpr(); 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); } } } @@ -11283,8 +11266,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 +11352,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 +12281,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 +12321,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 +12586,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 +12668,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 +13868,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', [])