[clang] a51597f - [HLSL] Handle logical pointer for array assign (#193227)

via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 04:51:34 PDT 2026


Author: Nathan Gauër
Date: 2026-04-27T13:51:29+02:00
New Revision: a51597f35f91ed5c25a391d5e965bfb6fe9a5ae2

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

LOG: [HLSL] Handle logical pointer for array assign (#193227)

This commits adds SPIR-V testing on an existing test (almost-NFC on DXIL
testing). It also copies it and invokes Clang using the experimental
logical pointer flag.
Adding this flag shows a missing case in the frontend, handled with this
commit.

Due to the difference in index handling between the structured.gep and
legacy one, the Cbuffer load codegen had to be rewritten. It's a bit
more naive, as we get one gep per level, but this will be handled by
optimizations later on.

Added: 
    clang/test/CodeGenHLSL/ArrayAssignable.logicalptr.hlsl
    clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.hlsl
    clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.logical.hlsl

Modified: 
    clang/lib/CodeGen/CGBuilder.h
    clang/lib/CodeGen/CGExprAgg.cpp
    clang/lib/CodeGen/CGHLSLRuntime.cpp
    clang/test/CodeGenHLSL/ArrayAssignable.hlsl

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CGBuilder.h b/clang/lib/CodeGen/CGBuilder.h
index 6d2a01ae33bd7..4a51f6be8578f 100644
--- a/clang/lib/CodeGen/CGBuilder.h
+++ b/clang/lib/CodeGen/CGBuilder.h
@@ -362,6 +362,28 @@ class CGBuilderTy : public CGBuilderBaseTy {
                       ElementType, Align, Addr.isKnownNonNull());
   }
 
+  using CGBuilderBaseTy::CreateStructuredGEP;
+  llvm::Value *CreateAccessChain(bool Logical, llvm::Type *BaseType,
+                                 llvm::Value *PtrBase,
+                                 ArrayRef<llvm::Value *> IdxList,
+                                 const Twine &Name = "") {
+
+    if (Logical)
+      return CreateStructuredGEP(BaseType, PtrBase, IdxList, Name);
+    return CreateInBoundsGEP(BaseType, PtrBase, IdxList, Name);
+  }
+
+  Address CreateAccessChain(bool Logical, Address Addr,
+                            ArrayRef<llvm::Value *> IdxList,
+                            llvm::Type *ElementType, CharUnits Align,
+                            const Twine &Name = "") {
+
+    return RawAddress(CreateAccessChain(Logical, Addr.getElementType(),
+                                        emitRawPointerFromAddress(Addr),
+                                        IdxList, Name),
+                      ElementType, Align, Addr.isKnownNonNull());
+  }
+
   using CGBuilderBaseTy::CreateIsNull;
   llvm::Value *CreateIsNull(Address Addr, const Twine &Name = "") {
     if (!Addr.hasOffset())

diff  --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 686358877e8e0..a4282c4f51199 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -652,9 +652,15 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType,
   auto Emit = [&](Expr *Init, uint64_t ArrayIndex) {
     llvm::Value *element = begin;
     if (ArrayIndex > 0) {
-      element = Builder.CreateInBoundsGEP(
-          llvmElementType, begin,
-          llvm::ConstantInt::get(CGF.SizeTy, ArrayIndex), "arrayinit.element");
+      if (CGF.getLangOpts().EmitLogicalPointer)
+        element = Builder.CreateStructuredGEP(
+            AType, begin, llvm::ConstantInt::get(CGF.SizeTy, ArrayIndex),
+            "arrayinit.element");
+      else
+        element = Builder.CreateInBoundsGEP(
+            llvmElementType, begin,
+            llvm::ConstantInt::get(CGF.SizeTy, ArrayIndex),
+            "arrayinit.element");
 
       // Tell the cleanup that it needs to destroy up to this
       // element.  TODO: some of these stores can be trivially

diff  --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 19c0f6d866c47..f2db4114c0798 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1605,64 +1605,34 @@ namespace {
 /// copying out of a cbuffer).
 class HLSLBufferCopyEmitter {
   CodeGenFunction &CGF;
-  Address DestPtr;
+  Address DstPtr;
   Address SrcPtr;
   llvm::Type *LayoutTy = nullptr;
 
   SmallVector<llvm::Value *> CurStoreIndices;
   SmallVector<llvm::Value *> CurLoadIndices;
 
-  void emitCopyAtIndices(llvm::Type *FieldTy, llvm::ConstantInt *StoreIndex,
-                         llvm::ConstantInt *LoadIndex) {
-    CurStoreIndices.push_back(StoreIndex);
-    CurLoadIndices.push_back(LoadIndex);
-    llvm::scope_exit RestoreIndices([&]() {
-      CurStoreIndices.pop_back();
-      CurLoadIndices.pop_back();
-    });
-
-    // First, see if this is some kind of aggregate and recurse.
-    if (processArray(FieldTy))
-      return;
-    if (processBufferLayoutArray(FieldTy))
-      return;
-    if (processStruct(FieldTy))
-      return;
-
-    // When we have a scalar or vector element we can emit the copy.
-    CharUnits Align = CharUnits::fromQuantity(
-        CGF.CGM.getDataLayout().getABITypeAlign(FieldTy));
-    Address SrcGEP = RawAddress(
-        CGF.Builder.CreateInBoundsGEP(LayoutTy, SrcPtr.getBasePointer(),
-                                      CurLoadIndices, "cbuf.src"),
-        FieldTy, Align, SrcPtr.isKnownNonNull());
-    Address DestGEP = CGF.Builder.CreateInBoundsGEP(
-        DestPtr, CurStoreIndices, FieldTy, Align, "cbuf.dest");
-    llvm::Value *Load = CGF.Builder.CreateLoad(SrcGEP, "cbuf.load");
-    CGF.Builder.CreateStore(Load, DestGEP);
+  // Creates & returns either a structured.gep or a ptradd/gep depending on
+  // langopts.
+  llvm::Value *emitAccessChain(llvm::Type *BaseTy, llvm::Value *Base,
+                               ArrayRef<llvm::Value *> Indices) {
+    bool EmitLogical = CGF.getLangOpts().EmitLogicalPointer;
+    if (EmitLogical)
+      return CGF.Builder.CreateAccessChain(EmitLogical, BaseTy, Base, Indices);
+
+    llvm::SmallVector<llvm::Value *> GEPIndices;
+    GEPIndices.reserve(Indices.size() + 1);
+    GEPIndices.push_back(llvm::ConstantInt::get(CGF.IntTy, 0));
+    GEPIndices.append(Indices.begin(), Indices.end());
+    return CGF.Builder.CreateAccessChain(EmitLogical, BaseTy, Base, GEPIndices);
   }
 
-  bool processArray(llvm::Type *FieldTy) {
-    auto *AT = dyn_cast<llvm::ArrayType>(FieldTy);
-    if (!AT)
-      return false;
-
-    // If we have an llvm::ArrayType this is just a regular array with no top
-    // level padding, so all we need to do is copy each member.
-    for (unsigned I = 0, E = AT->getNumElements(); I < E; ++I)
-      emitCopyAtIndices(AT->getElementType(),
-                        llvm::ConstantInt::get(CGF.SizeTy, I),
-                        llvm::ConstantInt::get(CGF.SizeTy, I));
-    return true;
-  }
-
-  bool processBufferLayoutArray(llvm::Type *FieldTy) {
+  bool isBufferLayoutArray(llvm::StructType *ST) {
     // A buffer layout array is a struct with two elements: the padded array,
     // and the last element. That is, is should look something like this:
     //
     //   { [%n x { %type, %padding }], %type }
     //
-    auto *ST = dyn_cast<llvm::StructType>(FieldTy);
     if (!ST || ST->getNumElements() != 2)
       return false;
 
@@ -1681,51 +1651,104 @@ class HLSLBufferCopyEmitter {
     llvm::Type *ElementTy = ST->getElementType(1);
     if (PaddedTy->getElementType(0) != ElementTy)
       return false;
+    return true;
+  }
 
-    // All but the last of the logical array elements are in the padded array.
-    unsigned NumElts = PaddedEltsTy->getNumElements() + 1;
-
-    // Add an extra indirection to the load for the struct and walk the
-    // array prefix.
-    CurLoadIndices.push_back(llvm::ConstantInt::get(CGF.Int32Ty, 0));
-    for (unsigned I = 0; I < NumElts - 1; ++I) {
-      // We need to copy the element itself, without the padding.
-      CurLoadIndices.push_back(llvm::ConstantInt::get(CGF.SizeTy, I));
-      emitCopyAtIndices(ElementTy, llvm::ConstantInt::get(CGF.SizeTy, I),
-                        llvm::ConstantInt::get(CGF.Int32Ty, 0));
-      CurLoadIndices.pop_back();
+  void emitBufferLayoutCopy(Value *Src, llvm::StructType *SrcTy, Value *Dst,
+                            llvm::ArrayType *DstTy) {
+    // Those assumptions are checked by isBufferLayoutArray.
+    auto *SrcPaddedArrayTy = cast<llvm::ArrayType>(SrcTy->getElementType(0));
+    auto *SrcPaddedEltTy =
+        cast<llvm::StructType>(SrcPaddedArrayTy->getElementType());
+    assert(SrcPaddedArrayTy->getNumElements() + 1 == DstTy->getNumElements());
+    assert(SrcPaddedEltTy->getElementType(0) == SrcTy->getElementType(1));
+
+    auto *SrcDataTy = SrcTy->getElementType(1);
+    auto Zero = llvm::ConstantInt::get(CGF.IntTy, 0);
+
+    for (unsigned I = 0; I < SrcPaddedArrayTy->getNumElements(); ++I) {
+      auto Index = llvm::ConstantInt::get(CGF.IntTy, I);
+      auto *SrcElt = emitAccessChain(SrcTy, Src, {Zero, Index, Zero});
+      auto *DstElt = emitAccessChain(DstTy, Dst, {Index});
+      emitElementCopy(SrcElt, SrcDataTy, DstElt, DstTy->getElementType());
     }
-    CurLoadIndices.pop_back();
-
-    // Now copy the last element.
-    emitCopyAtIndices(ElementTy,
-                      llvm::ConstantInt::get(CGF.SizeTy, NumElts - 1),
-                      llvm::ConstantInt::get(CGF.Int32Ty, 1));
 
-    return true;
+    auto *SrcElt =
+        emitAccessChain(SrcTy, Src, {llvm::ConstantInt::get(CGF.IntTy, 1)});
+    auto *DstElt = emitAccessChain(
+        DstTy, Dst,
+        {llvm::ConstantInt::get(CGF.IntTy, DstTy->getNumElements() - 1)});
+    emitElementCopy(SrcElt, SrcDataTy, DstElt, DstTy->getElementType());
   }
 
-  bool processStruct(llvm::Type *FieldTy) {
-    auto *ST = dyn_cast<llvm::StructType>(FieldTy);
-    if (!ST)
-      return false;
+  void emitCopy(Value *Src, llvm::StructType *SrcTy, Value *Dst,
+                llvm::Type *DstTy) {
+    if (isBufferLayoutArray(SrcTy))
+      return emitBufferLayoutCopy(Src, SrcTy, Dst,
+                                  cast<llvm::ArrayType>(DstTy));
+
+    unsigned SrcIndex = 0;
+    unsigned DstIndex = 0;
+
+    auto *DstST = cast<llvm::StructType>(DstTy);
+    while (SrcIndex < SrcTy->getNumElements() &&
+           DstIndex < DstST->getNumElements()) {
+      if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(
+              SrcTy->getElementType(SrcIndex))) {
+        SrcIndex += 1;
+        continue;
+      }
 
-    // Copy the struct field by field, but skip any explicit padding.
-    unsigned Skipped = 0;
-    for (unsigned I = 0, E = ST->getNumElements(); I < E; ++I) {
-      llvm::Type *ElementTy = ST->getElementType(I);
-      if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(ElementTy))
-        ++Skipped;
-      else
-        emitCopyAtIndices(ElementTy, llvm::ConstantInt::get(CGF.Int32Ty, I),
-                          llvm::ConstantInt::get(CGF.Int32Ty, I + Skipped));
+      if (CGF.CGM.getTargetCodeGenInfo().isHLSLPadding(
+              DstST->getElementType(DstIndex))) {
+        DstIndex += 1;
+        continue;
+      }
+
+      auto *SrcElt = emitAccessChain(
+          SrcTy, Src, {llvm::ConstantInt::get(CGF.IntTy, SrcIndex)});
+      auto *DstElt = emitAccessChain(
+          DstTy, Dst, {llvm::ConstantInt::get(CGF.IntTy, DstIndex)});
+      emitElementCopy(SrcElt, SrcTy->getElementType(SrcIndex), DstElt,
+                      DstST->getElementType(DstIndex));
+      DstIndex += 1;
+      SrcIndex += 1;
     }
-    return true;
+  }
+
+  void emitCopy(Value *Src, llvm::ArrayType *SrcTy, Value *Dst,
+                llvm::Type *DstTy) {
+    for (unsigned I = 0, E = SrcTy->getNumElements(); I < E; ++I) {
+      auto *SrcElt =
+          emitAccessChain(SrcTy, Src, {llvm::ConstantInt::get(CGF.IntTy, I)});
+      auto *DstElt =
+          emitAccessChain(DstTy, Dst, {llvm::ConstantInt::get(CGF.IntTy, I)});
+      emitElementCopy(SrcElt, SrcTy->getElementType(), DstElt,
+                      cast<llvm::ArrayType>(DstTy)->getElementType());
+    }
+  }
+
+  void emitElementCopy(Value *Src, llvm::Type *SrcTy, Value *Dst,
+                       llvm::Type *DstTy) {
+    if (auto *AT = dyn_cast<llvm::ArrayType>(SrcTy))
+      return emitCopy(Src, AT, Dst, DstTy);
+    if (auto *ST = dyn_cast<llvm::StructType>(SrcTy))
+      return emitCopy(Src, ST, Dst, DstTy);
+
+    // When we have a scalar or vector element we can emit the copy.
+    CharUnits SrcAlign =
+        CharUnits::fromQuantity(CGF.CGM.getDataLayout().getABITypeAlign(SrcTy));
+    CharUnits DstAlign =
+        CharUnits::fromQuantity(CGF.CGM.getDataLayout().getABITypeAlign(DstTy));
+    Address SrcAddr(Src, SrcTy, SrcAlign);
+    Address DstAddr(Dst, DstTy, DstAlign);
+    llvm::Value *Load = CGF.Builder.CreateLoad(SrcAddr, "cbuf.load");
+    CGF.Builder.CreateStore(Load, DstAddr);
   }
 
 public:
-  HLSLBufferCopyEmitter(CodeGenFunction &CGF, Address DestPtr, Address SrcPtr)
-      : CGF(CGF), DestPtr(DestPtr), SrcPtr(SrcPtr) {}
+  HLSLBufferCopyEmitter(CodeGenFunction &CGF, Address DstPtr, Address SrcPtr)
+      : CGF(CGF), DstPtr(DstPtr), SrcPtr(SrcPtr) {}
 
   bool emitCopy(QualType CType) {
     LayoutTy = HLSLBufferLayoutBuilder(CGF.CGM).layOutType(CType);
@@ -1735,16 +1758,16 @@ class HLSLBufferCopyEmitter {
     // currently.
     //
     // See https://github.com/llvm/wg-hlsl/issues/351
-    emitCopyAtIndices(LayoutTy, llvm::ConstantInt::get(CGF.SizeTy, 0),
-                      llvm::ConstantInt::get(CGF.SizeTy, 0));
+    emitElementCopy(SrcPtr.getBasePointer(), LayoutTy, DstPtr.getBasePointer(),
+                    DstPtr.getElementType());
     return true;
   }
 };
 } // namespace
 
-bool CGHLSLRuntime::emitBufferCopy(CodeGenFunction &CGF, Address DestPtr,
+bool CGHLSLRuntime::emitBufferCopy(CodeGenFunction &CGF, Address DstPtr,
                                    Address SrcPtr, QualType CType) {
-  return HLSLBufferCopyEmitter(CGF, DestPtr, SrcPtr).emitCopy(CType);
+  return HLSLBufferCopyEmitter(CGF, DstPtr, SrcPtr).emitCopy(CType);
 }
 
 LValue CGHLSLRuntime::emitBufferMemberExpr(CodeGenFunction &CGF,

diff  --git a/clang/test/CodeGenHLSL/ArrayAssignable.hlsl b/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
index 9a568fe3371d0..36d44b26d81bb 100644
--- a/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
+++ b/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
@@ -172,18 +172,20 @@ void arr_assign9() {
 
 // CHECK-LABEL: define hidden void {{.*}}arr_assign10
 // CHECK: [[C:%.*]] = alloca [2 x [2 x i32]], align 4
-// CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 0, i32 0
+// CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 0
+// CHECK-NEXT: [[V1:%.*]] = getelementptr inbounds [2 x i32], ptr [[V0]], i32 0, i32 0
 // CHECK-NEXT: [[L0:%.*]] = load i32, ptr addrspace(2) @c3, align 4
-// CHECK-NEXT: store i32 [[L0]], ptr [[V0]], align 4
-// CHECK-NEXT: [[V1:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 0, i32 1
+// CHECK-NEXT: store i32 [[L0]], ptr [[V1]], align 4
+// CHECK-NEXT: [[V1:%.*]] = getelementptr inbounds [2 x i32], ptr [[V0]], i32 0, i32 1
 // CHECK-NEXT: [[L1:%.*]] = load i32, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c3, i32 16), align 4
 // CHECK-NEXT: store i32 [[L1]], ptr [[V1]], align 4
-// CHECK-NEXT: [[V2:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 1, i32 0
+// CHECK-NEXT: [[V2:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 1
+// CHECK-NEXT: [[V3:%.*]] = getelementptr inbounds [2 x i32], ptr [[V2]], i32 0, i32 0
 // CHECK-NEXT: [[L2:%.*]] = load i32, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c3, i32 32), align 4
-// CHECK-NEXT: store i32 [[L2]], ptr [[V2]], align 4
-// CHECK-NEXT: [[V3:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 1, i32 1
+// CHECK-NEXT: store i32 [[L2]], ptr [[V3]], align 4
+// CHECK-NEXT: [[V4:%.*]] = getelementptr inbounds [2 x i32], ptr [[V2]], i32 0, i32 1
 // CHECK-NEXT: [[L3:%.*]] = load i32, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c3, i32 48), align 4
-// CHECK-NEXT: store i32 [[L3]], ptr [[V3]], align 4
+// CHECK-NEXT: store i32 [[L3]], ptr [[V4]], align 4
 // CHECK-NEXT: ret void
 void arr_assign10() {
   int C[2][2];
@@ -192,18 +194,20 @@ void arr_assign10() {
 
 // CHECK-LABEL: define hidden void {{.*}}arr_assign11
 // CHECK: [[C:%.*]] = alloca [2 x %struct.S], align 1
-// CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds [2 x %struct.S], ptr [[C]], i32 0, i32 0, i32 0
+// CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds [2 x %struct.S], ptr [[C]], i32 0, i32 0
+// CHECK-NEXT: [[V1:%.*]] = getelementptr inbounds %struct.S, ptr [[V0]], i32 0, i32 0
 // CHECK-NEXT: [[L0:%.*]] = load i32, ptr addrspace(2) @c4, align 4
-// CHECK-NEXT: store i32 [[L0]], ptr [[V0]], align 4
-// CHECK-NEXT: [[V1:%.*]] = getelementptr inbounds [2 x %struct.S], ptr [[C]], i32 0, i32 0, i32 1
+// CHECK-NEXT: store i32 [[L0]], ptr [[V1]], align 4
+// CHECK-NEXT: [[V2:%.*]] = getelementptr inbounds %struct.S, ptr [[V0]], i32 0, i32 1
 // CHECK-NEXT: [[L1:%.*]] = load float, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c4, i32 4), align 4
-// CHECK-NEXT: store float [[L1]], ptr [[V1]], align 4
-// CHECK-NEXT: [[V2:%.*]] = getelementptr inbounds [2 x %struct.S], ptr [[C]], i32 0, i32 1, i32 0
+// CHECK-NEXT: store float [[L1]], ptr [[V2]], align 4
+// CHECK-NEXT: [[V3:%.*]] = getelementptr inbounds [2 x %struct.S], ptr [[C]], i32 0, i32 1
+// CHECK-NEXT: [[V4:%.*]] = getelementptr inbounds %struct.S, ptr [[V3]], i32 0, i32 0
 // CHECK-NEXT: [[L2:%.*]] = load i32, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c4, i32 16), align 4
-// CHECK-NEXT: store i32 [[L2]], ptr [[V2]], align 4
-// CHECK-NEXT: [[V3:%.*]] = getelementptr inbounds [2 x %struct.S], ptr [[C]], i32 0, i32 1, i32 1
+// CHECK-NEXT: store i32 [[L2]], ptr [[V4]], align 4
+// CHECK-NEXT: [[V5:%.*]] = getelementptr inbounds %struct.S, ptr [[V3]], i32 0, i32 1
 // CHECK-NEXT: [[L3:%.*]] = load float, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c4, i32 20), align 4
-// CHECK-NEXT: store float [[L3]], ptr [[V3]], align 4
+// CHECK-NEXT: store float [[L3]], ptr [[V5]], align 4
 // CHECK-NEXT: ret void
 void arr_assign11() {
   S C[2];

diff  --git a/clang/test/CodeGenHLSL/ArrayAssignable.logicalptr.hlsl b/clang/test/CodeGenHLSL/ArrayAssignable.logicalptr.hlsl
new file mode 100644
index 0000000000000..9816c79be2a23
--- /dev/null
+++ b/clang/test/CodeGenHLSL/ArrayAssignable.logicalptr.hlsl
@@ -0,0 +1,443 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s -fexperimental-logical-pointer | FileCheck %s --check-prefixes=CHECK-DXIL
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s -fexperimental-logical-pointer | FileCheck %s --check-prefixes=CHECK-SPIR
+
+struct S {
+  int x;
+  float f;
+};
+
+cbuffer CBArrays : register(b0) {
+  float c1[2];
+  int4 c2[2];
+  int c3[2][2];
+  S c4[2];
+}
+
+// CHECK-DXIL: [[CBLayout:%.*]] = type <{ <{ [1 x <{ float, target("dx.Padding", 12) }>], float }>, target("dx.Padding", 12), [2 x <4 x i32>], <{ [1 x <{ <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>, target("dx.Padding", 12) }>], <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }> }>, target("dx.Padding", 12), <{ [1 x <{ %S, target("dx.Padding", 8) }>], %S }> }>
+// CHECK-DXIL: @CBArrays.cb = global target("dx.CBuffer", [[CBLayout]])
+// CHECK-DXIL: @c1 = external hidden addrspace(2) global <{ [1 x <{ float, target("dx.Padding", 12) }>], float }>, align 4
+// CHECK-DXIL: @c2 = external hidden addrspace(2) global [2 x <4 x i32>], align 4
+// CHECK-DXIL: @c3 = external hidden addrspace(2) global <{ [1 x <{ <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>, target("dx.Padding", 12) }>], <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }> }>, align 4
+// CHECK-DXIL: @c4 = external hidden addrspace(2) global <{ [1 x <{ %S, target("dx.Padding", 8) }>], %S }>, align 1
+
+// CHECK-SPIR: [[CBLayout:%.*]] = type <{ <{ [1 x <{ float, target("spirv.Padding", 12) }>], float }>, target("spirv.Padding", 12), [2 x <4 x i32>], <{ [1 x <{ <{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }>, target("spirv.Padding", 12) }>], <{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }> }>, target("spirv.Padding", 12), <{ [1 x <{ %S, target("spirv.Padding", 8) }>], %S }> }>
+// CHECK-SPIR: @CBArrays.cb = global target("spirv.VulkanBuffer", %__cblayout_CBArrays, 2, 0) poison
+// CHECK-SPIR: @c1 = external hidden addrspace(12) global <{ [1 x <{ float, target("spirv.Padding", 12) }>], float }>, align 4
+// CHECK-SPIR: @c2 = external hidden addrspace(12) global [2 x <4 x i32>], align 4
+// CHECK-SPIR: @c3 = external hidden addrspace(12) global <{ [1 x <{ <{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }>, target("spirv.Padding", 12) }>], <{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }> }>, align 4
+// CHECK-SPIR: @c4 = external hidden addrspace(12) global <{ [1 x <{ %S, target("spirv.Padding", 8) }>], %S }>, align 1
+
+
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign1v(
+// CHECK-DXIL-SAME: ) #[[ATTR2:[0-9]+]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign1v.Arr, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memset.p0.i32(ptr align 4 [[ARR2]], i8 0, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i32 8, i1 false)
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign1v(
+// CHECK-SPIR-SAME: ) #[[ATTR2:[0-9]+]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[ARR:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign1v.Arr, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[ARR2]], i8 0, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i64 8, i1 false)
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign1() {
+  int Arr[2] = {0, 1};
+  int Arr2[2] = {0, 0};
+  Arr = Arr2;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign2v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign2v.Arr, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memset.p0.i32(ptr align 4 [[ARR2]], i8 0, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR3:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR3]], ptr align 4 @__const._Z11arr_assign2v.Arr3, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR2]], ptr align 4 [[ARR3]], i32 8, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i32 8, i1 false)
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign2v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[ARR:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign2v.Arr, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[ARR2]], i8 0, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR3:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR3]], ptr align 4 @__const._Z11arr_assign2v.Arr3, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR2]], ptr align 4 [[ARR3]], i64 8, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i64 8, i1 false)
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign2() {
+  int Arr[2] = {0, 1};
+  int Arr2[2] = {0, 0};
+  int Arr3[2] = {3, 4};
+  Arr = Arr2 = Arr3;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign3v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR2]], ptr align 4 @__const._Z11arr_assign3v.Arr2, i32 16, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR3:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR3]], ptr align 4 @__const._Z11arr_assign3v.Arr3, i32 16, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR2]], ptr align 4 [[ARR3]], i32 16, i1 false)
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign3v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR2]], ptr align 4 @__const._Z11arr_assign3v.Arr2, i64 16, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR3:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR3]], ptr align 4 @__const._Z11arr_assign3v.Arr3, i64 16, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR2]], ptr align 4 [[ARR3]], i64 16, i1 false)
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign3() {
+  int Arr2[2][2] = {{0, 0}, {1, 1}};
+  int Arr3[2][2] = {{1, 1}, {0, 0}};
+  Arr2 = Arr3;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign4v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign4v.Arr, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memset.p0.i32(ptr align 4 [[ARR2]], i8 0, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i32 8, i1 false)
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[ARR]], i32 0)
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[TMP0]], align 4
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign4v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[ARR:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign4v.Arr, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[ARR2]], i8 0, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i64 8, i1 false)
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[ARR]], i64 0)
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[TMP1]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign4() {
+  int Arr[2] = {0, 1};
+  int Arr2[2] = {0, 0};
+  (Arr = Arr2)[0] = 6;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign5v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign5v.Arr, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memset.p0.i32(ptr align 4 [[ARR2]], i8 0, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR3:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR3]], ptr align 4 @__const._Z11arr_assign5v.Arr3, i32 8, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR2]], ptr align 4 [[ARR3]], i32 8, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i32 8, i1 false)
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[ARR]], i32 0)
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[TMP0]], align 4
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign5v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[ARR:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign5v.Arr, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[ARR2]], i8 0, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR3:%.*]] = call elementtype([2 x i32]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR3]], ptr align 4 @__const._Z11arr_assign5v.Arr3, i64 8, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR2]], ptr align 4 [[ARR3]], i64 8, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i64 8, i1 false)
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[ARR]], i64 0)
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[TMP1]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign5() {
+  int Arr[2] = {0, 1};
+  int Arr2[2] = {0, 0};
+  int Arr3[2] = {3, 4};
+  (Arr = Arr2 = Arr3)[0] = 6;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign6v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign6v.Arr, i32 16, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR2]], ptr align 4 @__const._Z11arr_assign6v.Arr2, i32 16, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i32 16, i1 false)
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [2 x i32]]) [[ARR]], i32 0)
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP0]], i32 0)
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[TMP1]], align 4
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign6v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[ARR:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign6v.Arr, i64 16, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR2]], ptr align 4 @__const._Z11arr_assign6v.Arr2, i64 16, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i64 16, i1 false)
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [2 x i32]]) [[ARR]], i64 0)
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP1]], i64 0)
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[TMP2]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign6() {
+  int Arr[2][2] = {{0, 0}, {1, 1}};
+  int Arr2[2][2] = {{1, 1}, {0, 0}};
+  (Arr = Arr2)[0][0] = 6;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign7v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign7v.Arr, i32 16, i1 false)
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR2]], ptr align 4 @__const._Z11arr_assign7v.Arr2, i32 16, i1 false)
+// CHECK-DXIL-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i32 16, i1 false)
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [2 x i32]]) [[ARR]], i32 0)
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[TMP0]], align 4
+// CHECK-DXIL-NEXT:    [[ARRAYINIT_ELEMENT:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP0]], i32 1)
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[ARRAYINIT_ELEMENT]], align 4
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign7v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[ARR:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 @__const._Z11arr_assign7v.Arr, i64 16, i1 false)
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR2]], ptr align 4 @__const._Z11arr_assign7v.Arr2, i64 16, i1 false)
+// CHECK-SPIR-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ARR]], ptr align 4 [[ARR2]], i64 16, i1 false)
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [2 x i32]]) [[ARR]], i64 0)
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[TMP1]], align 4
+// CHECK-SPIR-NEXT:    [[ARRAYINIT_ELEMENT:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP1]], i64 1)
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[ARRAYINIT_ELEMENT]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign7() {
+  int Arr[2][2] = {{0, 1}, {2, 3}};
+  int Arr2[2][2] = {{0, 0}, {1, 1}};
+  (Arr = Arr2)[0] = {6, 6};
+}
+
+// Verify you can assign from a cbuffer array
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign8v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[C:%.*]] = call elementtype([2 x float]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ float, target("dx.Padding", 12) }>], float }>) @c1, i32 0, i32 0, i32 0)
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x float]) [[C]], i32 0)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD:%.*]] = load float, ptr addrspace(2) [[TMP0]], align 4
+// CHECK-DXIL-NEXT:    store float [[CBUF_LOAD]], ptr [[TMP1]], align 4
+// CHECK-DXIL-NEXT:    [[TMP2:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ float, target("dx.Padding", 12) }>], float }>) @c1, i32 1)
+// CHECK-DXIL-NEXT:    [[TMP3:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x float]) [[C]], i32 1)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD1:%.*]] = load float, ptr addrspace(2) [[TMP2]], align 4
+// CHECK-DXIL-NEXT:    store float [[CBUF_LOAD1]], ptr [[TMP3]], align 4
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign8v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[C:%.*]] = call elementtype([2 x float]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ float, target("spirv.Padding", 12) }>], float }>) @c1, i32 0, i32 0, i32 0)
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x float]) [[C]], i32 0)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD:%.*]] = load float, ptr addrspace(12) [[TMP1]], align 4
+// CHECK-SPIR-NEXT:    store float [[CBUF_LOAD]], ptr [[TMP2]], align 4
+// CHECK-SPIR-NEXT:    [[TMP3:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ float, target("spirv.Padding", 12) }>], float }>) @c1, i32 1)
+// CHECK-SPIR-NEXT:    [[TMP4:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x float]) [[C]], i32 1)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD1:%.*]] = load float, ptr addrspace(12) [[TMP3]], align 4
+// CHECK-SPIR-NEXT:    store float [[CBUF_LOAD1]], ptr [[TMP4]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign8() {
+  float C[2];
+  C = c1;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign9v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[C:%.*]] = call elementtype([2 x <4 x i32>]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype([2 x <4 x i32>]) @c2, i32 0)
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x <4 x i32>]) [[C]], i32 0)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD:%.*]] = load <4 x i32>, ptr addrspace(2) [[TMP0]], align 4
+// CHECK-DXIL-NEXT:    store <4 x i32> [[CBUF_LOAD]], ptr [[TMP1]], align 4
+// CHECK-DXIL-NEXT:    [[TMP2:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype([2 x <4 x i32>]) @c2, i32 1)
+// CHECK-DXIL-NEXT:    [[TMP3:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x <4 x i32>]) [[C]], i32 1)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD1:%.*]] = load <4 x i32>, ptr addrspace(2) [[TMP2]], align 4
+// CHECK-DXIL-NEXT:    store <4 x i32> [[CBUF_LOAD1]], ptr [[TMP3]], align 4
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z11arr_assign9v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[C:%.*]] = call elementtype([2 x <4 x i32>]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype([2 x <4 x i32>]) @c2, i32 0)
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x <4 x i32>]) [[C]], i32 0)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD:%.*]] = load <4 x i32>, ptr addrspace(12) [[TMP1]], align 4
+// CHECK-SPIR-NEXT:    store <4 x i32> [[CBUF_LOAD]], ptr [[TMP2]], align 4
+// CHECK-SPIR-NEXT:    [[TMP3:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype([2 x <4 x i32>]) @c2, i32 1)
+// CHECK-SPIR-NEXT:    [[TMP4:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x <4 x i32>]) [[C]], i32 1)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD1:%.*]] = load <4 x i32>, ptr addrspace(12) [[TMP3]], align 4
+// CHECK-SPIR-NEXT:    store <4 x i32> [[CBUF_LOAD1]], ptr [[TMP4]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign9() {
+// TODO: We should be able to just memcpy here.
+// See https://github.com/llvm/wg-hlsl/issues/351
+
+  int4 C[2];
+  C = c2;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z12arr_assign10v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[C:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>, target("dx.Padding", 12) }>], <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }> }>) @c3, i32 0, i32 0, i32 0)
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [2 x i32]]) [[C]], i32 0)
+// CHECK-DXIL-NEXT:    [[TMP2:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>) [[TMP0]], i32 0, i32 0, i32 0)
+// CHECK-DXIL-NEXT:    [[TMP3:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP1]], i32 0)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD:%.*]] = load i32, ptr addrspace(2) [[TMP2]], align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD]], ptr [[TMP3]], align 4
+// CHECK-DXIL-NEXT:    [[TMP4:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>) [[TMP0]], i32 1)
+// CHECK-DXIL-NEXT:    [[TMP5:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP1]], i32 1)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD1:%.*]] = load i32, ptr addrspace(2) [[TMP4]], align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD1]], ptr [[TMP5]], align 4
+// CHECK-DXIL-NEXT:    [[TMP6:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>, target("dx.Padding", 12) }>], <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }> }>) @c3, i32 1)
+// CHECK-DXIL-NEXT:    [[TMP7:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [2 x i32]]) [[C]], i32 1)
+// CHECK-DXIL-NEXT:    [[TMP8:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>) [[TMP6]], i32 0, i32 0, i32 0)
+// CHECK-DXIL-NEXT:    [[TMP9:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP7]], i32 0)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace(2) [[TMP8]], align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD2]], ptr [[TMP9]], align 4
+// CHECK-DXIL-NEXT:    [[TMP10:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>) [[TMP6]], i32 1)
+// CHECK-DXIL-NEXT:    [[TMP11:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP7]], i32 1)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD3:%.*]] = load i32, ptr addrspace(2) [[TMP10]], align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD3]], ptr [[TMP11]], align 4
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z12arr_assign10v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[C:%.*]] = call elementtype([2 x [2 x i32]]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ <{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }>, target("spirv.Padding", 12) }>], <{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }> }>) @c3, i32 0, i32 0, i32 0)
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [2 x i32]]) [[C]], i32 0)
+// CHECK-SPIR-NEXT:    [[TMP3:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }>) [[TMP1]], i32 0, i32 0, i32 0)
+// CHECK-SPIR-NEXT:    [[TMP4:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP2]], i32 0)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD:%.*]] = load i32, ptr addrspace(12) [[TMP3]], align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD]], ptr [[TMP4]], align 4
+// CHECK-SPIR-NEXT:    [[TMP5:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }>) [[TMP1]], i32 1)
+// CHECK-SPIR-NEXT:    [[TMP6:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP2]], i32 1)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD1:%.*]] = load i32, ptr addrspace(12) [[TMP5]], align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD1]], ptr [[TMP6]], align 4
+// CHECK-SPIR-NEXT:    [[TMP7:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ <{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }>, target("spirv.Padding", 12) }>], <{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }> }>) @c3, i32 1)
+// CHECK-SPIR-NEXT:    [[TMP8:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [2 x i32]]) [[C]], i32 1)
+// CHECK-SPIR-NEXT:    [[TMP9:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }>) [[TMP7]], i32 0, i32 0, i32 0)
+// CHECK-SPIR-NEXT:    [[TMP10:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP8]], i32 0)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace(12) [[TMP9]], align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD2]], ptr [[TMP10]], align 4
+// CHECK-SPIR-NEXT:    [[TMP11:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ i32, target("spirv.Padding", 12) }>], i32 }>) [[TMP7]], i32 1)
+// CHECK-SPIR-NEXT:    [[TMP12:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x i32]) [[TMP8]], i32 1)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD3:%.*]] = load i32, ptr addrspace(12) [[TMP11]], align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD3]], ptr [[TMP12]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign10() {
+  int C[2][2];
+  C = c3;
+}
+
+// CHECK-DXIL-LABEL: define hidden void @_Z12arr_assign11v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[C:%.*]] = call elementtype([2 x [[STRUCT_S:%.*]]]) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ [[S:%.*]], target("dx.Padding", 8) }>], [[S]] }>) @c4, i32 0, i32 0, i32 0)
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [[STRUCT_S]]]) [[C]], i32 0)
+// CHECK-DXIL-NEXT:    [[TMP2:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype([[S]]) [[TMP0]], i32 0)
+// CHECK-DXIL-NEXT:    [[TMP3:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([[STRUCT_S]]) [[TMP1]], i32 0)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD:%.*]] = load i32, ptr addrspace(2) [[TMP2]], align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD]], ptr [[TMP3]], align 4
+// CHECK-DXIL-NEXT:    [[TMP4:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype([[S]]) [[TMP0]], i32 1)
+// CHECK-DXIL-NEXT:    [[TMP5:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([[STRUCT_S]]) [[TMP1]], i32 1)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD1:%.*]] = load float, ptr addrspace(2) [[TMP4]], align 4
+// CHECK-DXIL-NEXT:    store float [[CBUF_LOAD1]], ptr [[TMP5]], align 4
+// CHECK-DXIL-NEXT:    [[TMP6:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(<{ [1 x <{ [[S]], target("dx.Padding", 8) }>], [[S]] }>) @c4, i32 1)
+// CHECK-DXIL-NEXT:    [[TMP7:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [[STRUCT_S]]]) [[C]], i32 1)
+// CHECK-DXIL-NEXT:    [[TMP8:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype([[S]]) [[TMP6]], i32 0)
+// CHECK-DXIL-NEXT:    [[TMP9:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([[STRUCT_S]]) [[TMP7]], i32 0)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace(2) [[TMP8]], align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD2]], ptr [[TMP9]], align 4
+// CHECK-DXIL-NEXT:    [[TMP10:%.*]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype([[S]]) [[TMP6]], i32 1)
+// CHECK-DXIL-NEXT:    [[TMP11:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([[STRUCT_S]]) [[TMP7]], i32 1)
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD3:%.*]] = load float, ptr addrspace(2) [[TMP10]], align 4
+// CHECK-DXIL-NEXT:    store float [[CBUF_LOAD3]], ptr [[TMP11]], align 4
+// CHECK-DXIL-NEXT:    ret void
+//
+// CHECK-SPIR-LABEL: define hidden spir_func void @_Z12arr_assign11v(
+// CHECK-SPIR-SAME: ) #[[ATTR2]] {
+// CHECK-SPIR-NEXT:  [[ENTRY:.*:]]
+// CHECK-SPIR-NEXT:    [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry()
+// CHECK-SPIR-NEXT:    [[C:%.*]] = call elementtype([2 x [[STRUCT_S:%.*]]]) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ [[S:%.*]], target("spirv.Padding", 8) }>], [[S]] }>) @c4, i32 0, i32 0, i32 0)
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [[STRUCT_S]]]) [[C]], i32 0)
+// CHECK-SPIR-NEXT:    [[TMP3:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype([[S]]) [[TMP1]], i32 0)
+// CHECK-SPIR-NEXT:    [[TMP4:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([[STRUCT_S]]) [[TMP2]], i32 0)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD:%.*]] = load i32, ptr addrspace(12) [[TMP3]], align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD]], ptr [[TMP4]], align 4
+// CHECK-SPIR-NEXT:    [[TMP5:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype([[S]]) [[TMP1]], i32 1)
+// CHECK-SPIR-NEXT:    [[TMP6:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([[STRUCT_S]]) [[TMP2]], i32 1)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD1:%.*]] = load float, ptr addrspace(12) [[TMP5]], align 4
+// CHECK-SPIR-NEXT:    store float [[CBUF_LOAD1]], ptr [[TMP6]], align 4
+// CHECK-SPIR-NEXT:    [[TMP7:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(<{ [1 x <{ [[S]], target("spirv.Padding", 8) }>], [[S]] }>) @c4, i32 1)
+// CHECK-SPIR-NEXT:    [[TMP8:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([2 x [[STRUCT_S]]]) [[C]], i32 1)
+// CHECK-SPIR-NEXT:    [[TMP9:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype([[S]]) [[TMP7]], i32 0)
+// CHECK-SPIR-NEXT:    [[TMP10:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([[STRUCT_S]]) [[TMP8]], i32 0)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace(12) [[TMP9]], align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD2]], ptr [[TMP10]], align 4
+// CHECK-SPIR-NEXT:    [[TMP11:%.*]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype([[S]]) [[TMP7]], i32 1)
+// CHECK-SPIR-NEXT:    [[TMP12:%.*]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype([[STRUCT_S]]) [[TMP8]], i32 1)
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD3:%.*]] = load float, ptr addrspace(12) [[TMP11]], align 4
+// CHECK-SPIR-NEXT:    store float [[CBUF_LOAD3]], ptr [[TMP12]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
+void arr_assign11() {
+  S C[2];
+  C = c4;
+}

diff  --git a/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.hlsl b/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.hlsl
new file mode 100644
index 0000000000000..506de82412f8a
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.hlsl
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK-DXIL
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK-SPIR
+
+struct S {
+  float3 a;
+  float4 b;
+};
+// CHECK-DXIL-DAG: %S = type <{ <3 x float>, target("dx.Padding", 4), <4 x float> }>
+// CHECK-DXIL-DAG: %struct.S = type { <3 x float>, <4 x float> }
+// CHECK-SPIR-DAG: %S = type <{ <3 x float>, target("spirv.Padding", 4), <4 x float> }>
+// CHECK-SPIR-DAG: %struct.S = type { <3 x float>, <4 x float> }
+
+cbuffer CB {
+  S cbs;
+};
+// CHECK-DXIL-DAG: @cbs = external hidden addrspace(2) global %S, align 1
+// CHECK-SPIR-DAG: @cbs = external hidden addrspace(12) global %S, align 1
+
+void main() {
+  S tmp = (S)cbs;
+// CHECK-DXIL: %agg-temp = alloca %struct.S, align 1
+// CHECK-DXIL: %[[#DST:]] = getelementptr inbounds %struct.S, ptr %agg-temp, i32 0, i32 0
+// CHECK-DXIL: %cbuf.load = load <3 x float>, ptr addrspace(2) @cbs, align 4
+// CHECK-DXIL: store <3 x float> %cbuf.load, ptr %[[#DST]], align 4
+
+// CHECK-DXIL: %[[#DST:]] = getelementptr inbounds %struct.S, ptr %agg-temp, i32 0, i32 1
+// CHECK-DXIL: %cbuf.load1 = load <4 x float>, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @cbs, i32 16), align 4
+// CHECK-DXIL: store <4 x float> %cbuf.load1, ptr %[[#DST]], align 4
+
+// CHECK-SPIR: %agg-temp = alloca %struct.S, align 1
+// CHECK-SPIR: %[[#DST:]] = getelementptr inbounds %struct.S, ptr %agg-temp, i32 0, i32 0
+// CHECK-SPIR: %cbuf.load = load <3 x float>, ptr addrspace(12) @cbs, align 4
+// CHECK-SPIR: store <3 x float> %cbuf.load, ptr %[[#DST]], align 4
+
+// CHECK-SPIR: %[[#DST:]] = getelementptr inbounds %struct.S, ptr %agg-temp, i32 0, i32 1
+// CHECK-SPIR: %cbuf.load1 = load <4 x float>, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @cbs, i64 16), align 4
+// CHECK-SPIR: store <4 x float> %cbuf.load1, ptr %[[#DST]], align 4
+}

diff  --git a/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.logical.hlsl b/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.logical.hlsl
new file mode 100644
index 0000000000000..4f940880bbd8f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.logical.hlsl
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s -fexperimental-logical-pointer | FileCheck %s --check-prefixes=CHECK-DXIL
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s -fexperimental-logical-pointer | FileCheck %s --check-prefixes=CHECK-SPIR
+
+struct S {
+  float3 a;
+  float4 b;
+};
+// CHECK-DXIL-DAG: %S = type <{ <3 x float>, target("dx.Padding", 4), <4 x float> }>
+// CHECK-DXIL-DAG: %struct.S = type { <3 x float>, <4 x float> }
+// CHECK-SPIR-DAG: %S = type <{ <3 x float>, target("spirv.Padding", 4), <4 x float> }>
+// CHECK-SPIR-DAG: %struct.S = type { <3 x float>, <4 x float> }
+
+cbuffer CB {
+  S cbs;
+};
+// CHECK-DXIL-DAG: @cbs = external hidden addrspace(2) global %S, align 1
+// CHECK-SPIR-DAG: @cbs = external hidden addrspace(12) global %S, align 1
+
+void main() {
+  S tmp = (S)cbs;
+// CHECK-DXIL: %agg-temp = call elementtype(%struct.S) ptr @llvm.structured.alloca.p0()
+// CHECK-DXIL: %[[#SRC:]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(%S) @cbs, i32 0)
+// CHECK-DXIL: %[[#DST:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %agg-temp, i32 0)
+// CHECK-DXIL: %cbuf.load = load <3 x float>, ptr addrspace(2) %[[#SRC]], align 4
+// CHECK-DXIL: store <3 x float> %cbuf.load, ptr %[[#DST]], align 4
+
+// CHECK-DXIL: %[[#SRC:]] = call ptr addrspace(2) (ptr addrspace(2), ...) @llvm.structured.gep.p2(ptr addrspace(2) elementtype(%S) @cbs, i32 2)
+// CHECK-DXIL: %[[#DST:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %agg-temp, i32 1)
+// CHECK-DXIL: %cbuf.load1 = load <4 x float>, ptr addrspace(2) %[[#SRC]], align 4
+// CHECK-DXIL: store <4 x float> %cbuf.load1, ptr %[[#DST]], align 4
+
+// CHECK-SPIR: %agg-temp = call elementtype(%struct.S) ptr @llvm.structured.alloca.p0()
+// CHECK-SPIR: %[[#SRC:]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(%S) @cbs, i32 0)
+// CHECK-SPIR: %[[#DST:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %agg-temp, i32 0)
+// CHECK-SPIR: %cbuf.load = load <3 x float>, ptr addrspace(12) %[[#SRC]], align 4
+// CHECK-SPIR: store <3 x float> %cbuf.load, ptr %[[#DST]], align 4
+
+// CHECK-SPIR: %[[#SRC:]] = call ptr addrspace(12) (ptr addrspace(12), ...) @llvm.structured.gep.p12(ptr addrspace(12) elementtype(%S) @cbs, i32 2)
+// CHECK-SPIR: %[[#DST:]] = call ptr (ptr, ...) @llvm.structured.gep.p0(ptr elementtype(%struct.S) %agg-temp, i32 1)
+// CHECK-SPIR: %cbuf.load1 = load <4 x float>, ptr addrspace(12) %[[#SRC]], align 4
+// CHECK-SPIR: store <4 x float> %cbuf.load1, ptr %[[#DST]], align 4
+}


        


More information about the cfe-commits mailing list