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