[clang] [HLSL] Allow a groupshared struct to be passed as an implicit parameter (PR #229939)
Sarah Spall via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 14 09:47:59 PDT 2026
https://github.com/spall updated https://github.com/llvm/llvm-project/pull/229939
>From 83235680a7d359f216091f848c9a478d2c761906 Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Wed, 7 Oct 2026 15:40:26 -0700
Subject: [PATCH 1/5] implicit struct passing for groupshared struct
---
clang/lib/Sema/SemaOverload.cpp | 13 ++++----
clang/test/AST/HLSL/StructPassing-AST.hlsl | 18 +++++++++++
.../BasicFeatures/StructPassing.hlsl | 30 +++++++++++++++++++
3 files changed, 56 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index e5049d11480ac4..44b397d2a47b93 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6344,12 +6344,15 @@ ExprResult Sema::PerformImplicitObjectArgumentInitialization(
QualType FromRecordType, DestType;
QualType ImplicitParamRecordType = Method->getFunctionObjectParameterType();
+ LangAS FromAS = From->getType().getAddressSpace();
if (getLangOpts().HLSL &&
- From->getType().getAddressSpace() == LangAS::hlsl_constant) {
- QualType CastType = From->getType().getLocalUnqualifiedType().withConst();
- From = ImplicitCastExpr::Create(Context, CastType, CK_LValueToRValue, From,
- /*BasePath=*/nullptr, VK_PRValue,
- FPOptionsOverride());
+ (FromAS == LangAS::hlsl_constant || FromAS == LangAS::hlsl_groupshared)) {
+ QualType CastType = From->getType().getLocalUnqualifiedType();
+ From = ImplicitCastExpr::Create(
+ Context,
+ FromAS == LangAS::hlsl_constant ? CastType.withConst() : CastType,
+ CK_LValueToRValue, From,
+ /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride());
}
Expr::Classification FromClassification;
diff --git a/clang/test/AST/HLSL/StructPassing-AST.hlsl b/clang/test/AST/HLSL/StructPassing-AST.hlsl
index 8ae83fa89b3905..745187514bd432 100644
--- a/clang/test/AST/HLSL/StructPassing-AST.hlsl
+++ b/clang/test/AST/HLSL/StructPassing-AST.hlsl
@@ -118,5 +118,23 @@ void case6(S s) {
useP(s);
}
+struct G {
+ void f() {}
+};
+
+groupshared G g;
+
+// CHECK: FunctionDecl {{.*}} case7 'void ()'
+// CHECK-NEXT: CompoundStmt {{.*}}
+// CHECK-NEXT: ExprWithCleanups {{.*}} 'void'
+// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'void'
+// CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .f {{.*}}
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'G' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+void case7() {
+ g.f();
+}
+
// CHECK-NOT: CXXConstructExpr
// CHECK-NOT: CXXOperatorCallExpr
diff --git a/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
index c80c7c9466afcf..a846f682493fa6 100644
--- a/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
+++ b/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
@@ -133,3 +133,33 @@ void case6(S s) {
// derived to base conversion in argument passing
useP(s);
}
+
+// CHECK-LABEL: define hidden void @_Z5case7v(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_G:%.*]], align 1
+// CHECK-NEXT: call void @_ZN1G1fEv(ptr noundef nonnull align 1 dereferenceable(1) [[REF_TMP]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
+// CHECK-NEXT: ret void
+//
+void case7();
+
+struct G {
+// CHECK-LABEL: define linkonce_odr hidden void @_ZN1G1fEv(
+// CHECK-SAME: ptr noundef nonnull align 1 dereferenceable(1) [[THIS:%.*]]) #[[ATTR0]] align 2 {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 4
+// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 4
+// CHECK-NEXT: ret void
+//
+ void f() {}
+};
+
+groupshared G g;
+
+void case7() {
+ // implicit argument passing of a groupshared struct
+ g.f();
+}
>From 4437bff2ca71e485c76f4bdb8ebaa58aac64cbfc Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Wed, 7 Oct 2026 16:47:10 -0700
Subject: [PATCH 2/5] add new test. change test file so update script won't
break new tests
---
.../BasicFeatures/StructPassing.hlsl | 59 +++++++++++++++----
1 file changed, 48 insertions(+), 11 deletions(-)
diff --git a/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
index a846f682493fa6..07455027a1ac71 100644
--- a/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
+++ b/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
@@ -134,19 +134,33 @@ void case6(S s) {
useP(s);
}
+// disabled otherwise the IR will be generated in the wrong order.
+// UTC_ARGS: --disable
+
+struct G {
+ int X;
+ void f() {}
+ void setX(int val) { this.X = val; }
+};
+
+groupshared G g;
+
// CHECK-LABEL: define hidden void @_Z5case7v(
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_G:%.*]], align 1
-// CHECK-NEXT: call void @_ZN1G1fEv(ptr noundef nonnull align 1 dereferenceable(1) [[REF_TMP]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
+// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[REF_TMP]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
+// CHECK-NEXT: call void @_ZN1G1fEv(ptr noundef nonnull align 1 dereferenceable(4) [[REF_TMP]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
// CHECK-NEXT: ret void
//
-void case7();
+void case7() {
+ // implicit argument passing of a groupshared struct
+ g.f();
+}
-struct G {
// CHECK-LABEL: define linkonce_odr hidden void @_ZN1G1fEv(
-// CHECK-SAME: ptr noundef nonnull align 1 dereferenceable(1) [[THIS:%.*]]) #[[ATTR0]] align 2 {
+// CHECK-SAME: ptr noundef nonnull align 1 dereferenceable(4) [[THIS:%.*]]) #[[ATTR0]] align 2 {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 4
@@ -154,12 +168,35 @@ struct G {
// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 4
// CHECK-NEXT: ret void
//
- void f() {}
-};
-
-groupshared G g;
-void case7() {
- // implicit argument passing of a groupshared struct
- g.f();
+// CHECK-LABEL: define hidden void @_Z5case8i(
+// CHECK-SAME: i32 noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_G:%.*]], align 1
+// CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[REF_TMP]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
+// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT: call void @_ZN1G4setXEi(ptr noundef nonnull align 1 dereferenceable(4) [[REF_TMP]], i32 noundef [[TMP1]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
+// CHECK-NEXT: ret void
+//
+void case8(int val) {
+ // implicit argument passing of a groupshared struct where the implicit object is modified
+ g.setX(val);
}
+
+// CHECK-LABEL: define linkonce_odr hidden void @_ZN1G4setXEi(
+// CHECK-SAME: ptr noundef nonnull align 1 dereferenceable(4) [[THIS:%.*]], i32 noundef [[VAL:%.*]]) #[[ATTR0]] align 2 {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 4
+// CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 4
+// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_G:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK-NEXT: store i32 [[TMP1]], ptr [[X]], align 1
+// CHECK-NEXT: ret void
+//
>From 641eb2fbb5864421d21c99b5dc813131e6ff2fbe Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Thu, 8 Oct 2026 08:31:52 -0700
Subject: [PATCH 3/5] respond to pr comment
---
clang/lib/Sema/SemaOverload.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 44b397d2a47b93..d90ef3a2a49bb3 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6348,10 +6348,10 @@ ExprResult Sema::PerformImplicitObjectArgumentInitialization(
if (getLangOpts().HLSL &&
(FromAS == LangAS::hlsl_constant || FromAS == LangAS::hlsl_groupshared)) {
QualType CastType = From->getType().getLocalUnqualifiedType();
- From = ImplicitCastExpr::Create(
- Context,
- FromAS == LangAS::hlsl_constant ? CastType.withConst() : CastType,
- CK_LValueToRValue, From,
+ if (FromAS == LangAS::hlsl_constant)
+ CastType = CastType.withConst();
+
+ From = ImplicitCastExpr::Create(Context, CastType, CK_LValueToRValue, From,
/*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride());
}
>From 961e9d6e4e0acb162f081eaf8064715d14f50a2c Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Thu, 8 Oct 2026 10:20:59 -0700
Subject: [PATCH 4/5] format
---
clang/lib/Sema/SemaOverload.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index d90ef3a2a49bb3..1876f547db836e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6350,9 +6350,10 @@ ExprResult Sema::PerformImplicitObjectArgumentInitialization(
QualType CastType = From->getType().getLocalUnqualifiedType();
if (FromAS == LangAS::hlsl_constant)
CastType = CastType.withConst();
-
+
From = ImplicitCastExpr::Create(Context, CastType, CK_LValueToRValue, From,
- /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride());
+ /*BasePath=*/nullptr, VK_PRValue,
+ FPOptionsOverride());
}
Expr::Classification FromClassification;
>From cec374d943d58ba0df937381f1e06eab6c870834 Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Wed, 14 Oct 2026 09:13:35 -0700
Subject: [PATCH 5/5] update so we use hlsl in out expr for implicit object
param
---
clang/lib/CodeGen/CGExpr.cpp | 6 +-
clang/lib/CodeGen/CGExprCXX.cpp | 16 ++++-
clang/lib/CodeGen/CodeGenFunction.h | 2 +-
clang/lib/Sema/SemaOverload.cpp | 31 ++++++++--
clang/test/AST/HLSL/StructPassing-AST.hlsl | 60 +++++++++++++++++--
.../BasicFeatures/StructPassing.hlsl | 49 ++++++++++++---
6 files changed, 142 insertions(+), 22 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 8323a170a1dbbc..4fc208550f2710 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6585,7 +6585,8 @@ CodeGenFunction::EmitHLSLOutArgLValues(const HLSLOutArgExpr *E, QualType Ty) {
}
LValue CodeGenFunction::EmitHLSLOutArgExpr(const HLSLOutArgExpr *E,
- CallArgList &Args, QualType Ty) {
+ CallArgList &Args, QualType Ty,
+ bool IsImplicitObject) {
auto [BaseLV, TempLV] = EmitHLSLOutArgLValues(E, Ty);
@@ -6594,7 +6595,8 @@ LValue CodeGenFunction::EmitHLSLOutArgExpr(const HLSLOutArgExpr *E,
Address TmpAddr(Addr, ElTy, TempLV.getAlignment());
Args.addWriteback(BaseLV, TmpAddr, nullptr, E->getWritebackCast());
- Args.add(RValue::get(TmpAddr, *this), Ty);
+ if (!IsImplicitObject)
+ Args.add(RValue::get(TmpAddr, *this), Ty);
return TempLV;
}
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 8279611accba33..46d50e26533ac6 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -278,8 +278,18 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
}
}
- auto getLValueForThis = [this, IsArrow,
- Base](bool EmitCheckedForStore = false) {
+ LValue HLSLThisLValue;
+ if (auto *HLSLOutArg = dyn_cast<HLSLOutArgExpr>(Base)) {
+ if (!RtlArgs)
+ RtlArgs = &RtlArgStorage;
+ HLSLThisLValue = EmitHLSLOutArgExpr(
+ HLSLOutArg, *RtlArgs, HLSLOutArg->getType(), /*IsImplicitObject=*/true);
+ EmitCallArgs(*RtlArgs, MD->getType()->castAs<FunctionProtoType>(),
+ drop_begin(CE->arguments(), 0), CE->getDirectCallee());
+ }
+
+ auto getLValueForThis = [this, IsArrow, Base,
+ HLSLThisLValue](bool EmitCheckedForStore = false) {
// FIXME: Respect EmitCheckedForStore for the IsArrow case.
if (IsArrow) {
LValueBaseInfo BaseInfo;
@@ -288,6 +298,8 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr(
return MakeAddrLValue(ThisValue, Base->getType()->getPointeeType(),
BaseInfo, TBAAInfo);
}
+ if (isa<HLSLOutArgExpr>(Base))
+ return HLSLThisLValue;
if (EmitCheckedForStore)
return EmitCheckedLValue(Base, TCK_Store);
return EmitLValue(Base);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 00294ad547d5ec..28fef2193b5164 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4586,7 +4586,7 @@ class CodeGenFunction : public CodeGenTypeCache {
std::pair<LValue, LValue> EmitHLSLOutArgLValues(const HLSLOutArgExpr *E,
QualType Ty);
LValue EmitHLSLOutArgExpr(const HLSLOutArgExpr *E, CallArgList &Args,
- QualType Ty);
+ QualType Ty, bool IsImplicitObject = false);
Address EmitExtVectorElementLValue(LValue V);
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 1876f547db836e..0746d28378ece3 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6346,14 +6346,35 @@ ExprResult Sema::PerformImplicitObjectArgumentInitialization(
LangAS FromAS = From->getType().getAddressSpace();
if (getLangOpts().HLSL &&
- (FromAS == LangAS::hlsl_constant || FromAS == LangAS::hlsl_groupshared)) {
- QualType CastType = From->getType().getLocalUnqualifiedType();
- if (FromAS == LangAS::hlsl_constant)
- CastType = CastType.withConst();
-
+ (FromAS == LangAS::hlsl_constant ||
+ (FromAS == LangAS::hlsl_groupshared && Method->isConst()))) {
+ QualType CastType = From->getType().getLocalUnqualifiedType().withConst();
From = ImplicitCastExpr::Create(Context, CastType, CK_LValueToRValue, From,
/*BasePath=*/nullptr, VK_PRValue,
FPOptionsOverride());
+ } else if (getLangOpts().HLSL && FromAS == LangAS::hlsl_groupshared) {
+ ASTContext &Ctx = SemaRef.getASTContext();
+ QualType Ty =
+ From->getType().getLocalUnqualifiedType().getNonLValueExprType(Ctx);
+ InitializedEntity Entity = InitializedEntity::InitializeTemporary(Ty);
+ auto *FromOpV = new (Ctx) OpaqueValueExpr(
+ From->getBeginLoc(), From->getType().getLocalUnqualifiedType(),
+ VK_LValue, OK_Ordinary, From);
+ ExprResult Res =
+ SemaRef.PerformCopyInitialization(Entity, From->getBeginLoc(), From);
+ if (Res.isInvalid())
+ return ExprError();
+ Expr *Base = Res.get();
+ auto *OpV = new (Ctx)
+ OpaqueValueExpr(From->getBeginLoc(), Ty, VK_LValue, OK_Ordinary, Base);
+
+ Res = SemaRef.ActOnBinOp(SemaRef.getCurScope(), From->getBeginLoc(),
+ tok::equal, FromOpV, OpV);
+ if (Res.isInvalid())
+ return ExprError();
+ Expr *Writeback = Res.get();
+ From = HLSLOutArgExpr::Create(Ctx, Ty, FromOpV, OpV, Writeback,
+ /*IsInOut=*/true);
}
Expr::Classification FromClassification;
diff --git a/clang/test/AST/HLSL/StructPassing-AST.hlsl b/clang/test/AST/HLSL/StructPassing-AST.hlsl
index 745187514bd432..c8c87b46b9e0a7 100644
--- a/clang/test/AST/HLSL/StructPassing-AST.hlsl
+++ b/clang/test/AST/HLSL/StructPassing-AST.hlsl
@@ -119,22 +119,72 @@ void case6(S s) {
}
struct G {
+ int X;
void f() {}
+ void setX(int val) { X = val; }
+ int getX() const { return X; }
};
groupshared G g;
-// CHECK: FunctionDecl {{.*}} case7 'void ()'
-// CHECK-NEXT: CompoundStmt {{.*}}
-// CHECK-NEXT: ExprWithCleanups {{.*}} 'void'
+// CHECK: FunctionDecl {{.*}} case7 'void ()' external-linkage
+// CHECK-NEXT: CompoundStmt
// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'void'
-// CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .f {{.*}}
-// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .f
+// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'G' lvalue inout
+// CHECK-NEXT: OpaqueValueExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+// CHECK-NEXT: OpaqueValueExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'G' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+// CHECK-NEXT: BinaryOperator {{.*}} 'G' lvalue '='
+// CHECK-NEXT: OpaqueValueExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'G' <LValueToRValue>
+// CHECK-NEXT: OpaqueValueExpr {{.*}} 'G' lvalue
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'G' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
void case7() {
g.f();
}
+// CHECK: FunctionDecl {{.*}} case8 'void (int)' external-linkage
+// CHECK-NEXT: ParmVarDecl {{.*}} used V 'int'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'void'
+// CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .setX
+// CHECK-NEXT: HLSLOutArgExpr {{.*}} 'G' lvalue inout
+// CHECK-NEXT: OpaqueValueExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+// CHECK-NEXT: OpaqueValueExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'G' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+// CHECK-NEXT: BinaryOperator {{.*}} 'G' lvalue '='
+// CHECK-NEXT: OpaqueValueExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'G' <LValueToRValue>
+// CHECK-NEXT: OpaqueValueExpr {{.*}} 'G' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'G' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'V' 'int'
+void case8(int V) {
+ g.setX(V);
+}
+
+// CHECK: FunctionDecl {{.*}} case9 'void ()'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: DeclStmt
+// CHECK-NEXT: VarDecl {{.*}} Y 'int' cinit
+// CHECK-NEXT: ExprWithCleanups {{.*}} 'int'
+// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'int'
+// CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .getX
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'const G' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'const G' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'groupshared G' lvalue Var {{.*}} 'g' 'groupshared G'
+void case9() {
+ int Y = g.getX();
+}
+
// CHECK-NOT: CXXConstructExpr
// CHECK-NOT: CXXOperatorCallExpr
diff --git a/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
index 07455027a1ac71..382bf0cf860442 100644
--- a/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
+++ b/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
@@ -141,6 +141,7 @@ struct G {
int X;
void f() {}
void setX(int val) { this.X = val; }
+ int getX() const { return X; }
};
groupshared G g;
@@ -149,9 +150,12 @@ groupshared G g;
// CHECK-SAME: ) #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
-// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_G:%.*]], align 1
-// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[REF_TMP]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
-// CHECK-NEXT: call void @_ZN1G1fEv(ptr noundef nonnull align 1 dereferenceable(4) [[REF_TMP]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
+// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_G:%.*]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = alloca [[STRUCT_G]], align 1
+// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[TMP]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
+// CHECK-NEXT: call void @_ZN1G1fEv(ptr noundef nonnull align 1 dereferenceable(4) [[TMP]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
+// CHECK-NEXT: call void @llvm.memcpy.p3.p0.i32(ptr addrspace(3) align 1 @g, ptr align 1 [[TMP]], i32 4, i1 false)
+// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[TMP1]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
// CHECK-NEXT: ret void
//
void case7() {
@@ -174,11 +178,14 @@ void case7() {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
// CHECK-NEXT: [[VAL_ADDR:%.*]] = alloca i32, align 4
-// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_G:%.*]], align 1
+// CHECK-NEXT: [[TMP:%.*]] = alloca [[STRUCT_G:%.*]], align 1
+// CHECK-NEXT: [[TMP1:%.*]] = alloca [[STRUCT_G]], align 1
// CHECK-NEXT: store i32 [[VAL]], ptr [[VAL_ADDR]], align 4
-// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[REF_TMP]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
-// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[VAL_ADDR]], align 4
-// CHECK-NEXT: call void @_ZN1G4setXEi(ptr noundef nonnull align 1 dereferenceable(4) [[REF_TMP]], i32 noundef [[TMP1]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
+// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[TMP]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
+// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT: call void @_ZN1G4setXEi(ptr noundef nonnull align 1 dereferenceable(4) [[TMP]], i32 noundef [[TMP2]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
+// CHECK-NEXT: call void @llvm.memcpy.p3.p0.i32(ptr addrspace(3) align 1 @g, ptr align 1 [[TMP]], i32 4, i1 false)
+// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[TMP1]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
// CHECK-NEXT: ret void
//
void case8(int val) {
@@ -200,3 +207,31 @@ void case8(int val) {
// CHECK-NEXT: store i32 [[TMP1]], ptr [[X]], align 1
// CHECK-NEXT: ret void
//
+
+// CHECK-LABEL: define hidden void @_Z5case9v(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-NEXT: [[Y:%.*]] = alloca i32, align 4
+// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT_G:%.*]], align 1
+// CHECK-NEXT: call void @llvm.memcpy.p0.p3.i32(ptr align 1 [[REF_TMP]], ptr addrspace(3) align 1 @g, i32 4, i1 false)
+// CHECK-NEXT: [[CALL1:%.*]] = call noundef i32 @_ZNK1G4getXEv(ptr noundef nonnull align 1 dereferenceable(4) [[REF_TMP]]) #[[ATTR3]] [ "convergencectrl"(token [[TMP0]]) ]
+// CHECK-NEXT: store i32 [[CALL1]], ptr [[Y]], align 4
+// CHECK-NEXT: ret void
+//
+void case9() {
+ // implicit argument passing of a groupshared struct where the function is const
+ int Y = g.getX();
+}
+
+// CHECK-LABEL: define linkonce_odr hidden noundef i32 @_ZNK1G4getXEv(
+// CHECK-SAME: ptr noundef nonnull align 1 dereferenceable(4) [[THIS:%.*]]) #[[ATTR0]] align 2 {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 4
+// CHECK-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 4
+// CHECK-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 4
+// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_G:%.*]], ptr [[THIS1]], i32 0, i32 0
+// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[X]], align 1
+// CHECK-NEXT: ret i32 [[TMP1]]
+//
More information about the cfe-commits
mailing list