https://github.com/spall updated https://github.com/llvm/llvm-project/pull/111047
>From 119def060924f13bd1fe07f6d73ce27a1b52ea12 Mon Sep 17 00:00:00 2001 From: Sarah Spall <sp...@planetbauer.com> Date: Tue, 17 Sep 2024 20:25:46 +0000 Subject: [PATCH 1/8] theoretically fix issue --- clang/lib/Sema/SemaType.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index e526a11973975d..4874a4902a5cc6 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -5675,6 +5675,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); } @@ -8616,7 +8619,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); } } >From 3231a7b7327b6550291dfbeeec5a1907a7473e7f Mon Sep 17 00:00:00 2001 From: Sarah Spall <sp...@planetbauer.com> Date: Wed, 2 Oct 2024 21:34:02 +0000 Subject: [PATCH 2/8] get inout working --- clang/include/clang/AST/ASTContext.h | 4 +++ clang/lib/AST/ASTContext.cpp | 10 +++++++ clang/lib/CodeGen/CGCall.cpp | 3 +- clang/lib/CodeGen/CGExpr.cpp | 7 +++-- clang/lib/Sema/SemaExprCXX.cpp | 13 +++++++-- clang/lib/Sema/SemaOverload.cpp | 43 ++++++++++++++++------------ 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 89fcb6789d880a..e9bacf8d7391c4 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1480,6 +1480,10 @@ class ASTContext : public RefCountedBase<ASTContext> { /// type to the decayed type. QualType getDecayedType(QualType Orig, QualType Decayed) const; + /// Return the uniqued reference to a constant array type from the + /// original array parameter type. + QualType getConstantArrayFromArrayParameterType(QualType Ty) const; + /// Return the uniqued reference to a specified array parameter type from the /// original array type. QualType getArrayParameterType(QualType Ty) const; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 09d159e5c3efd6..202d17d3999658 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3852,6 +3852,16 @@ QualType ASTContext::getDecayedType(QualType T) const { return getDecayedType(T, Decayed); } +QualType ASTContext::getConstantArrayFromArrayParameterType(QualType Ty) const { + if (Ty->isConstantArrayType() && !Ty->isArrayParameterType()) + return Ty; + assert(Ty->isArrayParameterType() && "Ty must be an array parameter type."); + const auto *ATy = cast<ArrayParameterType>(Ty); + return getConstantArrayType(ATy->getElementType(), ATy->getSize(), + ATy->getSizeExpr(), ATy->getSizeModifier(), + ATy->getIndexTypeQualifiers().getAsOpaqueValue()); +} + QualType ASTContext::getArrayParameterType(QualType Ty) const { if (Ty->isArrayParameterType()) return Ty; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 8f4f5d3ed81601..0206d2c1360d47 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4726,7 +4726,8 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, return emitWritebackArg(*this, args, CRE); } - assert(type->isReferenceType() == E->isGLValue() && + assert(type->isArrayParameterType() || + (type->isReferenceType() == E->isGLValue()) && "reference binding to unmaterialized r-value!"); // Add writeback for HLSLOutParamExpr. diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 096f4c4f550435..fcab838be40f2b 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5830,9 +5830,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 assignment operator RHS is often an RValue. + // EmitAggregateAssign expects an LValue for the RHS so call the below + // function instead. + EmitInitializationToLValue(E->getRHS(), LHS); return LHS; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index ab9367f911cc51..bb793b90ace59e 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4431,10 +4431,17 @@ 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) + if (ToType->isArrayParameterType()) { + FromType = Context.getArrayParameterType(FromType); + From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue, + /*BasePath=*/nullptr, CCK) .get(); + } else { // FromType must be ArrayParameterType + FromType = Context.getConstantArrayFromArrayParameterType(FromType); + 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 4aeceba128b29b..cbd964919226a7 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2242,26 +2242,9 @@ 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() && + bool argIsLValue = From->isGLValue(); + 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 @@ -2269,6 +2252,9 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, if (ToType->isArrayParameterType()) { FromType = S.Context.getArrayParameterType(FromType); SCS.First = ICK_HLSL_Array_RValue; + } else if (FromType->isArrayParameterType()) { + FromType = S.Context.getConstantArrayFromArrayParameterType(FromType); + SCS.First = ICK_HLSL_Array_RValue; } else { SCS.First = ICK_Identity; } @@ -2279,6 +2265,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; >From 67ca255b8dfd88edd884b95175e9849bf1f9d2b4 Mon Sep 17 00:00:00 2001 From: Sarah Spall <sp...@planetbauer.com> Date: Thu, 3 Oct 2024 14:26:00 +0000 Subject: [PATCH 3/8] fix tests cases broken by changes --- clang/test/CodeGenHLSL/ArrayAssignable.hlsl | 20 +++++++++----------- clang/test/CodeGenHLSL/ArrayTemporary.hlsl | 16 +++++++--------- clang/test/SemaHLSL/ArrayTemporary.hlsl | 3 --- 3 files changed, 16 insertions(+), 23 deletions(-) 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/ArrayTemporary.hlsl b/clang/test/CodeGenHLSL/ArrayTemporary.hlsl index 7d77c0aff736cc..ab0918974e3a41 100644 --- a/clang/test/CodeGenHLSL/ArrayTemporary.hlsl +++ b/clang/test/CodeGenHLSL/ArrayTemporary.hlsl @@ -46,9 +46,8 @@ void call3() { } // CHECK-LABEL: define void {{.*}}call4{{.*}}(ptr -// CHECK-SAME: noundef byval([2 x [2 x float]]) align 4 [[Arr:%.*]]) // CHECK: [[Tmp:%.*]] = alloca [2 x [2 x float]] -// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 16, i1 false) +// CHECK: store ptr {{.*}}, ptr [[Tmp]], align 4 // CHECK: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x [2 x float]]) align 4 [[Tmp]]) void call4(float Arr[2][2]) { @@ -67,12 +66,12 @@ void call4(float Arr[2][2]) { // CHECK: [[Tmp1:%.*]] = alloca [2 x float] // CHECK: [[Tmp2:%.*]] = alloca [4 x float] // CHECK: [[Tmp3:%.*]] = alloca [3 x i32] -// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp1]], ptr align 4 [[FA2]], i32 8, i1 false) -// CHECK: call void @_Z11template_fnIA2_fEvT_(ptr noundef byval([2 x float]) align 4 [[Tmp1]]) -// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp2]], ptr align 4 [[FA4]], i32 16, i1 false) -// CHECK: call void @_Z11template_fnIA4_fEvT_(ptr noundef byval([4 x float]) align 4 [[Tmp2]]) -// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp3]], ptr align 4 [[IA3]], i32 12, i1 false) -// CHECK: call void @_Z11template_fnIA3_iEvT_(ptr noundef byval([3 x i32]) align 4 [[Tmp3]]) +// CHECK: store ptr {{.*}}, ptr [[Tmp1]], align 4 +// CHECK: call void @"??$template_fn@$$BY01M@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 [[Tmp1]]) +// CHECK: store ptr {{.*}}, ptr [[Tmp2]], align 4 +// CHECK: call void @"??$template_fn@$$BY03M@@YAXY03M@Z"(ptr noundef byval([4 x float]) align 4 [[Tmp2]]) +// CHECK: store ptr {{.*}}, ptr [[Tmp3]], align 4 +// CHECK: call void @"??$template_fn@$$BY02H@@YAXY02H@Z"(ptr noundef byval([3 x i32]) align 4 [[Tmp3]]) template<typename T> void template_fn(T Val) {} @@ -83,7 +82,6 @@ void template_call(float FA2[2], float FA4[4], int IA3[3]) { template_fn(IA3); } - // Verify that Array parameter element access correctly codegens. // CHECK-LABEL: define void {{.*}}element_access{{.*}}(ptr // CHECK-SAME: noundef byval([2 x float]) align 4 [[FA2:%[0-9A-Z]+]] diff --git a/clang/test/SemaHLSL/ArrayTemporary.hlsl b/clang/test/SemaHLSL/ArrayTemporary.hlsl index dff9aff7d9b299..f1e446d8d3f8f1 100644 --- a/clang/test/SemaHLSL/ArrayTemporary.hlsl +++ b/clang/test/SemaHLSL/ArrayTemporary.hlsl @@ -75,17 +75,14 @@ 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: 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: 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: DeclRefExpr {{.*}} 'int[3]' lvalue ParmVar {{.*}} 'IA3' 'int[3]' void call(float FA2[2], float FA4[4], int IA3[3]) { >From fe046b9d66cead50d8b06e561fb358d490d551d3 Mon Sep 17 00:00:00 2001 From: Sarah Spall <sp...@planetbauer.com> Date: Thu, 3 Oct 2024 17:01:14 +0000 Subject: [PATCH 4/8] new tests --- clang/test/AST/HLSL/ArrayOutArgExpr.hlsl | 63 +++++++++++++++++++ .../BasicFeatures/ArrayOutputArguments.hlsl | 48 ++++++++++++++ .../Language/ArrayOutputArgs-errors.hlsl | 51 +++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 clang/test/AST/HLSL/ArrayOutArgExpr.hlsl create mode 100644 clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl create mode 100644 clang/test/SemaHLSL/Language/ArrayOutputArgs-errors.hlsl 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/BasicFeatures/ArrayOutputArguments.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl new file mode 100644 index 00000000000000..d4a5cdb9445a7f --- /dev/null +++ b/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl @@ -0,0 +1,48 @@ +// 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: call +// CHECK: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 +// CHECK-NEXT: call void @"?increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #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 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: [[A:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 +// CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 +// CHECK-NEXT: call void @"?fn2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #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 call2() { + int A[2] = { 0, 1 }; + fn2(A); + return A[0]; +} 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}} +} >From cd022bb87401af9ac067b7fd50c173bafe5ace90 Mon Sep 17 00:00:00 2001 From: Sarah Spall <sp...@planetbauer.com> Date: Thu, 3 Oct 2024 20:38:56 +0000 Subject: [PATCH 5/8] fix clang format issues --- clang/lib/AST/ASTContext.cpp | 4 ++-- clang/lib/CodeGen/CGCall.cpp | 4 ++-- clang/lib/Sema/SemaExprCXX.cpp | 6 +++--- clang/lib/Sema/SemaOverload.cpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 202d17d3999658..c0b90cd61107be 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3858,8 +3858,8 @@ QualType ASTContext::getConstantArrayFromArrayParameterType(QualType Ty) const { assert(Ty->isArrayParameterType() && "Ty must be an array parameter type."); const auto *ATy = cast<ArrayParameterType>(Ty); return getConstantArrayType(ATy->getElementType(), ATy->getSize(), - ATy->getSizeExpr(), ATy->getSizeModifier(), - ATy->getIndexTypeQualifiers().getAsOpaqueValue()); + ATy->getSizeExpr(), ATy->getSizeModifier(), + ATy->getIndexTypeQualifiers().getAsOpaqueValue()); } QualType ASTContext::getArrayParameterType(QualType Ty) const { diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 0206d2c1360d47..cc99cf82c04cdb 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4727,8 +4727,8 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, } assert(type->isArrayParameterType() || - (type->isReferenceType() == E->isGLValue()) && - "reference binding to unmaterialized r-value!"); + (type->isReferenceType() == E->isGLValue()) && + "reference binding to unmaterialized r-value!"); // Add writeback for HLSLOutParamExpr. if (const HLSLOutArgExpr *OE = dyn_cast<HLSLOutArgExpr>(E)) { diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index bb793b90ace59e..7cac1beedb539c 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4435,12 +4435,12 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, FromType = Context.getArrayParameterType(FromType); From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue, /*BasePath=*/nullptr, CCK) - .get(); + .get(); } else { // FromType must be ArrayParameterType FromType = Context.getConstantArrayFromArrayParameterType(FromType); From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue, - /*BasePath=*/nullptr, CCK) - .get(); + /*BasePath=*/nullptr, CCK) + .get(); } break; diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index cbd964919226a7..41e8cc3856049a 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2245,7 +2245,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, bool argIsLValue = From->isGLValue(); if (S.getLangOpts().HLSL && FromType->isConstantArrayType() && - ToType->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. @@ -2266,7 +2266,7 @@ 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) { + 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. >From b65b4edaedff3a4777027a04e44c7fe8c5d4d2c9 Mon Sep 17 00:00:00 2001 From: Sarah Spall <sp...@planetbauer.com> Date: Tue, 22 Oct 2024 20:57:21 +0000 Subject: [PATCH 6/8] addressing pr comments --- clang/include/clang/AST/ASTContext.h | 4 - clang/include/clang/AST/Type.h | 2 + clang/lib/AST/ASTContext.cpp | 10 -- clang/lib/AST/Type.cpp | 6 ++ clang/lib/CodeGen/CGCall.cpp | 9 +- clang/lib/CodeGen/CGExpr.cpp | 6 +- clang/lib/Sema/Sema.cpp | 9 ++ clang/lib/Sema/SemaExprCXX.cpp | 6 +- clang/lib/Sema/SemaOverload.cpp | 7 +- clang/test/CodeGenHLSL/ArrayTemporary.hlsl | 16 +-- .../BasicFeatures/ArrayOutputArguments.hlsl | 98 +++++++++++++++++-- clang/test/SemaHLSL/ArrayTemporary.hlsl | 3 + 12 files changed, 140 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index e9bacf8d7391c4..89fcb6789d880a 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1480,10 +1480,6 @@ class ASTContext : public RefCountedBase<ASTContext> { /// type to the decayed type. QualType getDecayedType(QualType Orig, QualType Decayed) const; - /// Return the uniqued reference to a constant array type from the - /// original array parameter type. - QualType getConstantArrayFromArrayParameterType(QualType Ty) const; - /// Return the uniqued reference to a specified array parameter type from the /// original array type. QualType getArrayParameterType(QualType Ty) const; diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 8979129017163b..72c8352b5355e6 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -3770,6 +3770,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/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index c0b90cd61107be..09d159e5c3efd6 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -3852,16 +3852,6 @@ QualType ASTContext::getDecayedType(QualType T) const { return getDecayedType(T, Decayed); } -QualType ASTContext::getConstantArrayFromArrayParameterType(QualType Ty) const { - if (Ty->isConstantArrayType() && !Ty->isArrayParameterType()) - return Ty; - assert(Ty->isArrayParameterType() && "Ty must be an array parameter type."); - const auto *ATy = cast<ArrayParameterType>(Ty); - return getConstantArrayType(ATy->getElementType(), ATy->getSize(), - ATy->getSizeExpr(), ATy->getSizeModifier(), - ATy->getIndexTypeQualifiers().getAsOpaqueValue()); -} - QualType ASTContext::getArrayParameterType(QualType Ty) const { if (Ty->isArrayParameterType()) return Ty; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 7de13977176f2d..67415304bfea2c 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -241,6 +241,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 cc99cf82c04cdb..06632cfd9bd3f9 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4726,16 +4726,17 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, return emitWritebackArg(*this, args, CRE); } - assert(type->isArrayParameterType() || - (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); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index fcab838be40f2b..1a943650086964 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5832,9 +5832,9 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) { // Don't emit an LValue for the RHS because it might not be an LValue LValue LHS = EmitLValue(E->getLHS()); - // In C assignment operator RHS is often an RValue. - // EmitAggregateAssign expects an LValue for the RHS so call the below - // function instead. + // 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 2b51765e80864a..98bd74f1621637 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -726,6 +726,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 7cac1beedb539c..5a2212dae41587 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4437,7 +4437,11 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, /*BasePath=*/nullptr, CCK) .get(); } else { // FromType must be ArrayParameterType - FromType = Context.getConstantArrayFromArrayParameterType(FromType); + 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(); diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 41e8cc3856049a..cf6c722d9c66ef 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2244,6 +2244,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, } 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 @@ -2253,7 +2257,8 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, FromType = S.Context.getArrayParameterType(FromType); SCS.First = ICK_HLSL_Array_RValue; } else if (FromType->isArrayParameterType()) { - FromType = S.Context.getConstantArrayFromArrayParameterType(FromType); + const ArrayParameterType *APT = cast<ArrayParameterType>(FromType); + FromType = APT->getConstantArrayType(S.Context); SCS.First = ICK_HLSL_Array_RValue; } else { SCS.First = ICK_Identity; diff --git a/clang/test/CodeGenHLSL/ArrayTemporary.hlsl b/clang/test/CodeGenHLSL/ArrayTemporary.hlsl index ab0918974e3a41..7d77c0aff736cc 100644 --- a/clang/test/CodeGenHLSL/ArrayTemporary.hlsl +++ b/clang/test/CodeGenHLSL/ArrayTemporary.hlsl @@ -46,8 +46,9 @@ void call3() { } // CHECK-LABEL: define void {{.*}}call4{{.*}}(ptr +// CHECK-SAME: noundef byval([2 x [2 x float]]) align 4 [[Arr:%.*]]) // CHECK: [[Tmp:%.*]] = alloca [2 x [2 x float]] -// CHECK: store ptr {{.*}}, ptr [[Tmp]], align 4 +// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 16, i1 false) // CHECK: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x [2 x float]]) align 4 [[Tmp]]) void call4(float Arr[2][2]) { @@ -66,12 +67,12 @@ void call4(float Arr[2][2]) { // CHECK: [[Tmp1:%.*]] = alloca [2 x float] // CHECK: [[Tmp2:%.*]] = alloca [4 x float] // CHECK: [[Tmp3:%.*]] = alloca [3 x i32] -// CHECK: store ptr {{.*}}, ptr [[Tmp1]], align 4 -// CHECK: call void @"??$template_fn@$$BY01M@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 [[Tmp1]]) -// CHECK: store ptr {{.*}}, ptr [[Tmp2]], align 4 -// CHECK: call void @"??$template_fn@$$BY03M@@YAXY03M@Z"(ptr noundef byval([4 x float]) align 4 [[Tmp2]]) -// CHECK: store ptr {{.*}}, ptr [[Tmp3]], align 4 -// CHECK: call void @"??$template_fn@$$BY02H@@YAXY02H@Z"(ptr noundef byval([3 x i32]) align 4 [[Tmp3]]) +// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp1]], ptr align 4 [[FA2]], i32 8, i1 false) +// CHECK: call void @_Z11template_fnIA2_fEvT_(ptr noundef byval([2 x float]) align 4 [[Tmp1]]) +// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp2]], ptr align 4 [[FA4]], i32 16, i1 false) +// CHECK: call void @_Z11template_fnIA4_fEvT_(ptr noundef byval([4 x float]) align 4 [[Tmp2]]) +// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp3]], ptr align 4 [[IA3]], i32 12, i1 false) +// CHECK: call void @_Z11template_fnIA3_iEvT_(ptr noundef byval([3 x i32]) align 4 [[Tmp3]]) template<typename T> void template_fn(T Val) {} @@ -82,6 +83,7 @@ void template_call(float FA2[2], float FA4[4], int IA3[3]) { template_fn(IA3); } + // Verify that Array parameter element access correctly codegens. // CHECK-LABEL: define void {{.*}}element_access{{.*}}(ptr // CHECK-SAME: noundef byval([2 x float]) align 4 [[FA2:%[0-9A-Z]+]] diff --git a/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl index d4a5cdb9445a7f..f37faadb716785 100644 --- a/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl +++ b/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl @@ -6,19 +6,19 @@ void increment(inout int Arr[2]) { Arr[0] += 2; } -// CHECK-LABEL: call +// CHECK-LABEL: arrayCall // CHECK: [[A:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 -// CHECK-NEXT: call void @"?increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3 +// CHECK-NEXT: call void @{{.*}}increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #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 call() { +export int arrayCall() { int A[2] = { 0, 1 }; increment(A); return A[0]; @@ -30,19 +30,105 @@ void fn2(out int Arr[2]) { Arr[1] += 6; } -// CHECK-LABEL: call2 +// CHECK-LABEL: arrayCall2 // CHECK: [[A:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 -// CHECK-NEXT: call void @"?fn2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3 +// CHECK-NEXT: call void @{{.*}}fn2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #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 call2() { +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: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 +// CHECK-NEXT: call void @{{.*}}nestedCall{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]], 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: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 +// CHECK-NEXT: call void {{.*}}increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #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: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 +// CHECK-NEXT: call void @{{.*}}outerCall{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #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: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 +// CHECK-NEXT: call void @{{.*}}outerCall2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #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 f1e446d8d3f8f1..0266a198e7ec98 100644 --- a/clang/test/SemaHLSL/ArrayTemporary.hlsl +++ b/clang/test/SemaHLSL/ArrayTemporary.hlsl @@ -75,14 +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]' <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]' <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]' <HLSLArrayRValue> // CHECK-NEXT: DeclRefExpr {{.*}} 'int[3]' lvalue ParmVar {{.*}} 'IA3' 'int[3]' void call(float FA2[2], float FA4[4], int IA3[3]) { >From b663f00c282b6c9ad621b3695933ff118e4ae22a Mon Sep 17 00:00:00 2001 From: Sarah Spall <sp...@planetbauer.com> Date: Fri, 8 Nov 2024 20:50:43 +0000 Subject: [PATCH 7/8] get rid of unwanted temporary generated by EmitCall --- clang/lib/CodeGen/CGCall.cpp | 5 ++++ .../BasicFeatures/ArrayOutputArguments.hlsl | 24 +++++-------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 06632cfd9bd3f9..855c1e9df0936f 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5324,6 +5324,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, IRCallArgs[FirstIRArg] = Val; break; } + } else if (I->getType()->isArrayParameterType()) { + // use the tmp created by the HLSLOutArgExpr + // instead of creating a new one below and copying the tmp into it. + IRCallArgs[FirstIRArg] = I->getKnownRValue().getScalarVal(); + break; } // For non-aggregate args and aggregate args meeting conditions above diff --git a/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl index f37faadb716785..eb7d755bca61df 100644 --- a/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl +++ b/clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl @@ -9,11 +9,9 @@ void increment(inout int Arr[2]) { // CHECK-LABEL: arrayCall // CHECK: [[A:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 -// CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 -// CHECK-NEXT: call void @{{.*}}increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3 +// 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 @@ -33,10 +31,8 @@ void fn2(out int Arr[2]) { // CHECK-LABEL: arrayCall2 // CHECK: [[A:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 -// CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 -// CHECK-NEXT: call void @{{.*}}fn2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3 +// 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 @@ -58,11 +54,9 @@ void nestedCall(inout int Arr[2], uint index) { // CHECK-LABEL: arrayCall3 // CHECK: [[A:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 -// CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 -// CHECK-NEXT: call void @{{.*}}nestedCall{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]], i32 noundef 0) #3 +// 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 @@ -75,10 +69,8 @@ export int arrayCall3() { // CHECK-LABEL: outerCall // CHECK: [[Tmp:%.*]] = alloca [2 x i32], align 4 -// CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 -// CHECK-NEXT: call void {{.*}}increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3 +// 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]) { @@ -88,11 +80,9 @@ void outerCall(inout int Arr[2]) { // CHECK-LABEL: arrayCall4 // CHECK: [[A:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 -// CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 -// CHECK-NEXT: call void @{{.*}}outerCall{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3 +// 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 @@ -118,11 +108,9 @@ void outerCall2(inout int Arr[2]) { // CHECK-LABEL: arrayCall5 // CHECK: [[A:%.*]] = alloca [2 x i32], align 4 // CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4 -// CHECK-NEXT: [[Tmp2:%.*]] = 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: store ptr [[Tmp]], ptr [[Tmp2]], align 4 -// CHECK-NEXT: call void @{{.*}}outerCall2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3 +// 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 >From 58577d225838312fadc2b5d642342f893eda0eab Mon Sep 17 00:00:00 2001 From: Sarah Spall <sp...@planetbauer.com> Date: Tue, 3 Dec 2024 14:23:56 -0800 Subject: [PATCH 8/8] Update clang/lib/CodeGen/CGCall.cpp Update comment with suggested change. Co-authored-by: Chris B <be...@abolishcrlf.org> --- clang/lib/CodeGen/CGCall.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 855c1e9df0936f..bd22dcc4ac124a 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5325,8 +5325,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, break; } } else if (I->getType()->isArrayParameterType()) { - // use the tmp created by the HLSLOutArgExpr - // instead of creating a new one below and copying the tmp into it. + // 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; } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits