Author: Sarah Spall Date: 2024-12-03T17:43:36-08:00 New Revision: 46de3a7064250bd2dfc7f8dc6e300474afa9fa97
URL: https://github.com/llvm/llvm-project/commit/46de3a7064250bd2dfc7f8dc6e300474afa9fa97 DIFF: https://github.com/llvm/llvm-project/commit/46de3a7064250bd2dfc7f8dc6e300474afa9fa97.diff LOG: [HLSL] get inout/out ABI for array parameters working (#111047) Get inout/out parameters working for HLSL Arrays. Utilizes the fix from #109323, and corrects the assignment behavior slightly to allow for Non-LValues on the RHS. Closes #106917 --------- Co-authored-by: Chris B <be...@abolishcrlf.org> Added: clang/test/AST/HLSL/ArrayOutArgExpr.hlsl clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl clang/test/SemaHLSL/Language/ArrayOutputArgs-errors.hlsl Modified: clang/include/clang/AST/Type.h clang/lib/AST/Type.cpp clang/lib/CodeGen/CGCall.cpp clang/lib/CodeGen/CGExpr.cpp clang/lib/Sema/Sema.cpp clang/lib/Sema/SemaExprCXX.cpp clang/lib/Sema/SemaOverload.cpp clang/lib/Sema/SemaType.cpp clang/test/CodeGenHLSL/ArrayAssignable.hlsl clang/test/SemaHLSL/ArrayTemporary.hlsl Removed: ################################################################################ diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 90a52b1dcbf624..6fd6c73a516f08 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -3754,6 +3754,8 @@ class ArrayParameterType : public ConstantArrayType { static bool classof(const Type *T) { return T->getTypeClass() == ArrayParameter; } + + QualType getConstantArrayType(const ASTContext &Ctx) const; }; /// Represents a C array with an unspecified size. For example 'int A[]' has diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 366bcc3216b3fd..976361d07b68bf 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -267,6 +267,12 @@ void ConstantArrayType::Profile(llvm::FoldingSetNodeID &ID, SizeExpr->Profile(ID, Context, true); } +QualType ArrayParameterType::getConstantArrayType(const ASTContext &Ctx) const { + return Ctx.getConstantArrayType(getElementType(), getSize(), getSizeExpr(), + getSizeModifier(), + getIndexTypeQualifiers().getAsOpaqueValue()); +} + DependentSizedArrayType::DependentSizedArrayType(QualType et, QualType can, Expr *e, ArraySizeModifier sm, unsigned tq, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 28a5526fbea068..7c8d962fa5a920 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4725,15 +4725,17 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, return emitWritebackArg(*this, args, CRE); } - assert(type->isReferenceType() == E->isGLValue() && - "reference binding to unmaterialized r-value!"); - // Add writeback for HLSLOutParamExpr. + // Needs to be before the assert below because HLSLOutArgExpr is an LValue + // and is not a reference. if (const HLSLOutArgExpr *OE = dyn_cast<HLSLOutArgExpr>(E)) { EmitHLSLOutArgExpr(OE, args, type); return; } + assert(type->isReferenceType() == E->isGLValue() && + "reference binding to unmaterialized r-value!"); + if (E->isGLValue()) { assert(E->getObjectKind() == OK_Ordinary); return args.add(EmitReferenceBindingToExpr(E), type); @@ -5322,6 +5324,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, IRCallArgs[FirstIRArg] = Val; break; } + } else if (I->getType()->isArrayParameterType()) { + // Don't produce a temporary for ArrayParameterType arguments. + // ArrayParameterType arguments are only created from + // HLSL_ArrayRValue casts and HLSLOutArgExpr expressions, both + // of which create temporaries already. This allows us to just use the + // scalar for the decayed array pointer as the argument directly. + IRCallArgs[FirstIRArg] = I->getKnownRValue().getScalarVal(); + break; } // For non-aggregate args and aggregate args meeting conditions above diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 229f0e29f02341..5fccc9cbb37ec1 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5827,9 +5827,12 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { // This function implements trivial copy assignment for HLSL's // assignable constant arrays. LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) { - LValue TrivialAssignmentRHS = EmitLValue(E->getRHS()); + // Don't emit an LValue for the RHS because it might not be an LValue LValue LHS = EmitLValue(E->getLHS()); - EmitAggregateAssign(LHS, TrivialAssignmentRHS, E->getLHS()->getType()); + // In C the RHS of an assignment operator is an RValue. + // EmitAggregateAssign takes anan LValue for the RHS. Instead we can call + // EmitInitializationToLValue to emit an RValue into an LValue. + EmitInitializationToLValue(E->getRHS(), LHS); return LHS; } diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 942e7ece4283e3..d6517511d7db4d 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -723,6 +723,15 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, QualType ExprTy = Context.getCanonicalType(E->getType()); QualType TypeTy = Context.getCanonicalType(Ty); + // This cast is used in place of a regular LValue to RValue cast for + // HLSL Array Parameter Types. It needs to be emitted even if + // ExprTy == TypeTy, except if E is an HLSLOutArgExpr + // Emitting a cast in that case will prevent HLSLOutArgExpr from + // being handled properly in EmitCallArg + if (Kind == CK_HLSLArrayRValue && !isa<HLSLOutArgExpr>(E)) + return ImplicitCastExpr::Create(Context, Ty, Kind, E, BasePath, VK, + CurFPFeatureOverrides()); + if (ExprTy == TypeTy) return E; diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index d85819b21c8265..f58c0fa21e8380 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4431,10 +4431,21 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, break; case ICK_HLSL_Array_RValue: - FromType = Context.getArrayParameterType(FromType); - From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue, - /*BasePath=*/nullptr, CCK) - .get(); + if (ToType->isArrayParameterType()) { + FromType = Context.getArrayParameterType(FromType); + From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue, + /*BasePath=*/nullptr, CCK) + .get(); + } else { // FromType must be ArrayParameterType + assert(FromType->isArrayParameterType() && + "FromType must be ArrayParameterType in ICK_HLSL_Array_RValue \ + if it is not ToType"); + const ArrayParameterType *APT = cast<ArrayParameterType>(FromType); + FromType = APT->getConstantArrayType(Context); + From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue, + /*BasePath=*/nullptr, CCK) + .get(); + } break; case ICK_Function_To_Pointer: diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 4c9e37bd286dee..c174922a926fc6 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2236,33 +2236,24 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, return false; } } - // Lvalue-to-rvalue conversion (C++11 4.1): - // A glvalue (3.10) of a non-function, non-array type T can - // be converted to a prvalue. - bool argIsLValue = From->isGLValue(); - if (argIsLValue && !FromType->canDecayToPointerType() && - S.Context.getCanonicalType(FromType) != S.Context.OverloadTy) { - SCS.First = ICK_Lvalue_To_Rvalue; - - // C11 6.3.2.1p2: - // ... if the lvalue has atomic type, the value has the non-atomic version - // of the type of the lvalue ... - if (const AtomicType *Atomic = FromType->getAs<AtomicType>()) - FromType = Atomic->getValueType(); - // If T is a non-class type, the type of the rvalue is the - // cv-unqualified version of T. Otherwise, the type of the rvalue - // is T (C++ 4.1p1). C++ can't get here with class types; in C, we - // just strip the qualifiers because they don't matter. - FromType = FromType.getUnqualifiedType(); - } else if (S.getLangOpts().HLSL && FromType->isConstantArrayType() && - ToType->isConstantArrayType()) { + bool argIsLValue = From->isGLValue(); + // To handle conversion from ArrayParameterType to ConstantArrayType + // this block must be above the one below because Array parameters + // do not decay and when handling HLSLOutArgExprs and + // the From expression is an LValue. + if (S.getLangOpts().HLSL && FromType->isConstantArrayType() && + ToType->isConstantArrayType()) { // HLSL constant array parameters do not decay, so if the argument is a // constant array and the parameter is an ArrayParameterType we have special // handling here. if (ToType->isArrayParameterType()) { FromType = S.Context.getArrayParameterType(FromType); SCS.First = ICK_HLSL_Array_RValue; + } else if (FromType->isArrayParameterType()) { + const ArrayParameterType *APT = cast<ArrayParameterType>(FromType); + FromType = APT->getConstantArrayType(S.Context); + SCS.First = ICK_HLSL_Array_RValue; } else { SCS.First = ICK_Identity; } @@ -2273,6 +2264,25 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, SCS.setAllToTypes(ToType); return true; + } else if (argIsLValue && !FromType->canDecayToPointerType() && + S.Context.getCanonicalType(FromType) != S.Context.OverloadTy) { + // Lvalue-to-rvalue conversion (C++11 4.1): + // A glvalue (3.10) of a non-function, non-array type T can + // be converted to a prvalue. + + SCS.First = ICK_Lvalue_To_Rvalue; + + // C11 6.3.2.1p2: + // ... if the lvalue has atomic type, the value has the non-atomic version + // of the type of the lvalue ... + if (const AtomicType *Atomic = FromType->getAs<AtomicType>()) + FromType = Atomic->getValueType(); + + // If T is a non-class type, the type of the rvalue is the + // cv-unqualified version of T. Otherwise, the type of the rvalue + // is T (C++ 4.1p1). C++ can't get here with class types; in C, we + // just strip the qualifiers because they don't matter. + FromType = FromType.getUnqualifiedType(); } else if (FromType->isArrayType()) { // Array-to-pointer conversion (C++ 4.2) SCS.First = ICK_Array_To_Pointer; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index f32edc5ac06440..5fb936297aa54a 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -5681,6 +5681,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, assert(!T.isNull() && "T must not be null at the end of this function"); if (!AreDeclaratorChunksValid) return Context.getTrivialTypeSourceInfo(T); + + if (state.didParseHLSLParamMod() && !T->isConstantArrayType()) + T = S.HLSL().getInoutParameterType(T); return GetTypeSourceInfoForDeclarator(state, T, TInfo); } @@ -8634,7 +8637,6 @@ static void HandleHLSLParamModifierAttr(TypeProcessingState &State, return; if (Attr.getSemanticSpelling() == HLSLParamModifierAttr::Keyword_inout || Attr.getSemanticSpelling() == HLSLParamModifierAttr::Keyword_out) { - CurType = S.HLSL().getInoutParameterType(CurType); State.setParsedHLSLParamMod(true); } } diff --git a/clang/test/AST/HLSL/ArrayOutArgExpr.hlsl b/clang/test/AST/HLSL/ArrayOutArgExpr.hlsl new file mode 100644 index 00000000000000..10825bf0f93bc7 --- /dev/null +++ b/clang/test/AST/HLSL/ArrayOutArgExpr.hlsl @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump %s | FileCheck %s + +// CHECK-LABEL: increment +void increment(inout int Arr[2]) { + for (int I = 0; I < 2; I++) + Arr[0] += 2; +} + +// CHECK-LABEL: call +// CHECK: CallExpr 0x{{.*}} {{.*}} 'void' +// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'void (*)(inout int[2])' <FunctionToPointerDecay> +// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'void (inout int[2])' lvalue Function 0x{{.*}} 'increment' 'void (inout int[2])' +// CHECK: HLSLOutArgExpr 0x{{.*}} {{.*}} 'int[2]' lvalue inout +// CHECK: OpaqueValueExpr [[A:0x.*]] {{.*}} 'int[2]' lvalue +// CHECK: DeclRefExpr [[B:0x.*]] {{.*}} 'int[2]' lvalue Var [[E:0x.*]] 'A' 'int[2]' +// CHECK: OpaqueValueExpr [[C:0x.*]] {{.*}} 'int[2]' lvalue +// CHECK: ImplicitCastExpr [[D:0x.*]] {{.*}} 'int[2]' <HLSLArrayRValue> +// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue +// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]' +// CHECK: BinaryOperator 0x{{.*}} {{.*}} 'int[2]' lvalue '=' +// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue +// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]' +// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'int[2]' <HLSLArrayRValue> +// CHECK: OpaqueValueExpr [[C]] {{.*}} 'int[2]' lvalue +// CHECK: ImplicitCastExpr [[D]] {{.*}} 'int[2]' <HLSLArrayRValue> +// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue +// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]' +export int call() { + int A[2] = { 0, 1 }; + increment(A); + return A[0]; +} + +// CHECK-LABEL: fn2 +void fn2(out int Arr[2]) { + Arr[0] += 5; + Arr[1] += 6; +} + +// CHECK-LABEL: call2 +// CHECK: CallExpr 0x{{.*}} {{.*}} 'void' +// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'void (*)(out int[2])' <FunctionToPointerDecay> +// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'void (out int[2])' lvalue Function 0x{{.*}} 'fn2' 'void (out int[2])' +// CHECK: HLSLOutArgExpr 0x{{.*}} {{.*}} 'int[2]' lvalue out +// CHECK: OpaqueValueExpr [[A:0x.*]] {{.*}} 'int[2]' lvalue +// CHECK: DeclRefExpr [[B:0x.*]] {{.*}} 'int[2]' lvalue Var [[E:0x.*]] 'A' 'int[2]' +// CHECK: OpaqueValueExpr [[C:0x.*]] {{.*}} 'int[2]' lvalue +// CHECK: ImplicitCastExpr [[D:0x.*]] {{.*}} 'int[2]' <HLSLArrayRValue> +// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue +// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]' +// CHECK: BinaryOperator 0x{{.*}} {{.*}} 'int[2]' lvalue '=' +// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue +// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]' +// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'int[2]' <HLSLArrayRValue> +// CHECK: OpaqueValueExpr [[C]] {{.*}} 'int[2]' lvalue +// CHECK: ImplicitCastExpr [[D]] {{.*}} 'int[2]' <HLSLArrayRValue> +// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue +// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]' +export int call2() { + int A[2] = { 0, 1 }; + fn2(A); + return 1; +} diff --git a/clang/test/CodeGenHLSL/ArrayAssignable.hlsl b/clang/test/CodeGenHLSL/ArrayAssignable.hlsl index a0dfe26e5d147b..e2ff2de68ed990 100644 --- a/clang/test/CodeGenHLSL/ArrayAssignable.hlsl +++ b/clang/test/CodeGenHLSL/ArrayAssignable.hlsl @@ -100,18 +100,16 @@ void arr_assign6() { } // CHECK-LABEL: define void {{.*}}arr_assign7 -// CHECK: [[Arr3:%.*]] = alloca [2 x [2 x i32]], align 4 -// CHECK-NEXT: [[Arr4:%.*]] = alloca [2 x [2 x i32]], align 4 -// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK: [[Arr:%.*]] = alloca [2 x [2 x i32]], align 4 +// CHECK-NEXT: [[Arr2:%.*]] = alloca [2 x [2 x i32]], align 4 // CHECK-NOT: alloca -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 {{@.*}}, i32 16, i1 false) -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr4]], ptr align 4 {{@.*}}, i32 16, i1 false) -// CHECK-NEXT: store i32 6, ptr [[Tmp]], align 4 -// CHECK-NEXT: [[AIE:%.*]] = getelementptr inbounds i32, ptr [[Tmp]], i32 1 -// CHECK-NEXT: store i32 6, ptr [[AIE]], align 4 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 [[Arr4]], i32 16, i1 false) -// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[Arr3]], i32 0, i32 0 -// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Idx]], ptr align 4 [[Tmp]], i32 8, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{@.*}}, i32 16, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr2]], ptr align 4 {{@.*}}, i32 16, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 [[Arr2]], i32 16, i1 false) +// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[Arr]], i32 0, i32 0 +// CHECK-NEXT: store i32 6, ptr [[Idx]], align 4 +// CHECK-NEXT: [[Idx2:%.*]] = getelementptr inbounds i32, ptr %arrayidx, i32 1 +// CHECK-NEXT: store i32 6, ptr [[Idx2]], align 4 // CHECK-NEXT: ret void void arr_assign7() { int Arr[2][2] = {{0, 1}, {2, 3}}; diff --git a/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl new file mode 100644 index 00000000000000..eb7d755bca61df --- /dev/null +++ b/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl @@ -0,0 +1,122 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -disable-llvm-passes -emit-llvm -finclude-default-header -o - %s | FileCheck %s + +// CHECK-LABEL: increment +void increment(inout int Arr[2]) { + for (int I = 0; I < 2; I++) + Arr[0] += 2; +} + +// CHECK-LABEL: arrayCall +// CHECK: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[A]], i32 8, i1 false) +// CHECK-NEXT: call void @{{.*}}increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp]]) #3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false) +// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4 +// CHECK-NEXT: ret i32 [[B]] +export int arrayCall() { + int A[2] = { 0, 1 }; + increment(A); + return A[0]; +} + +// CHECK-LABEL: fn2 +void fn2(out int Arr[2]) { + Arr[0] += 5; + Arr[1] += 6; +} + +// CHECK-LABEL: arrayCall2 +// CHECK: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false) +// CHECK-NEXT: call void @{{.*}}fn2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp]]) #3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false) +// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4 +// CHECK-NEXT: ret i32 [[B]] +export int arrayCall2() { + int A[2] = { 0, 1 }; + fn2(A); + return A[0]; +} + +// CHECK-LABEL: nestedCall +void nestedCall(inout int Arr[2], uint index) { + if (index < 2) { + Arr[index] += 2; + nestedCall(Arr, index+1); + } +} + +// CHECK-LABEL: arrayCall3 +// CHECK: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[A]], i32 8, i1 false) +// CHECK-NEXT: call void @{{.*}}nestedCall{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp]], i32 noundef 0) #3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false) +// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 1 +// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4 +// CHECK-NEXt: ret i32 [[B]] +export int arrayCall3() { + int A[2] = { 0, 1 }; + nestedCall(A, 0); + return A[1]; +} + +// CHECK-LABEL: outerCall +// CHECK: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 %{{.*}}, i32 8, i1 false) +// CHECK-NEXT: call void {{.*}}increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp]]) #3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 {{.*}}, ptr align 4 [[Tmp]], i32 8, i1 false) +// CHECK-NEXT: ret void +void outerCall(inout int Arr[2]) { + increment(Arr); +} + +// CHECK-LABEL: arrayCall4 +// CHECK: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[A]], i32 8, i1 false) +// CHECK-NEXT: call void @{{.*}}outerCall{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp]]) #3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false) +// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4 +// CHECK-NEXT: ret i32 [[B]] +export int arrayCall4() { + int A[2] = { 0, 1 }; + outerCall(A); + return A[0]; +} + +// CHECK-LABEL: fn3 +void fn3(int Arr[2]) {} + +// CHECK-LABEL: outerCall2 +// CHECK: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 {{.*}}, i32 8, i1 false) +// CHECK-NEXT: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x i32]) align 4 [[Tmp]]) #3 +// CHECK-NEXT: ret void +void outerCall2(inout int Arr[2]) { + fn3(Arr); +} + +// CHECK-LABEL: arrayCall5 +// CHECK: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false) +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[A]], i32 8, i1 false) +// CHECK-NEXT: call void @{{.*}}outerCall2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp]]) #3 +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false) +// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 0 +// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4 +// CHECK-NEXT: ret i32 [[B]] +export int arrayCall5() { + int A[2] = { 0, 1 }; + outerCall2(A); + return A[0]; +} diff --git a/clang/test/SemaHLSL/ArrayTemporary.hlsl b/clang/test/SemaHLSL/ArrayTemporary.hlsl index dff9aff7d9b299..0266a198e7ec98 100644 --- a/clang/test/SemaHLSL/ArrayTemporary.hlsl +++ b/clang/test/SemaHLSL/ArrayTemporary.hlsl @@ -75,17 +75,17 @@ void template_fn(T Val) {} // CHECK: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2])' <FunctionToPointerDecay> // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2])' lvalue Function {{.*}} 'template_fn' 'void (float[2])' (FunctionTemplate {{.*}} 'template_fn') -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <LValueToRValue> +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <HLSLArrayRValue> // CHECK-NEXT: DeclRefExpr {{.*}} 'float[2]' lvalue ParmVar {{.*}} 'FA2' 'float[2]' // CHECK-NEXT: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[4])' <FunctionToPointerDecay> // CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[4])' lvalue Function {{.*}} 'template_fn' 'void (float[4])' (FunctionTemplate {{.*}} 'template_fn') -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[4]' <LValueToRValue> +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[4]' <HLSLArrayRValue> // CHECK-NEXT: DeclRefExpr {{.*}} 'float[4]' lvalue ParmVar {{.*}} 'FA4' 'float[4]' // CHECK-NEXT: CallExpr {{.*}} 'void' // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int[3])' <FunctionToPointerDecay> // CHECK-NEXT: DeclRefExpr {{.*}} 'void (int[3])' lvalue Function {{.*}} 'template_fn' 'void (int[3])' (FunctionTemplate {{.*}} 'template_fn') -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int[3]' <LValueToRValue> +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int[3]' <HLSLArrayRValue> // CHECK-NEXT: DeclRefExpr {{.*}} 'int[3]' lvalue ParmVar {{.*}} 'IA3' 'int[3]' void call(float FA2[2], float FA4[4], int IA3[3]) { diff --git a/clang/test/SemaHLSL/Language/ArrayOutputArgs-errors.hlsl b/clang/test/SemaHLSL/Language/ArrayOutputArgs-errors.hlsl new file mode 100644 index 00000000000000..46bed0d5a7cbdc --- /dev/null +++ b/clang/test/SemaHLSL/Language/ArrayOutputArgs-errors.hlsl @@ -0,0 +1,51 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -verify + +void increment(inout int Arr[2]) { + for (int I = 0; I < 2; I++) + Arr[0] += 2; +} + +export int wrongSize() { + int A[3] = { 0, 1, 2 }; + increment(A); + // expected-error@-1 {{no matching function for call to 'increment'}} + // expected-note@*:* {{candidate function not viable: no known conversion from 'int[3]' to 'int[2]' for 1st argument}} + return A[0]; +} + +export int wrongSize2() { + int A[1] = { 0 }; + increment(A); + // expected-error@-1 {{no matching function for call to 'increment'}} + // expected-note@*:* {{candidate function not viable: no known conversion from 'int[1]' to 'int[2]' for 1st argument}} + return A[0]; +} + +export void tooFewArgs() { + increment(); + // expected-error@-1 {{no matching function for call to 'increment'}} + // expected-note@*:* {{candidate function not viable: requires single argument 'Arr', but no arguments were provided}} +} + +export float wrongType() { + float A[2] = { 0, 1 }; + increment(A); + // expected-error@-1 {{no matching function for call to 'increment'}} + // expected-note@*:* {{candidate function not viable: no known conversion from 'float[2]' to 'int[2]' for 1st argument}} + return A[0]; +} + +export int wrongType2() { + increment(5); + // expected-error@-1 {{no matching function for call to 'increment'}} + // expected-note@*:* {{candidate function not viable: no known conversion from 'int' to 'int[2]' for 1st argument}} + return 1; +} + +export void tooManyArgs() { + int A[2] = { 0, 1 }; + int B[2] = { 2, 3 }; + increment(A, B); + // expected-error@-1 {{no matching function for call to 'increment'}} + // expected-note@*:* {{candidate function not viable: requires single argument 'Arr', but 2 arguments were provided}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits