[clang] dcdcfb1 - [HLSL] Allow a groupshared struct to be passed as an implicit parameter (#229939)

via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 20 14:53:38 PDT 2026


Author: Sarah Spall
Date: 2026-10-20T14:53:33-07:00
New Revision: dcdcfb1a48b8a214e7a5eb7973ed21650e5fd581

URL: https://github.com/llvm/llvm-project/commit/dcdcfb1a48b8a214e7a5eb7973ed21650e5fd581
DIFF: https://github.com/llvm/llvm-project/commit/dcdcfb1a48b8a214e7a5eb7973ed21650e5fd581.diff

LOG: [HLSL] Allow a groupshared struct to be passed as an implicit parameter (#229939)

If the implicit object parameter is in the groupshared address space and
the function is const then perform an LValue to RValue cast.
If the implicit object parameter is in the groupshared address space
then create an HSLOutArgExpr which handles the writeback of the object
from the temporary.
Adds an AST test and a Codegen test.
Closes #228563

Added: 
    

Modified: 
    clang/include/clang/Sema/SemaHLSL.h
    clang/lib/CodeGen/CGExpr.cpp
    clang/lib/CodeGen/CGExprCXX.cpp
    clang/lib/CodeGen/CodeGenFunction.h
    clang/lib/Sema/SemaHLSL.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/test/AST/HLSL/StructPassing-AST.hlsl
    clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 3d62f47dfde69..00be1b9b06e91 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -224,6 +224,9 @@ class SemaHLSL : public SemaBase {
   bool CanPerformElementwiseCast(Expr *Src, QualType DestType);
   bool CanPerformAggregateSplatCast(Expr *Src, QualType DestType);
   bool CanPerformPackedTypeCast(Expr *Src, QualType DestTy);
+  ExprResult CreateOutArgExpr(Expr *Arg, QualType ArgTy, QualType ParamTy,
+                              InitializedEntity Entity, SourceLocation Loc,
+                              bool IsInOut);
   ExprResult ActOnOutParamExpr(ParmVarDecl *Param, Expr *Arg);
 
   QualType getInoutParameterType(QualType Ty);

diff  --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 3953564acf0b2..beebd67116e5f 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -6525,7 +6525,8 @@ CodeGenFunction::EmitHLSLOutArgLValues(const HLSLOutArgExpr *E, QualType Ty) {
 }
 
 LValue CodeGenFunction::EmitHLSLOutArgExpr(const HLSLOutArgExpr *E,
-                                           CallArgList &Args, QualType Ty) {
+                                           CallArgList &Args, QualType Ty,
+                                           bool IsImplicit) {
 
   auto [BaseLV, TempLV] = EmitHLSLOutArgLValues(E, Ty);
 
@@ -6534,7 +6535,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 (!IsImplicit)
+    Args.add(RValue::get(TmpAddr, *this), Ty);
   return TempLV;
 }
 

diff  --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index a1b3e28277b35..60bb27a6fab69 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) {
+  std::optional<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 (HLSLThisLValue)
+      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 a2ac20dfafac1..733d9ab310aff 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -4584,7 +4584,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/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 4fe7ed151e918..b25b21777495f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -5756,7 +5756,42 @@ bool SemaHLSL::CanPerformPackedTypeCast(Expr *Src, QualType DestTy) {
           Ctx.hasSameUnqualifiedType(SrcTy, UIntTy));
 }
 
+ExprResult SemaHLSL::CreateOutArgExpr(Expr *Arg, QualType ArgTy,
+                                      QualType ParamTy,
+                                      InitializedEntity Entity,
+                                      SourceLocation Loc, bool IsInOut) {
+  ASTContext &Ctx = SemaRef.getASTContext();
+
+  QualType Ty = ParamTy.getNonLValueExprType(Ctx);
+
+  auto *ArgOpV =
+      new (Ctx) OpaqueValueExpr(Loc, ArgTy, VK_LValue, OK_Ordinary, Arg);
+
+  // Parameters are initialized via copy initialization. This allows for
+  // overload resolution of argument constructors.
+  ExprResult Res = SemaRef.PerformCopyInitialization(Entity, Loc, ArgOpV);
+  if (Res.isInvalid())
+    return ExprError();
+  Expr *Base = Res.get();
+  // After the cast, drop the reference type when creating the exprs.
+  Ty = Ty.getNonLValueExprType(Ctx);
+  auto *OpV = new (Ctx) OpaqueValueExpr(Loc, Ty, VK_LValue, OK_Ordinary, Base);
+
+  // Writebacks are performed with `=` binary operator, which allows for
+  // overload resolution on writeback result expressions.
+  Res = SemaRef.ActOnBinOp(SemaRef.getCurScope(), Arg->getBeginLoc(),
+                           tok::equal, ArgOpV, OpV);
+
+  if (Res.isInvalid())
+    return ExprError();
+  Expr *Writeback = Res.get();
+  auto *OutExpr =
+      HLSLOutArgExpr::Create(Ctx, Ty, ArgOpV, OpV, Writeback, IsInOut);
+  return ExprResult(OutExpr);
+}
+
 ExprResult SemaHLSL::ActOnOutParamExpr(ParmVarDecl *Param, Expr *Arg) {
+  ASTContext &Ctx = SemaRef.getASTContext();
   assert(Param->hasAttr<HLSLParamModifierAttr>() &&
          "We should not get here without a parameter modifier expression");
   const auto *Attr = Param->getAttr<HLSLParamModifierAttr>();
@@ -5770,8 +5805,6 @@ ExprResult SemaHLSL::ActOnOutParamExpr(ParmVarDecl *Param, Expr *Arg) {
     return ExprError();
   }
 
-  ASTContext &Ctx = SemaRef.getASTContext();
-
   QualType Ty = Param->getType().getNonLValueExprType(Ctx);
 
   // HLSL allows implicit conversions from scalars to vectors, but not the
@@ -5783,35 +5816,11 @@ ExprResult SemaHLSL::ActOnOutParamExpr(ParmVarDecl *Param, Expr *Arg) {
     return ExprError();
   }
 
-  auto *ArgOpV = new (Ctx) OpaqueValueExpr(Param->getBeginLoc(), Arg->getType(),
-                                           VK_LValue, OK_Ordinary, Arg);
-
-  // Parameters are initialized via copy initialization. This allows for
-  // overload resolution of argument constructors.
   InitializedEntity Entity =
       InitializedEntity::InitializeParameter(Ctx, Ty, false);
-  ExprResult Res =
-      SemaRef.PerformCopyInitialization(Entity, Param->getBeginLoc(), ArgOpV);
-  if (Res.isInvalid())
-    return ExprError();
-  Expr *Base = Res.get();
-  // After the cast, drop the reference type when creating the exprs.
-  Ty = Ty.getNonLValueExprType(Ctx);
-  auto *OpV = new (Ctx)
-      OpaqueValueExpr(Param->getBeginLoc(), Ty, VK_LValue, OK_Ordinary, Base);
 
-  // Writebacks are performed with `=` binary operator, which allows for
-  // overload resolution on writeback result expressions.
-  Res = SemaRef.ActOnBinOp(SemaRef.getCurScope(), Arg->getBeginLoc(),
-                           tok::equal, ArgOpV, OpV);
-
-  if (Res.isInvalid())
-    return ExprError();
-  Expr *Writeback = Res.get();
-  auto *OutExpr =
-      HLSLOutArgExpr::Create(Ctx, Ty, ArgOpV, OpV, Writeback, IsInOut);
-
-  return ExprResult(OutExpr);
+  return CreateOutArgExpr(Arg, Arg->getType(), Param->getType(), Entity,
+                          Param->getBeginLoc(), IsInOut);
 }
 
 QualType SemaHLSL::getInoutParameterType(QualType Ty) {

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 2ca12f7a172e7..0aa794b0907b0 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -34,6 +34,7 @@
 #include "clang/Sema/SemaAMDGPU.h"
 #include "clang/Sema/SemaARM.h"
 #include "clang/Sema/SemaCUDA.h"
+#include "clang/Sema/SemaHLSL.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/Template.h"
@@ -6344,12 +6345,24 @@ ExprResult Sema::PerformImplicitObjectArgumentInitialization(
   QualType FromRecordType, DestType;
   QualType ImplicitParamRecordType = Method->getFunctionObjectParameterType();
 
+  LangAS FromAS = From->getType().getAddressSpace();
   if (getLangOpts().HLSL &&
-      From->getType().getAddressSpace() == LangAS::hlsl_constant) {
+      (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();
+    InitializedEntity TempEntity =
+        InitializedEntity::InitializeTemporary(Ty.getNonLValueExprType(Ctx));
+    ExprResult Res = SemaRef.HLSL().CreateOutArgExpr(
+        From, Ty, Ty, TempEntity, From->getBeginLoc(), /*IsInOut=*/true);
+    if (Res.isInvalid())
+      return ExprError();
+    From = Res.get();
   }
 
   Expr::Classification FromClassification;

diff  --git a/clang/test/AST/HLSL/StructPassing-AST.hlsl b/clang/test/AST/HLSL/StructPassing-AST.hlsl
index 8ae83fa89b390..08fdd6d7ed612 100644
--- a/clang/test/AST/HLSL/StructPassing-AST.hlsl
+++ b/clang/test/AST/HLSL/StructPassing-AST.hlsl
@@ -118,5 +118,77 @@ void case6(S s) {
   useP(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 ()' external-linkage
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'void'
+// 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: OpaqueValueExpr {{.*}} 'G' lvalue
+// 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: OpaqueValueExpr {{.*}} 'G' lvalue
+// 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: OpaqueValueExpr {{.*}} 'G' lvalue
+// 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: OpaqueValueExpr {{.*}} 'G' lvalue
+// 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 c80c7c9466afc..382bf0cf86044 100644
--- a/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
+++ b/clang/test/CodeGenHLSL/BasicFeatures/StructPassing.hlsl
@@ -133,3 +133,105 @@ void case6(S s) {
   // derived to base conversion in argument passing
   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; }
+  int getX() const { return X; }
+};
+
+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:    [[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() {
+  // implicit argument passing of a groupshared struct
+  g.f();
+}
+
+// CHECK-LABEL: define linkonce_odr hidden void @_ZN1G1fEv(
+// 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:    ret void
+//
+
+// 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:    [[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 [[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) {
+  // 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
+//
+
+// 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