Skip to content

Commit 46de3a7

Browse files
spallllvm-beanz
andauthored
[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 <[email protected]>
1 parent 6a0d6fc commit 46de3a7

File tree

13 files changed

+331
-44
lines changed

13 files changed

+331
-44
lines changed

clang/include/clang/AST/Type.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3754,6 +3754,8 @@ class ArrayParameterType : public ConstantArrayType {
37543754
static bool classof(const Type *T) {
37553755
return T->getTypeClass() == ArrayParameter;
37563756
}
3757+
3758+
QualType getConstantArrayType(const ASTContext &Ctx) const;
37573759
};
37583760

37593761
/// Represents a C array with an unspecified size. For example 'int A[]' has

clang/lib/AST/Type.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,12 @@ void ConstantArrayType::Profile(llvm::FoldingSetNodeID &ID,
267267
SizeExpr->Profile(ID, Context, true);
268268
}
269269

270+
QualType ArrayParameterType::getConstantArrayType(const ASTContext &Ctx) const {
271+
return Ctx.getConstantArrayType(getElementType(), getSize(), getSizeExpr(),
272+
getSizeModifier(),
273+
getIndexTypeQualifiers().getAsOpaqueValue());
274+
}
275+
270276
DependentSizedArrayType::DependentSizedArrayType(QualType et, QualType can,
271277
Expr *e, ArraySizeModifier sm,
272278
unsigned tq,

clang/lib/CodeGen/CGCall.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4725,15 +4725,17 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
47254725
return emitWritebackArg(*this, args, CRE);
47264726
}
47274727

4728-
assert(type->isReferenceType() == E->isGLValue() &&
4729-
"reference binding to unmaterialized r-value!");
4730-
47314728
// Add writeback for HLSLOutParamExpr.
4729+
// Needs to be before the assert below because HLSLOutArgExpr is an LValue
4730+
// and is not a reference.
47324731
if (const HLSLOutArgExpr *OE = dyn_cast<HLSLOutArgExpr>(E)) {
47334732
EmitHLSLOutArgExpr(OE, args, type);
47344733
return;
47354734
}
47364735

4736+
assert(type->isReferenceType() == E->isGLValue() &&
4737+
"reference binding to unmaterialized r-value!");
4738+
47374739
if (E->isGLValue()) {
47384740
assert(E->getObjectKind() == OK_Ordinary);
47394741
return args.add(EmitReferenceBindingToExpr(E), type);
@@ -5322,6 +5324,14 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
53225324
IRCallArgs[FirstIRArg] = Val;
53235325
break;
53245326
}
5327+
} else if (I->getType()->isArrayParameterType()) {
5328+
// Don't produce a temporary for ArrayParameterType arguments.
5329+
// ArrayParameterType arguments are only created from
5330+
// HLSL_ArrayRValue casts and HLSLOutArgExpr expressions, both
5331+
// of which create temporaries already. This allows us to just use the
5332+
// scalar for the decayed array pointer as the argument directly.
5333+
IRCallArgs[FirstIRArg] = I->getKnownRValue().getScalarVal();
5334+
break;
53255335
}
53265336

53275337
// For non-aggregate args and aggregate args meeting conditions above

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5827,9 +5827,12 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
58275827
// This function implements trivial copy assignment for HLSL's
58285828
// assignable constant arrays.
58295829
LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) {
5830-
LValue TrivialAssignmentRHS = EmitLValue(E->getRHS());
5830+
// Don't emit an LValue for the RHS because it might not be an LValue
58315831
LValue LHS = EmitLValue(E->getLHS());
5832-
EmitAggregateAssign(LHS, TrivialAssignmentRHS, E->getLHS()->getType());
5832+
// In C the RHS of an assignment operator is an RValue.
5833+
// EmitAggregateAssign takes anan LValue for the RHS. Instead we can call
5834+
// EmitInitializationToLValue to emit an RValue into an LValue.
5835+
EmitInitializationToLValue(E->getRHS(), LHS);
58335836
return LHS;
58345837
}
58355838

clang/lib/Sema/Sema.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,15 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
723723
QualType ExprTy = Context.getCanonicalType(E->getType());
724724
QualType TypeTy = Context.getCanonicalType(Ty);
725725

726+
// This cast is used in place of a regular LValue to RValue cast for
727+
// HLSL Array Parameter Types. It needs to be emitted even if
728+
// ExprTy == TypeTy, except if E is an HLSLOutArgExpr
729+
// Emitting a cast in that case will prevent HLSLOutArgExpr from
730+
// being handled properly in EmitCallArg
731+
if (Kind == CK_HLSLArrayRValue && !isa<HLSLOutArgExpr>(E))
732+
return ImplicitCastExpr::Create(Context, Ty, Kind, E, BasePath, VK,
733+
CurFPFeatureOverrides());
734+
726735
if (ExprTy == TypeTy)
727736
return E;
728737

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4431,10 +4431,21 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
44314431
break;
44324432

44334433
case ICK_HLSL_Array_RValue:
4434-
FromType = Context.getArrayParameterType(FromType);
4435-
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
4436-
/*BasePath=*/nullptr, CCK)
4437-
.get();
4434+
if (ToType->isArrayParameterType()) {
4435+
FromType = Context.getArrayParameterType(FromType);
4436+
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
4437+
/*BasePath=*/nullptr, CCK)
4438+
.get();
4439+
} else { // FromType must be ArrayParameterType
4440+
assert(FromType->isArrayParameterType() &&
4441+
"FromType must be ArrayParameterType in ICK_HLSL_Array_RValue \
4442+
if it is not ToType");
4443+
const ArrayParameterType *APT = cast<ArrayParameterType>(FromType);
4444+
FromType = APT->getConstantArrayType(Context);
4445+
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
4446+
/*BasePath=*/nullptr, CCK)
4447+
.get();
4448+
}
44384449
break;
44394450

44404451
case ICK_Function_To_Pointer:

clang/lib/Sema/SemaOverload.cpp

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,33 +2236,24 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
22362236
return false;
22372237
}
22382238
}
2239-
// Lvalue-to-rvalue conversion (C++11 4.1):
2240-
// A glvalue (3.10) of a non-function, non-array type T can
2241-
// be converted to a prvalue.
2242-
bool argIsLValue = From->isGLValue();
2243-
if (argIsLValue && !FromType->canDecayToPointerType() &&
2244-
S.Context.getCanonicalType(FromType) != S.Context.OverloadTy) {
2245-
SCS.First = ICK_Lvalue_To_Rvalue;
2246-
2247-
// C11 6.3.2.1p2:
2248-
// ... if the lvalue has atomic type, the value has the non-atomic version
2249-
// of the type of the lvalue ...
2250-
if (const AtomicType *Atomic = FromType->getAs<AtomicType>())
2251-
FromType = Atomic->getValueType();
22522239

2253-
// If T is a non-class type, the type of the rvalue is the
2254-
// cv-unqualified version of T. Otherwise, the type of the rvalue
2255-
// is T (C++ 4.1p1). C++ can't get here with class types; in C, we
2256-
// just strip the qualifiers because they don't matter.
2257-
FromType = FromType.getUnqualifiedType();
2258-
} else if (S.getLangOpts().HLSL && FromType->isConstantArrayType() &&
2259-
ToType->isConstantArrayType()) {
2240+
bool argIsLValue = From->isGLValue();
2241+
// To handle conversion from ArrayParameterType to ConstantArrayType
2242+
// this block must be above the one below because Array parameters
2243+
// do not decay and when handling HLSLOutArgExprs and
2244+
// the From expression is an LValue.
2245+
if (S.getLangOpts().HLSL && FromType->isConstantArrayType() &&
2246+
ToType->isConstantArrayType()) {
22602247
// HLSL constant array parameters do not decay, so if the argument is a
22612248
// constant array and the parameter is an ArrayParameterType we have special
22622249
// handling here.
22632250
if (ToType->isArrayParameterType()) {
22642251
FromType = S.Context.getArrayParameterType(FromType);
22652252
SCS.First = ICK_HLSL_Array_RValue;
2253+
} else if (FromType->isArrayParameterType()) {
2254+
const ArrayParameterType *APT = cast<ArrayParameterType>(FromType);
2255+
FromType = APT->getConstantArrayType(S.Context);
2256+
SCS.First = ICK_HLSL_Array_RValue;
22662257
} else {
22672258
SCS.First = ICK_Identity;
22682259
}
@@ -2273,6 +2264,25 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
22732264

22742265
SCS.setAllToTypes(ToType);
22752266
return true;
2267+
} else if (argIsLValue && !FromType->canDecayToPointerType() &&
2268+
S.Context.getCanonicalType(FromType) != S.Context.OverloadTy) {
2269+
// Lvalue-to-rvalue conversion (C++11 4.1):
2270+
// A glvalue (3.10) of a non-function, non-array type T can
2271+
// be converted to a prvalue.
2272+
2273+
SCS.First = ICK_Lvalue_To_Rvalue;
2274+
2275+
// C11 6.3.2.1p2:
2276+
// ... if the lvalue has atomic type, the value has the non-atomic version
2277+
// of the type of the lvalue ...
2278+
if (const AtomicType *Atomic = FromType->getAs<AtomicType>())
2279+
FromType = Atomic->getValueType();
2280+
2281+
// If T is a non-class type, the type of the rvalue is the
2282+
// cv-unqualified version of T. Otherwise, the type of the rvalue
2283+
// is T (C++ 4.1p1). C++ can't get here with class types; in C, we
2284+
// just strip the qualifiers because they don't matter.
2285+
FromType = FromType.getUnqualifiedType();
22762286
} else if (FromType->isArrayType()) {
22772287
// Array-to-pointer conversion (C++ 4.2)
22782288
SCS.First = ICK_Array_To_Pointer;

clang/lib/Sema/SemaType.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5681,6 +5681,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
56815681
assert(!T.isNull() && "T must not be null at the end of this function");
56825682
if (!AreDeclaratorChunksValid)
56835683
return Context.getTrivialTypeSourceInfo(T);
5684+
5685+
if (state.didParseHLSLParamMod() && !T->isConstantArrayType())
5686+
T = S.HLSL().getInoutParameterType(T);
56845687
return GetTypeSourceInfoForDeclarator(state, T, TInfo);
56855688
}
56865689

@@ -8634,7 +8637,6 @@ static void HandleHLSLParamModifierAttr(TypeProcessingState &State,
86348637
return;
86358638
if (Attr.getSemanticSpelling() == HLSLParamModifierAttr::Keyword_inout ||
86368639
Attr.getSemanticSpelling() == HLSLParamModifierAttr::Keyword_out) {
8637-
CurType = S.HLSL().getInoutParameterType(CurType);
86388640
State.setParsedHLSLParamMod(true);
86398641
}
86408642
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump %s | FileCheck %s
2+
3+
// CHECK-LABEL: increment
4+
void increment(inout int Arr[2]) {
5+
for (int I = 0; I < 2; I++)
6+
Arr[0] += 2;
7+
}
8+
9+
// CHECK-LABEL: call
10+
// CHECK: CallExpr 0x{{.*}} {{.*}} 'void'
11+
// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'void (*)(inout int[2])' <FunctionToPointerDecay>
12+
// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'void (inout int[2])' lvalue Function 0x{{.*}} 'increment' 'void (inout int[2])'
13+
// CHECK: HLSLOutArgExpr 0x{{.*}} {{.*}} 'int[2]' lvalue inout
14+
// CHECK: OpaqueValueExpr [[A:0x.*]] {{.*}} 'int[2]' lvalue
15+
// CHECK: DeclRefExpr [[B:0x.*]] {{.*}} 'int[2]' lvalue Var [[E:0x.*]] 'A' 'int[2]'
16+
// CHECK: OpaqueValueExpr [[C:0x.*]] {{.*}} 'int[2]' lvalue
17+
// CHECK: ImplicitCastExpr [[D:0x.*]] {{.*}} 'int[2]' <HLSLArrayRValue>
18+
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
19+
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
20+
// CHECK: BinaryOperator 0x{{.*}} {{.*}} 'int[2]' lvalue '='
21+
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
22+
// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
23+
// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'int[2]' <HLSLArrayRValue>
24+
// CHECK: OpaqueValueExpr [[C]] {{.*}} 'int[2]' lvalue
25+
// CHECK: ImplicitCastExpr [[D]] {{.*}} 'int[2]' <HLSLArrayRValue>
26+
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
27+
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
28+
export int call() {
29+
int A[2] = { 0, 1 };
30+
increment(A);
31+
return A[0];
32+
}
33+
34+
// CHECK-LABEL: fn2
35+
void fn2(out int Arr[2]) {
36+
Arr[0] += 5;
37+
Arr[1] += 6;
38+
}
39+
40+
// CHECK-LABEL: call2
41+
// CHECK: CallExpr 0x{{.*}} {{.*}} 'void'
42+
// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'void (*)(out int[2])' <FunctionToPointerDecay>
43+
// CHECK: DeclRefExpr 0x{{.*}} {{.*}} 'void (out int[2])' lvalue Function 0x{{.*}} 'fn2' 'void (out int[2])'
44+
// CHECK: HLSLOutArgExpr 0x{{.*}} {{.*}} 'int[2]' lvalue out
45+
// CHECK: OpaqueValueExpr [[A:0x.*]] {{.*}} 'int[2]' lvalue
46+
// CHECK: DeclRefExpr [[B:0x.*]] {{.*}} 'int[2]' lvalue Var [[E:0x.*]] 'A' 'int[2]'
47+
// CHECK: OpaqueValueExpr [[C:0x.*]] {{.*}} 'int[2]' lvalue
48+
// CHECK: ImplicitCastExpr [[D:0x.*]] {{.*}} 'int[2]' <HLSLArrayRValue>
49+
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
50+
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
51+
// CHECK: BinaryOperator 0x{{.*}} {{.*}} 'int[2]' lvalue '='
52+
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
53+
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
54+
// CHECK: ImplicitCastExpr 0x{{.*}} {{.*}} 'int[2]' <HLSLArrayRValue>
55+
// CHECK: OpaqueValueExpr [[C]] {{.*}} 'int[2]' lvalue
56+
// CHECK: ImplicitCastExpr [[D]] {{.*}} 'int[2]' <HLSLArrayRValue>
57+
// CHECK: OpaqueValueExpr [[A]] {{.*}} 'int[2]' lvalue
58+
// CHECK: DeclRefExpr [[B]] {{.*}} 'int[2]' lvalue Var [[E]] 'A' 'int[2]'
59+
export int call2() {
60+
int A[2] = { 0, 1 };
61+
fn2(A);
62+
return 1;
63+
}

clang/test/CodeGenHLSL/ArrayAssignable.hlsl

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,16 @@ void arr_assign6() {
100100
}
101101

102102
// CHECK-LABEL: define void {{.*}}arr_assign7
103-
// CHECK: [[Arr3:%.*]] = alloca [2 x [2 x i32]], align 4
104-
// CHECK-NEXT: [[Arr4:%.*]] = alloca [2 x [2 x i32]], align 4
105-
// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4
103+
// CHECK: [[Arr:%.*]] = alloca [2 x [2 x i32]], align 4
104+
// CHECK-NEXT: [[Arr2:%.*]] = alloca [2 x [2 x i32]], align 4
106105
// CHECK-NOT: alloca
107-
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 {{@.*}}, i32 16, i1 false)
108-
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr4]], ptr align 4 {{@.*}}, i32 16, i1 false)
109-
// CHECK-NEXT: store i32 6, ptr [[Tmp]], align 4
110-
// CHECK-NEXT: [[AIE:%.*]] = getelementptr inbounds i32, ptr [[Tmp]], i32 1
111-
// CHECK-NEXT: store i32 6, ptr [[AIE]], align 4
112-
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 [[Arr4]], i32 16, i1 false)
113-
// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[Arr3]], i32 0, i32 0
114-
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Idx]], ptr align 4 [[Tmp]], i32 8, i1 false)
106+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{@.*}}, i32 16, i1 false)
107+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr2]], ptr align 4 {{@.*}}, i32 16, i1 false)
108+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 [[Arr2]], i32 16, i1 false)
109+
// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[Arr]], i32 0, i32 0
110+
// CHECK-NEXT: store i32 6, ptr [[Idx]], align 4
111+
// CHECK-NEXT: [[Idx2:%.*]] = getelementptr inbounds i32, ptr %arrayidx, i32 1
112+
// CHECK-NEXT: store i32 6, ptr [[Idx2]], align 4
115113
// CHECK-NEXT: ret void
116114
void arr_assign7() {
117115
int Arr[2][2] = {{0, 1}, {2, 3}};

0 commit comments

Comments
 (0)