[clang] [HLSL] get inout/out ABI for array parameters working (PR #111047)

Sarah Spall via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 8 13:49:43 PST 2024


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 <spall at planetbauer.com>
Date: Tue, 17 Sep 2024 20:25:46 +0000
Subject: [PATCH 1/7] 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 <spall at planetbauer.com>
Date: Wed, 2 Oct 2024 21:34:02 +0000
Subject: [PATCH 2/7] 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 <spall at planetbauer.com>
Date: Thu, 3 Oct 2024 14:26:00 +0000
Subject: [PATCH 3/7] 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 at Z"(ptr noundef byval([2 x float]) align 4 [[Tmp1]])
+// CHECK: store ptr {{.*}}, ptr [[Tmp2]], align 4
+// CHECK: call void @"??$template_fn@$$BY03M@@YAXY03M at Z"(ptr noundef byval([4 x float]) align 4 [[Tmp2]])
+// CHECK: store ptr {{.*}}, ptr [[Tmp3]], align 4
+// CHECK: call void @"??$template_fn@$$BY02H@@YAXY02H at 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 <spall at planetbauer.com>
Date: Thu, 3 Oct 2024 17:01:14 +0000
Subject: [PATCH 4/7] 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 at -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 at -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 at -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 at -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 at -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 at -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 <spall at planetbauer.com>
Date: Thu, 3 Oct 2024 20:38:56 +0000
Subject: [PATCH 5/7] 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 <spall at planetbauer.com>
Date: Tue, 22 Oct 2024 20:57:21 +0000
Subject: [PATCH 6/7] 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 at Z"(ptr noundef byval([2 x float]) align 4 [[Tmp1]])
-// CHECK: store ptr {{.*}}, ptr [[Tmp2]], align 4
-// CHECK: call void @"??$template_fn@$$BY03M@@YAXY03M at Z"(ptr noundef byval([4 x float]) align 4 [[Tmp2]])
-// CHECK: store ptr {{.*}}, ptr [[Tmp3]], align 4
-// CHECK: call void @"??$template_fn@$$BY02H@@YAXY02H at 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 <spall at planetbauer.com>
Date: Fri, 8 Nov 2024 20:50:43 +0000
Subject: [PATCH 7/7] 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



More information about the cfe-commits mailing list