Skip to content

Commit 7277546

Browse files
committed
addressing pr comments
1 parent 6a4ce56 commit 7277546

File tree

12 files changed

+138
-36
lines changed

12 files changed

+138
-36
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,10 +1471,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
14711471
/// type to the decayed type.
14721472
QualType getDecayedType(QualType Orig, QualType Decayed) const;
14731473

1474-
/// Return the uniqued reference to a constant array type from the
1475-
/// original array parameter type.
1476-
QualType getConstantArrayFromArrayParameterType(QualType Ty) const;
1477-
14781474
/// Return the uniqued reference to a specified array parameter type from the
14791475
/// original array type.
14801476
QualType getArrayParameterType(QualType Ty) const;

clang/include/clang/AST/Type.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3757,6 +3757,8 @@ class ArrayParameterType : public ConstantArrayType {
37573757
static bool classof(const Type *T) {
37583758
return T->getTypeClass() == ArrayParameter;
37593759
}
3760+
3761+
QualType getConstantArrayType(const ASTContext &Ctx) const;
37603762
};
37613763

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

clang/lib/AST/ASTContext.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3839,16 +3839,6 @@ QualType ASTContext::getDecayedType(QualType T) const {
38393839
return getDecayedType(T, Decayed);
38403840
}
38413841

3842-
QualType ASTContext::getConstantArrayFromArrayParameterType(QualType Ty) const {
3843-
if (Ty->isConstantArrayType() && !Ty->isArrayParameterType())
3844-
return Ty;
3845-
assert(Ty->isArrayParameterType() && "Ty must be an array parameter type.");
3846-
const auto *ATy = cast<ArrayParameterType>(Ty);
3847-
return getConstantArrayType(ATy->getElementType(), ATy->getSize(),
3848-
ATy->getSizeExpr(), ATy->getSizeModifier(),
3849-
ATy->getIndexTypeQualifiers().getAsOpaqueValue());
3850-
}
3851-
38523842
QualType ASTContext::getArrayParameterType(QualType Ty) const {
38533843
if (Ty->isArrayParameterType())
38543844
return Ty;

clang/lib/AST/Type.cpp

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

243+
QualType ArrayParameterType::getConstantArrayType(const ASTContext &Ctx) const {
244+
return Ctx.getConstantArrayType(getElementType(), getSize(), getSizeExpr(),
245+
getSizeModifier(),
246+
getIndexTypeQualifiers().getAsOpaqueValue());
247+
}
248+
243249
DependentSizedArrayType::DependentSizedArrayType(QualType et, QualType can,
244250
Expr *e, ArraySizeModifier sm,
245251
unsigned tq,

clang/lib/CodeGen/CGCall.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4726,16 +4726,17 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
47264726
return emitWritebackArg(*this, args, CRE);
47274727
}
47284728

4729-
assert(type->isArrayParameterType() ||
4730-
(type->isReferenceType() == E->isGLValue()) &&
4731-
"reference binding to unmaterialized r-value!");
4732-
47334729
// Add writeback for HLSLOutParamExpr.
4730+
// Needs to be before the assert below because HLSLOutArgExpr is an LValue
4731+
// and is not a reference.
47344732
if (const HLSLOutArgExpr *OE = dyn_cast<HLSLOutArgExpr>(E)) {
47354733
EmitHLSLOutArgExpr(OE, args, type);
47364734
return;
47374735
}
47384736

4737+
assert(type->isReferenceType() == E->isGLValue() &&
4738+
"reference binding to unmaterialized r-value!");
4739+
47394740
if (E->isGLValue()) {
47404741
assert(E->getObjectKind() == OK_Ordinary);
47414742
return args.add(EmitReferenceBindingToExpr(E), type);

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5823,9 +5823,9 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
58235823
LValue CodeGenFunction::EmitHLSLArrayAssignLValue(const BinaryOperator *E) {
58245824
// Don't emit an LValue for the RHS because it might not be an LValue
58255825
LValue LHS = EmitLValue(E->getLHS());
5826-
// In C assignment operator RHS is often an RValue.
5827-
// EmitAggregateAssign expects an LValue for the RHS so call the below
5828-
// function instead.
5826+
// In C the RHS of an assignment operator is an RValue.
5827+
// EmitAggregateAssign takes anan LValue for the RHS. Instead we can call
5828+
// EmitInitializationToLValue to emit an RValue into an LValue.
58295829
EmitInitializationToLValue(E->getRHS(), LHS);
58305830
return LHS;
58315831
}

clang/lib/Sema/Sema.cpp

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

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

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4437,7 +4437,11 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
44374437
/*BasePath=*/nullptr, CCK)
44384438
.get();
44394439
} else { // FromType must be ArrayParameterType
4440-
FromType = Context.getConstantArrayFromArrayParameterType(FromType);
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);
44414445
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
44424446
/*BasePath=*/nullptr, CCK)
44434447
.get();

clang/lib/Sema/SemaOverload.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2244,6 +2244,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
22442244
}
22452245

22462246
bool argIsLValue = From->isGLValue();
2247+
// To handle conversion from ArrayParameterType to ConstantArrayType
2248+
// this block must be above the one below because Array parameters
2249+
// do not decay and when handling HLSLOutArgExprs and
2250+
// the From expression is an LValue.
22472251
if (S.getLangOpts().HLSL && FromType->isConstantArrayType() &&
22482252
ToType->isConstantArrayType()) {
22492253
// 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,
22532257
FromType = S.Context.getArrayParameterType(FromType);
22542258
SCS.First = ICK_HLSL_Array_RValue;
22552259
} else if (FromType->isArrayParameterType()) {
2256-
FromType = S.Context.getConstantArrayFromArrayParameterType(FromType);
2260+
const ArrayParameterType *APT = cast<ArrayParameterType>(FromType);
2261+
FromType = APT->getConstantArrayType(S.Context);
22572262
SCS.First = ICK_HLSL_Array_RValue;
22582263
} else {
22592264
SCS.First = ICK_Identity;

clang/test/CodeGenHLSL/ArrayTemporary.hlsl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ void call3() {
4747

4848
// CHECK-LABEL: define void {{.*}}call4{{.*}}(ptr
4949
// CHECK: [[Tmp:%.*]] = alloca [2 x [2 x float]]
50-
// CHECK: store ptr {{.*}}, ptr [[Tmp]], align 4
50+
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 16, i1 false)
5151
// CHECK: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x [2 x float]]) align 4 [[Tmp]])
5252

5353
void call4(float Arr[2][2]) {
@@ -66,12 +66,12 @@ void call4(float Arr[2][2]) {
6666
// CHECK: [[Tmp1:%.*]] = alloca [2 x float]
6767
// CHECK: [[Tmp2:%.*]] = alloca [4 x float]
6868
// CHECK: [[Tmp3:%.*]] = alloca [3 x i32]
69-
// CHECK: store ptr {{.*}}, ptr [[Tmp1]], align 4
70-
// CHECK: call void @"??$template_fn@$$BY01M@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 [[Tmp1]])
71-
// CHECK: store ptr {{.*}}, ptr [[Tmp2]], align 4
72-
// CHECK: call void @"??$template_fn@$$BY03M@@YAXY03M@Z"(ptr noundef byval([4 x float]) align 4 [[Tmp2]])
73-
// CHECK: store ptr {{.*}}, ptr [[Tmp3]], align 4
74-
// CHECK: call void @"??$template_fn@$$BY02H@@YAXY02H@Z"(ptr noundef byval([3 x i32]) align 4 [[Tmp3]])
69+
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp1]], ptr align 4 [[FA2]], i32 8, i1 false)
70+
// CHECK: call void @_Z11template_fnIA2_fEvT_(ptr noundef byval([2 x float]) align 4 [[Tmp1]])
71+
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp2]], ptr align 4 [[FA4]], i32 16, i1 false)
72+
// CHECK: call void @_Z11template_fnIA4_fEvT_(ptr noundef byval([4 x float]) align 4 [[Tmp2]])
73+
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp3]], ptr align 4 [[IA3]], i32 12, i1 false)
74+
// CHECK: call void @_Z11template_fnIA3_iEvT_(ptr noundef byval([3 x i32]) align 4 [[Tmp3]])
7575

7676
template<typename T>
7777
void template_fn(T Val) {}

clang/test/CodeGenHLSL/BasicFeatures/ArrayOutputArguments.hlsl

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@ void increment(inout int Arr[2]) {
66
Arr[0] += 2;
77
}
88

9-
// CHECK-LABEL: call
9+
// CHECK-LABEL: arrayCall
1010
// CHECK: [[A:%.*]] = alloca [2 x i32], align 4
1111
// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4
1212
// CHECK-NEXT: [[Tmp2:%.*]] = alloca [2 x i32], align 4
1313
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false)
1414
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[A]], i32 8, i1 false)
1515
// CHECK-NEXT: store ptr [[Tmp]], ptr [[Tmp2]], align 4
16-
// CHECK-NEXT: call void @"?increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3
16+
// CHECK-NEXT: call void @{{.*}}increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3
1717
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false)
1818
// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 0
1919
// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4
2020
// CHECK-NEXT: ret i32 [[B]]
21-
export int call() {
21+
export int arrayCall() {
2222
int A[2] = { 0, 1 };
2323
increment(A);
2424
return A[0];
@@ -30,19 +30,105 @@ void fn2(out int Arr[2]) {
3030
Arr[1] += 6;
3131
}
3232

33-
// CHECK-LABEL: call2
33+
// CHECK-LABEL: arrayCall2
3434
// CHECK: [[A:%.*]] = alloca [2 x i32], align 4
3535
// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4
3636
// CHECK-NEXT: [[Tmp2:%.*]] = alloca [2 x i32], align 4
3737
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false)
3838
// CHECK-NEXT: store ptr [[Tmp]], ptr [[Tmp2]], align 4
39-
// CHECK-NEXT: call void @"?fn2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3
39+
// CHECK-NEXT: call void @{{.*}}fn2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3
4040
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false)
4141
// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 0
4242
// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4
4343
// CHECK-NEXT: ret i32 [[B]]
44-
export int call2() {
44+
export int arrayCall2() {
4545
int A[2] = { 0, 1 };
4646
fn2(A);
4747
return A[0];
4848
}
49+
50+
// CHECK-LABEL: nestedCall
51+
void nestedCall(inout int Arr[2], uint index) {
52+
if (index < 2) {
53+
Arr[index] += 2;
54+
nestedCall(Arr, index+1);
55+
}
56+
}
57+
58+
// CHECK-LABEL: arrayCall3
59+
// CHECK: [[A:%.*]] = alloca [2 x i32], align 4
60+
// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4
61+
// CHECK-NEXT: [[Tmp2:%.*]] = alloca [2 x i32], align 4
62+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false)
63+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[A]], i32 8, i1 false)
64+
// CHECK-NEXT: store ptr [[Tmp]], ptr [[Tmp2]], align 4
65+
// CHECK-NEXT: call void @{{.*}}nestedCall{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]], i32 noundef 0) #3
66+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false)
67+
// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 1
68+
// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4
69+
// CHECK-NEXt: ret i32 [[B]]
70+
export int arrayCall3() {
71+
int A[2] = { 0, 1 };
72+
nestedCall(A, 0);
73+
return A[1];
74+
}
75+
76+
// CHECK-LABEL: outerCall
77+
// CHECK: [[Tmp:%.*]] = alloca [2 x i32], align 4
78+
// CHECK-NEXT: [[Tmp2:%.*]] = alloca [2 x i32], align 4
79+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 %{{.*}}, i32 8, i1 false)
80+
// CHECK-NEXT: store ptr [[Tmp]], ptr [[Tmp2]], align 4
81+
// CHECK-NEXT: call void {{.*}}increment{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3
82+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 {{.*}}, ptr align 4 [[Tmp]], i32 8, i1 false)
83+
// CHECK-NEXT: ret void
84+
void outerCall(inout int Arr[2]) {
85+
increment(Arr);
86+
}
87+
88+
// CHECK-LABEL: arrayCall4
89+
// CHECK: [[A:%.*]] = alloca [2 x i32], align 4
90+
// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4
91+
// CHECK-NEXT: [[Tmp2:%.*]] = alloca [2 x i32], align 4
92+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false)
93+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[A]], i32 8, i1 false)
94+
// CHECK-NEXT: store ptr [[Tmp]], ptr [[Tmp2]], align 4
95+
// CHECK-NEXT: call void @{{.*}}outerCall{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3
96+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false)
97+
// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 0
98+
// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4
99+
// CHECK-NEXT: ret i32 [[B]]
100+
export int arrayCall4() {
101+
int A[2] = { 0, 1 };
102+
outerCall(A);
103+
return A[0];
104+
}
105+
106+
// CHECK-LABEL: fn3
107+
void fn3(int Arr[2]) {}
108+
109+
// CHECK-LABEL: outerCall2
110+
// CHECK: [[Tmp:%.*]] = alloca [2 x i32], align 4
111+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 {{.*}}, i32 8, i1 false)
112+
// CHECK-NEXT: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x i32]) align 4 [[Tmp]]) #3
113+
// CHECK-NEXT: ret void
114+
void outerCall2(inout int Arr[2]) {
115+
fn3(Arr);
116+
}
117+
118+
// CHECK-LABEL: arrayCall5
119+
// CHECK: [[A:%.*]] = alloca [2 x i32], align 4
120+
// CHECK-NEXT: [[Tmp:%.*]] = alloca [2 x i32], align 4
121+
// CHECK-NEXT: [[Tmp2:%.*]] = alloca [2 x i32], align 4
122+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 @{{.*}}, i32 8, i1 false)
123+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[A]], i32 8, i1 false)
124+
// CHECK-NEXT: store ptr [[Tmp]], ptr [[Tmp2]], align 4
125+
// CHECK-NEXT: call void @{{.*}}outerCall2{{.*}}(ptr noalias noundef byval([2 x i32]) align 4 [[Tmp2]]) #3
126+
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 [[Tmp]], i32 8, i1 false)
127+
// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[A]], i32 0, i32 0
128+
// CHECK-NEXT: [[B:%.*]] = load i32, ptr [[Idx]], align 4
129+
// CHECK-NEXT: ret i32 [[B]]
130+
export int arrayCall5() {
131+
int A[2] = { 0, 1 };
132+
outerCall2(A);
133+
return A[0];
134+
}

clang/test/SemaHLSL/ArrayTemporary.hlsl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,17 @@ void template_fn(T Val) {}
7575
// CHECK: CallExpr {{.*}} 'void'
7676
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2])' <FunctionToPointerDecay>
7777
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2])' lvalue Function {{.*}} 'template_fn' 'void (float[2])' (FunctionTemplate {{.*}} 'template_fn')
78+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <HLSLArrayRValue>
7879
// CHECK-NEXT: DeclRefExpr {{.*}} 'float[2]' lvalue ParmVar {{.*}} 'FA2' 'float[2]'
7980
// CHECK-NEXT: CallExpr {{.*}} 'void'
8081
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[4])' <FunctionToPointerDecay>
8182
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[4])' lvalue Function {{.*}} 'template_fn' 'void (float[4])' (FunctionTemplate {{.*}} 'template_fn')
83+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[4]' <HLSLArrayRValue>
8284
// CHECK-NEXT: DeclRefExpr {{.*}} 'float[4]' lvalue ParmVar {{.*}} 'FA4' 'float[4]'
8385
// CHECK-NEXT: CallExpr {{.*}} 'void'
8486
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int[3])' <FunctionToPointerDecay>
8587
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (int[3])' lvalue Function {{.*}} 'template_fn' 'void (int[3])' (FunctionTemplate {{.*}} 'template_fn')
88+
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int[3]' <HLSLArrayRValue>
8689
// CHECK-NEXT: DeclRefExpr {{.*}} 'int[3]' lvalue ParmVar {{.*}} 'IA3' 'int[3]'
8790

8891
void call(float FA2[2], float FA4[4], int IA3[3]) {

0 commit comments

Comments
 (0)