[HLSL] Added HLSL this as a reference
authorGrace Jennings <gracejennings@gmail.com>
Mon, 7 Nov 2022 21:16:54 +0000 (13:16 -0800)
committerXiang Li <python3kgae@outlook.com>
Mon, 7 Nov 2022 21:50:08 +0000 (13:50 -0800)
This change makes `this` a reference instead of a pointer in
HLSL. HLSL does not have the `->` operator, and accesses through `this`
are with the `.` syntax.

Tests were added and altered to make sure
the AST accurately reflects the types.

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D135721

13 files changed:
clang/lib/AST/ExprClassification.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/Sema/HLSLExternalSemaSource.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaExprMember.cpp
clang/test/AST/HLSL/RWBuffer-AST.hlsl
clang/test/AST/HLSL/this-reference-template.hlsl [new file with mode: 0644]
clang/test/AST/HLSL/this-reference.hlsl [new file with mode: 0644]
clang/test/CodeGenHLSL/this-assignment-overload.hlsl [new file with mode: 0644]
clang/test/CodeGenHLSL/this-assignment.hlsl [new file with mode: 0644]
clang/test/CodeGenHLSL/this-reference.hlsl [new file with mode: 0644]

index 6c122ca..88081d9 100644 (file)
@@ -160,7 +160,6 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::CXXPseudoDestructorExprClass:
   case Expr::UnaryExprOrTypeTraitExprClass:
   case Expr::CXXNewExprClass:
-  case Expr::CXXThisExprClass:
   case Expr::CXXNullPtrLiteralExprClass:
   case Expr::ImaginaryLiteralClass:
   case Expr::GNUNullExprClass:
@@ -205,6 +204,10 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
   case Expr::RequiresExprClass:
     return Cl::CL_PRValue;
 
+  // Make HLSL this reference-like
+  case Expr::CXXThisExprClass:
+    return Lang.HLSL ? Cl::CL_LValue : Cl::CL_PRValue;
+
   case Expr::ConstantExprClass:
     return ClassifyInternal(Ctx, cast<ConstantExpr>(E)->getSubExpr());
 
index 493b340..40d9b8f 100644 (file)
@@ -1383,6 +1383,8 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
     return EmitOMPArraySectionExpr(cast<OMPArraySectionExpr>(E));
   case Expr::ExtVectorElementExprClass:
     return EmitExtVectorElementExpr(cast<ExtVectorElementExpr>(E));
+  case Expr::CXXThisExprClass:
+    return MakeAddrLValue(LoadCXXThisAddress(), E->getType());
   case Expr::MemberExprClass:
     return EmitMemberExpr(cast<MemberExpr>(E));
   case Expr::CompoundLiteralExprClass:
index f1b90fa..7459d9c 100644 (file)
@@ -175,9 +175,11 @@ struct BuiltinTypeDeclBuilder {
     Expr *Call = CallExpr::Create(AST, Fn, {RCExpr}, AST.VoidPtrTy, VK_PRValue,
                                   SourceLocation(), FPOptionsOverride());
 
-    CXXThisExpr *This = new (AST)
-        CXXThisExpr(SourceLocation(), Constructor->getThisType(), true);
-    Expr *Handle = MemberExpr::CreateImplicit(AST, This, true, Fields["h"],
+    CXXThisExpr *This = new (AST) CXXThisExpr(
+        SourceLocation(),
+        Constructor->getThisType().getTypePtr()->getPointeeType(), true);
+    This->setValueKind(ExprValueKind::VK_LValue);
+    Expr *Handle = MemberExpr::CreateImplicit(AST, This, false, Fields["h"],
                                               Fields["h"]->getType(), VK_LValue,
                                               OK_Ordinary);
 
@@ -260,10 +262,12 @@ struct BuiltinTypeDeclBuilder {
     auto FnProtoLoc = TSInfo->getTypeLoc().getAs<FunctionProtoTypeLoc>();
     FnProtoLoc.setParam(0, IdxParam);
 
-    auto *This = new (AST)
-        CXXThisExpr(SourceLocation(), MethodDecl->getThisType(), true);
+    auto *This = new (AST) CXXThisExpr(
+        SourceLocation(),
+        MethodDecl->getThisType().getTypePtr()->getPointeeType(), true);
+    This->setValueKind(ExprValueKind::VK_LValue);
     auto *HandleAccess = MemberExpr::CreateImplicit(
-        AST, This, true, Handle, Handle->getType(), VK_LValue, OK_Ordinary);
+        AST, This, false, Handle, Handle->getType(), VK_LValue, OK_Ordinary);
 
     auto *IndexExpr = DeclRefExpr::Create(
         AST, NestedNameSpecifierLoc(), SourceLocation(), IdxParam, false,
index ea7997b..73603b5 100644 (file)
@@ -14681,7 +14681,8 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
 
     MemberBuilder From(OtherRef, OtherRefType, /*IsArrow=*/false, MemberLookup);
 
-    MemberBuilder To(This, getCurrentThisType(), /*IsArrow=*/true, MemberLookup);
+    MemberBuilder To(This, getCurrentThisType(), /*IsArrow=*/!LangOpts.HLSL,
+                     MemberLookup);
 
     // Build the copy of this field.
     StmtResult Copy = buildSingleCopyAssign(*this, Loc, FieldType,
@@ -14699,9 +14700,16 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation,
 
   if (!Invalid) {
     // Add a "return *this;"
-    ExprResult ThisObj = CreateBuiltinUnaryOp(Loc, UO_Deref, This.build(*this, Loc));
+    Expr *ThisExpr = nullptr;
+    if (!LangOpts.HLSL) {
+      ExprResult ThisObj =
+          CreateBuiltinUnaryOp(Loc, UO_Deref, This.build(*this, Loc));
+      ThisExpr = ThisObj.get();
+    } else {
+      ThisExpr = This.build(*this, Loc);
+    }
 
-    StmtResult Return = BuildReturnStmt(Loc, ThisObj.get());
+    StmtResult Return = BuildReturnStmt(Loc, ThisExpr);
     if (Return.isInvalid())
       Invalid = true;
     else
index 2493b4a..c40bcb0 100644 (file)
@@ -15587,7 +15587,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
     }
   }
 
-  if (getLangOpts().HLSL) {
+  if (getLangOpts().HLSL && OpLoc.isValid()) {
     if (Opc == UO_AddrOf)
       return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 0);
     if (Opc == UO_Deref)
index 41c4348..c093eab 100644 (file)
@@ -1390,6 +1390,13 @@ ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
 
 Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,
                              bool IsImplicit) {
+  if (getLangOpts().HLSL && Type.getTypePtr()->isPointerType()) {
+    auto *This = new (Context)
+        CXXThisExpr(Loc, Type.getTypePtr()->getPointeeType(), IsImplicit);
+    This->setValueKind(ExprValueKind::VK_LValue);
+    MarkThisReferenced(This);
+    return This;
+  }
   auto *This = new (Context) CXXThisExpr(Loc, Type, IsImplicit);
   MarkThisReferenced(This);
   return This;
index fc68b52..8eeed1a 100644 (file)
@@ -1903,6 +1903,14 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
     if (SS.getRange().isValid())
       Loc = SS.getRange().getBegin();
     baseExpr = BuildCXXThisExpr(loc, ThisTy, /*IsImplicit=*/true);
+    if (getLangOpts().HLSL && ThisTy.getTypePtr()->isPointerType()) {
+      ThisTy = ThisTy.getTypePtr()->getPointeeType();
+      return BuildMemberReferenceExpr(baseExpr, ThisTy,
+                                      /*OpLoc*/ SourceLocation(),
+                                      /*IsArrow*/ false, SS, TemplateKWLoc,
+                                      /*FirstQualifierInScope*/ nullptr, R,
+                                      TemplateArgs, S);
+    }
   }
 
   return BuildMemberReferenceExpr(baseExpr, ThisTy,
index 0929462..9dd9244 100644 (file)
@@ -46,8 +46,8 @@ RWBuffer<float> Buffer;
 // CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>>
 // CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>>
 // CHECK-NEXT: ArraySubscriptExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'element_type' lvalue
-// CHECK-NEXT: MemberExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'element_type *' lvalue ->h 0x{{[0-9A-Fa-f]+}}
-// CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'const RWBuffer<element_type> *' implicit this
+// CHECK-NEXT: MemberExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'element_type *' lvalue .h 0x{{[0-9A-Fa-f]+}}
+// CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'const RWBuffer<element_type>' lvalue implicit this
 // CHECK-NEXT: DeclRefExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'unsigned int' ParmVar 0x{{[0-9A-Fa-f]+}} 'Idx' 'unsigned int'
 // CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit always_inline
 
@@ -56,8 +56,8 @@ RWBuffer<float> Buffer;
 // CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>>
 // CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>>
 // CHECK-NEXT: ArraySubscriptExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'element_type' lvalue
-// CHECK-NEXT: MemberExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'element_type *' lvalue ->h 0x{{[0-9A-Fa-f]+}}
-// CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'RWBuffer<element_type> *' implicit this
+// CHECK-NEXT: MemberExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'element_type *' lvalue .h 0x{{[0-9A-Fa-f]+}}
+// CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'RWBuffer<element_type>' lvalue implicit this
 // CHECK-NEXT: DeclRefExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'unsigned int' ParmVar 0x{{[0-9A-Fa-f]+}} 'Idx' 'unsigned int'
 // CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit always_inline
 
diff --git a/clang/test/AST/HLSL/this-reference-template.hlsl b/clang/test/AST/HLSL/this-reference-template.hlsl
new file mode 100644 (file)
index 0000000..c27d69d
--- /dev/null
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -disable-llvm-passes -o - -hlsl-entry main %s | FileCheck %s\r
+\r
+template<typename K, typename V>\r
+struct Pair {\r
+  K First;\r
+  V Second;\r
+\r
+  K getFirst() {\r
+         return this.First;\r
+  }\r
+\r
+  V getSecond() {\r
+    return Second;\r
+  }\r
+};\r
+\r
+[numthreads(1, 1, 1)]\r
+void main() {\r
+  Pair<int, float> Vals = {1, 2.0};\r
+  Vals.First = Vals.getFirst();\r
+  Vals.Second = Vals.getSecond();\r
+}\r
+\r
+// CHECK:     -CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:8:3, line:10:3> line:8:5 getFirst 'K ()' implicit-inline\r
+// CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:16, line:10:3>\r
+// CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:9:4, col:16>\r
+// CHECK-NEXT:-CXXDependentScopeMemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> '<dependent type>' lvalue .First\r
+// CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:11> 'Pair<K, V>' lvalue this\r
+// CHECK-NEXT:-CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:12:3, line:14:3> line:12:5 getSecond 'V ()' implicit-inline\r
+// CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:17, line:14:3>\r
+// CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:13:5, col:12>\r
+// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'V' lvalue .Second 0x{{[0-9A-Fa-f]+}}\r
+// CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'Pair<K, V>' lvalue implicit this\r
+\r
+// CHECK:     -CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:8:3, line:10:3> line:8:5 used getFirst 'int ()' implicit-inline\r
+// CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:16, line:10:3>\r
+// CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:9:4, col:16>\r
+// CHECK-NEXT:-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> 'int':'int' <LValueToRValue>\r
+// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> 'int':'int' lvalue .First 0x{{[0-9A-Fa-f]+}}\r
+// CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:11> 'Pair<int, float>' lvalue this\r
+// CHECK-NEXT:-CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:12:3, line:14:3> line:12:5 used getSecond 'float ()' implicit-inline\r
+// CHECK-NEXT:-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:17, line:14:3>\r
+// CHECK-NEXT:-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:13:5, col:12>\r
+// CHECK-NEXT:-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'float':'float' <LValueToRValue>\r
+// CHECK-NEXT:-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'float':'float' lvalue .Second 0x{{[0-9A-Fa-f]+}}\r
+// CHECK-NEXT:-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'Pair<int, float>' lvalue implicit this\r
diff --git a/clang/test/AST/HLSL/this-reference.hlsl b/clang/test/AST/HLSL/this-reference.hlsl
new file mode 100644 (file)
index 0000000..67d8e7b
--- /dev/null
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -ast-dump -disable-llvm-passes -o - -hlsl-entry main %s | FileCheck %s\r
+\r
+class Pair {\r
+  int First;\r
+  int Second;\r
+\r
+  int getFirst() {\r
+         return this.First;\r
+  }\r
+\r
+  int getSecond() {\r
+    return Second;\r
+  }\r
+};\r
+\r
+class PairInfo : Pair {\r
+  int Sum;\r
+\r
+  int getSum() {\r
+    return this.First + Second;\r
+  }\r
+};\r
+\r
+[numthreads(1, 1, 1)]\r
+void main() {\r
+  Pair Vals = {1, 2};\r
+  Vals.First = Vals.getFirst();\r
+  Vals.Second = Vals.getSecond();\r
+\r
+  PairInfo ValsInfo;\r
+  ValsInfo.First = Vals.First;\r
+  ValsInfo.Second = Vals.Second;\r
+  ValsInfo.Sum = ValsInfo.getSum();\r
+\r
+}\r
+\r
+// CHECK:     -CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:7:3, line:9:3> line:7:7 used getFirst 'int ()' implicit-inline\r
+// CHECK-NEXT:`-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:18, line:9:3>\r
+// CHECK-NEXT:`-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:8:4, col:16>\r
+// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> 'int' <LValueToRValue>\r
+// CHECK-NEXT:`-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:11, col:16> 'int' lvalue .First 0x{{[0-9A-Fa-f]+}}\r
+// CHECK-NEXT:`-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:11> 'Pair' lvalue this\r
+// CHECK-NEXT:-CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:11:3, line:13:3> line:11:7 used getSecond 'int ()' implicit-inline\r
+// CHECK-NEXT:`-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:19, line:13:3>\r
+// CHECK-NEXT:`-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:12:5, col:12>\r
+// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'int' <LValueToRValue>\r
+// CHECK-NEXT:`-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'int' lvalue .Second 0x{{[0-9A-Fa-f]+}}\r
+// CHECK-NEXT:`-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'Pair' lvalue implicit this\r
+\r
+\r
+// CHECK:     CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <line:19:3, line:21:3> line:19:7 used getSum 'int ()' implicit-inline\r
+// CHECK-NEXT:`-CompoundStmt 0x{{[0-9A-Fa-f]+}} <col:16, line:21:3>\r
+// CHECK-NEXT:`-ReturnStmt 0x{{[0-9A-Fa-f]+}} <line:20:5, col:25>\r
+// CHECK-NEXT:`-BinaryOperator 0x{{[0-9A-Fa-f]+}} <col:12, col:25> 'int' '+'\r
+// CHECK-NEXT:-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:12, col:17> 'int' <LValueToRValue>\r
+// CHECK-NEXT:`-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:12, col:17> 'int' lvalue .First 0x{{[0-9A-Fa-f]+}}\r
+// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'Pair' lvalue <UncheckedDerivedToBase (Pair)>\r
+// CHECK-NEXT:`-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:12> 'PairInfo' lvalue this\r
+// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:25> 'int' <LValueToRValue>\r
+// CHECK-NEXT:`-MemberExpr 0x{{[0-9A-Fa-f]+}} <col:25> 'int' lvalue .Second 0x{{[0-9A-Fa-f]+}}\r
+// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9A-Fa-f]+}} <col:25> 'Pair' lvalue <UncheckedDerivedToBase (Pair)>\r
+// CHECK-NEXT:`-CXXThisExpr 0x{{[0-9A-Fa-f]+}} <col:25> 'PairInfo' lvalue implicit this\r
diff --git a/clang/test/CodeGenHLSL/this-assignment-overload.hlsl b/clang/test/CodeGenHLSL/this-assignment-overload.hlsl
new file mode 100644 (file)
index 0000000..92504df
--- /dev/null
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -disable-llvm-passes -o - -std=hlsl202x %s | FileCheck %s\r
+\r
+struct Pair {\r
+  int First;\r
+  int Second;\r
+  int getFirst() {\r
+    Pair Another = {5, 10};\r
+    this = Another;\r
+      return this.First;\r
+  }\r
+  int getSecond() {\r
+    this = Pair();\r
+    return Second;\r
+  }\r
+  void operator=(Pair P) {\r
+    First = P.First;\r
+    Second = 2;\r
+  }\r
+};\r
+[numthreads(1, 1, 1)]\r
+void main() {\r
+  Pair Vals = {1, 2};\r
+  Vals.First = Vals.getFirst();\r
+  Vals.Second = Vals.getSecond();\r
+}\r
+\r
+// This test makes a probably safe assumption that HLSL 202x includes operator overloading for assignment operators.\r
+// CHECK:     define linkonce_odr noundef i32 @"?getFirst@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #2 align 2 {\r
+// CHECK-NEXT:entry:\r
+// CHECK-NEXT:%this.addr = alloca ptr, align 4\r
+// CHECK-NEXT:%Another = alloca %struct.Pair, align 4\r
+// CHECK-NEXT:%agg.tmp = alloca %struct.Pair, align 4\r
+// CHECK-NEXT:store ptr %this, ptr %this.addr, align 4\r
+// CHECK-NEXT:%this1 = load ptr, ptr %this.addr, align 4\r
+// CHECK-NEXT:%First = getelementptr inbounds %struct.Pair, ptr %Another, i32 0, i32 0\r
+// CHECK-NEXT:store i32 5, ptr %First, align 4\r
+// CHECK-NEXT:%Second = getelementptr inbounds %struct.Pair, ptr %Another, i32 0, i32 1\r
+// CHECK-NEXT:store i32 10, ptr %Second, align 4\r
+// CHECK-NEXT:call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr align 4 %Another, i32 8, i1 false)\r
+// CHECK-NEXT:call void @"??4Pair@@QAAXU0@@Z"(ptr noundef nonnull align 4 dereferenceable(8) %this1, ptr noundef byval(%struct.Pair) align 4 %agg.tmp)\r
+// CHECK-NEXT:%First2 = getelementptr inbounds %struct.Pair, ptr %this1, i32 0, i32 0\r
+// CHECK-NEXT:%0 = load i32, ptr %First2, align 4\r
+// CHECK-NEXT:ret i32 %0\r
+\r
+// CHECK:     define linkonce_odr noundef i32 @"?getSecond@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #2 align 2 {\r
+// CHECK-NEXT:entry:\r
+// CHECK-NEXT:%this.addr = alloca ptr, align 4\r
+// CHECK-NEXT:%agg.tmp = alloca %struct.Pair, align 4\r
+// CHECK-NEXT:store ptr %this, ptr %this.addr, align 4\r
+// CHECK-NEXT:%this1 = load ptr, ptr %this.addr, align 4\r
+// CHECK-NEXT:call void @llvm.memset.p0.i32(ptr align 4 %agg.tmp, i8 0, i32 8, i1 false)\r
+// CHECK-NEXT:call void @"??4Pair@@QAAXU0@@Z"(ptr noundef nonnull align 4 dereferenceable(8) %this1, ptr noundef byval(%struct.Pair) align 4 %agg.tmp)\r
+// CHECK-NEXT:%Second = getelementptr inbounds %struct.Pair, ptr %this1, i32 0, i32 1\r
+// CHECK-NEXT:%0 = load i32, ptr %Second, align 4\r
+// CHECK-NEXT:ret i32 %0\r
diff --git a/clang/test/CodeGenHLSL/this-assignment.hlsl b/clang/test/CodeGenHLSL/this-assignment.hlsl
new file mode 100644 (file)
index 0000000..bb67fb6
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s | FileCheck %s\r
+\r
+struct Pair {\r
+  int First;\r
+  int Second;\r
+\r
+  int getFirst() {\r
+    Pair Another = {5, 10};\r
+    this = Another;\r
+         return this.First;\r
+  }\r
+\r
+  int getSecond() {\r
+    this = Pair();\r
+    return Second;\r
+  }\r
+};\r
+\r
+[numthreads(1, 1, 1)]\r
+void main() {\r
+  Pair Vals = {1, 2.0};\r
+  Vals.First = Vals.getFirst();\r
+  Vals.Second = Vals.getSecond();\r
+}\r
+\r
+// This tests reference like implicit this in HLSL\r
+// CHECK:     define linkonce_odr noundef i32 @"?getFirst@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #3 align 2 {\r
+// CHECK-NEXT:entry:\r
+// CHECK-NEXT:%this.addr = alloca ptr, align 4\r
+// CHECK-NEXT:%Another = alloca %struct.Pair, align 4\r
+// CHECK-NEXT:store ptr %this, ptr %this.addr, align 4\r
+// CHECK-NEXT:%this1 = load ptr, ptr %this.addr, align 4\r
+// CHECK-NEXT:call void @llvm.memcpy.p0.p0.i32(ptr align 4 %Another, ptr align 4 @"__const.?getFirst@Pair@@QAAHXZ.Another", i32 8, i1 false)\r
+// CHECK-NEXT:call void @llvm.memcpy.p0.p0.i32(ptr align 4 %this1, ptr align 4 %Another, i32 8, i1 false)\r
+// CHECK-NEXT:%First = getelementptr inbounds %struct.Pair, ptr %this1, i32 0, i32 0\r
+\r
+// CHECK:     define linkonce_odr noundef i32 @"?getSecond@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %this) #3 align 2 {\r
+// CHECK-NEXT:entry:\r
+// CHECK-NEXT:%this.addr = alloca ptr, align 4\r
+// CHECK-NEXT:%ref.tmp = alloca %struct.Pair, align 4\r
+// CHECK-NEXT:store ptr %this, ptr %this.addr, align 4\r
+// CHECK-NEXT:%this1 = load ptr, ptr %this.addr, align 4\r
+// CHECK-NEXT:call void @llvm.memset.p0.i32(ptr align 4 %ref.tmp, i8 0, i32 8, i1 false)\r
+// CHECK-NEXT:call void @llvm.memcpy.p0.p0.i32(ptr align 4 %this1, ptr align 4 %ref.tmp, i32 8, i1 false)\r
+// CHECK-NEXT:%Second = getelementptr inbounds %struct.Pair, ptr %this1, i32 0, i32 1\r
diff --git a/clang/test/CodeGenHLSL/this-reference.hlsl b/clang/test/CodeGenHLSL/this-reference.hlsl
new file mode 100644 (file)
index 0000000..22bab1d
--- /dev/null
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s | FileCheck %s\r
+\r
+struct Pair {\r
+  int First;\r
+  float Second;\r
+\r
+  int getFirst() {\r
+         return this.First;\r
+  }\r
+\r
+  float getSecond() {\r
+    return Second;\r
+  }\r
+};\r
+\r
+[numthreads(1, 1, 1)]\r
+void main() {\r
+  Pair Vals = {1, 2.0};\r
+  Vals.First = Vals.getFirst();\r
+  Vals.Second = Vals.getSecond();\r
+}\r
+\r
+// This tests reference like `this` in HLSL\r
+  // CHECK:       %call = call noundef i32 @"?getFirst@Pair@@QAAHXZ"(ptr noundef nonnull align 4 dereferenceable(8) %Vals)\r
+  // CHECK-NEXT:  %First = getelementptr inbounds %struct.Pair, ptr %Vals, i32 0, i32 0\r
+  // CHECK-NEXT:  store i32 %call, ptr %First, align 4\r
+  // CHECK-NEXT:  %call1 = call noundef float @"?getSecond@Pair@@QAAMXZ"(ptr noundef nonnull align 4 dereferenceable(8) %Vals)\r
+  // CHECK-NEXT:  %Second = getelementptr inbounds %struct.Pair, ptr %Vals, i32 0, i32 1\r