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

Nathan Gauër via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 21 06:59:15 PDT 2026


https://github.com/Keenuts created https://github.com/llvm/llvm-project/pull/193227

This commits adds SPIR-V testing on an existing test (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.

>From 1272e8f44af4757de95662b71db19ce5575d23b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Mon, 20 Apr 2026 15:03:11 +0200
Subject: [PATCH] [HLSL] Handle logical pointer for array assign

This commits adds SPIR-V testing on an existing test (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.
---
 clang/lib/CodeGen/CGBuilder.h                 |  22 +
 clang/lib/CodeGen/CGExprAgg.cpp               |  12 +-
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 173 +++----
 clang/lib/CodeGen/CodeGenModule.cpp           |   4 +
 clang/test/CodeGenHLSL/ArrayAssignable.hlsl   | 461 +++++++++++++-----
 .../ArrayAssignable.logicalptr.hlsl           | 443 +++++++++++++++++
 6 files changed, 897 insertions(+), 218 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/ArrayAssignable.logicalptr.hlsl

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 3a4291719da74..e5007f59e452c 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -647,9 +647,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 1e25172d18890..a42b2c3b7b596 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1469,64 +1469,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;
 
@@ -1545,51 +1515,86 @@ 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));
+
+    auto *DST = cast<llvm::StructType>(DstTy);
+    for (unsigned I = 0; I < SrcTy->getNumElements(); ++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(I), DstElt,
+                      DST->getElementType(I));
+    }
+  }
 
-    // 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));
+  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());
     }
-    return true;
+  }
+
+  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);
@@ -1599,16 +1604,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/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index c635a6c175b25..28937f4a5382e 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -484,6 +484,10 @@ CodeGenModule::CodeGenModule(ASTContext &C,
     TBAA.reset(new CodeGenTBAA(Context, getTypes(), TheModule, CodeGenOpts,
                                getLangOpts()));
 
+  if (LangOpts.EmitLogicalPointer)
+    getModule().addModuleFlag(llvm::Module::Error,
+                              "ExperimentalEmitLogicalPointer", true);
+
   // If debug info or coverage generation is enabled, create the CGDebugInfo
   // object.
   if (CodeGenOpts.getDebugInfo() != llvm::codegenoptions::NoDebugInfo ||
diff --git a/clang/test/CodeGenHLSL/ArrayAssignable.hlsl b/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
index 9a568fe3371d0..acd1e1a3e0724 100644
--- a/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
+++ b/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
@@ -1,18 +1,12 @@
-// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
+// 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 | 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 {
   int x;
   float f;
 };
 
-// CHECK: [[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: @CBArrays.cb = global target("dx.CBuffer", [[CBLayout]])
-// CHECK: @c1 = external hidden addrspace(2) global <{ [1 x <{ float, target("dx.Padding", 12) }>], float }>, align 4
-// CHECK: @c2 = external hidden addrspace(2) global [2 x <4 x i32>], align 4
-// CHECK: @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: @c4 = external hidden addrspace(2) global <{ [1 x <{ %S, target("dx.Padding", 8) }>], %S }>, align 1
-
 cbuffer CBArrays : register(b0) {
   float c1[2];
   int4 c2[2];
@@ -20,31 +14,75 @@ cbuffer CBArrays : register(b0) {
   S c4[2];
 }
 
-// CHECK-LABEL: define hidden void {{.*}}arr_assign1
-// CHECK: [[Arr:%.*]] = alloca [2 x i32], align 4
-// CHECK-NEXT: [[Arr2:%.*]] = alloca [2 x i32], align 4
-// CHECK-NOT: alloca
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{@.*}}, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memset.p0.i32(ptr align 4 [[Arr2]], i8 0, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 [[Arr2]], i32 8, i1 false)
-// CHECK-NEXT: ret void
+// 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:%.*]] = alloca [2 x i32], align 4
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = alloca [2 x i32], align 4
+// 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:    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:%.*]] = alloca [2 x i32], align 4
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = alloca [2 x i32], align 4
+// 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:    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-LABEL: define hidden void {{.*}}arr_assign2
-// CHECK: [[Arr:%.*]] = alloca [2 x i32], align 4
-// CHECK-NEXT: [[Arr2:%.*]] = alloca [2 x i32], align 4
-// CHECK-NEXT: [[Arr3:%.*]] = alloca [2 x i32], align 4
-// CHECK-NOT: alloca
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{@.*}}, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memset.p0.i32(ptr align 4 [[Arr2]], i8 0, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 {{@.*}}, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr2]], ptr align 4 [[Arr3]], i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 [[Arr2]], i32 8, i1 false)
-// CHECK-NEXT: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign2v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = alloca [2 x i32], align 4
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = alloca [2 x i32], align 4
+// CHECK-DXIL-NEXT:    [[ARR3:%.*]] = alloca [2 x i32], align 4
+// 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:    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 [[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:%.*]] = alloca [2 x i32], align 4
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = alloca [2 x i32], align 4
+// CHECK-SPIR-NEXT:    [[ARR3:%.*]] = alloca [2 x i32], align 4
+// 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:    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 [[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};
@@ -52,49 +90,95 @@ void arr_assign2() {
   Arr = Arr2 = Arr3;
 }
 
-// CHECK-LABEL: define hidden void {{.*}}arr_assign3
-// CHECK: [[Arr3:%.*]] = alloca [2 x [2 x i32]], align 4
-// CHECK-NEXT: [[Arr4:%.*]] = alloca [2 x [2 x i32]], align 4
-// CHECK-NOT: alloca
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 {{@.*}}, i32 16, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr4]], ptr align 4 {{@.*}}, i32 16, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 [[Arr4]], i32 16, i1 false)
-// CHECK-NEXT: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign3v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = alloca [2 x [2 x i32]], align 4
+// CHECK-DXIL-NEXT:    [[ARR3:%.*]] = alloca [2 x [2 x i32]], align 4
+// 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:    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:%.*]] = alloca [2 x [2 x i32]], align 4
+// CHECK-SPIR-NEXT:    [[ARR3:%.*]] = alloca [2 x [2 x i32]], align 4
+// 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:    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-LABEL: define hidden void {{.*}}arr_assign4
-// CHECK: [[Arr:%.*]] = alloca [2 x i32], align 4
-// CHECK-NEXT: [[Arr2:%.*]] = alloca [2 x i32], align 4
-// CHECK-NOT: alloca
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{@.*}}, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memset.p0.i32(ptr align 4 [[Arr2]], i8 0, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 [[Arr2]], i32 8, i1 false)
-// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[Arr]], i32 0, i32 0
-// CHECK-NEXT: store i32 6, ptr [[Idx]], align 4
-// CHECK-NEXT: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign4v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = alloca [2 x i32], align 4
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = alloca [2 x i32], align 4
+// 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:    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:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARR]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[ARRAYIDX]], 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:%.*]] = alloca [2 x i32], align 4
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = alloca [2 x i32], align 4
+// 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:    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:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARR]], i64 0, i64 0
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[ARRAYIDX]], 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-LABEL: define hidden void {{.*}}arr_assign5
-// CHECK: [[Arr:%.*]] = alloca [2 x i32], align 4
-// CHECK-NEXT: [[Arr2:%.*]] = alloca [2 x i32], align 4
-// CHECK-NEXT: [[Arr3:%.*]] = alloca [2 x i32], align 4
-// CHECK-NOT: alloca
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{@.*}}, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memset.p0.i32(ptr align 4 [[Arr2]], i8 0, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 {{@.*}}, i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr2]], ptr align 4 [[Arr3]], i32 8, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 [[Arr2]], i32 8, i1 false)
-// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x i32], ptr [[Arr]], i32 0, i32 0
-// CHECK-NEXT: store i32 6, ptr [[Idx]], align 4
-// CHECK-NEXT: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign5v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = alloca [2 x i32], align 4
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = alloca [2 x i32], align 4
+// CHECK-DXIL-NEXT:    [[ARR3:%.*]] = alloca [2 x i32], align 4
+// 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:    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 [[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:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARR]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[ARRAYIDX]], 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:%.*]] = alloca [2 x i32], align 4
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = alloca [2 x i32], align 4
+// CHECK-SPIR-NEXT:    [[ARR3:%.*]] = alloca [2 x i32], align 4
+// 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:    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 [[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:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARR]], i64 0, i64 0
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[ARRAYIDX]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
 void arr_assign5() {
   int Arr[2] = {0, 1};
   int Arr2[2] = {0, 0};
@@ -102,35 +186,68 @@ void arr_assign5() {
   (Arr = Arr2 = Arr3)[0] = 6;
 }
 
-// CHECK-LABEL: define hidden void {{.*}}arr_assign6
-// CHECK: [[Arr3:%.*]] = alloca [2 x [2 x i32]], align 4
-// CHECK-NEXT: [[Arr4:%.*]] = alloca [2 x [2 x i32]], align 4
-// CHECK-NOT: alloca
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 {{@.*}}, i32 16, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr4]], ptr align 4 {{@.*}}, i32 16, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr3]], ptr align 4 [[Arr4]], i32 16, i1 false)
-// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[Arr3]], i32 0, i32 0
-// CHECK-NEXT: [[Idx2:%.*]] = getelementptr inbounds [2 x i32], ptr [[Idx]], i32 0, i32 0
-// CHECK-NEXT: store i32 6, ptr [[Idx2]], align 4
-// CHECK-NEXT: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign6v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = alloca [2 x [2 x i32]], align 4
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = alloca [2 x [2 x i32]], align 4
+// 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:    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:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[ARR]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAYIDX]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[ARRAYIDX1]], 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:%.*]] = alloca [2 x [2 x i32]], align 4
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = alloca [2 x [2 x i32]], align 4
+// 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:    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:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[ARR]], i64 0, i64 0
+// CHECK-SPIR-NEXT:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [2 x i32], ptr [[ARRAYIDX]], i64 0, i64 0
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[ARRAYIDX1]], 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-LABEL: define hidden void {{.*}}arr_assign7
-// CHECK: [[Arr:%.*]] = alloca [2 x [2 x i32]], align 4
-// CHECK-NEXT: [[Arr2:%.*]] = alloca [2 x [2 x i32]], align 4
-// CHECK-NOT: alloca
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{@.*}}, i32 16, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr2]], ptr align 4 {{@.*}}, i32 16, i1 false)
-// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 [[Arr2]], i32 16, i1 false)
-// CHECK-NEXT: [[Idx:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[Arr]], i32 0, i32 0
-// CHECK-NEXT: store i32 6, ptr [[Idx]], align 4
-// CHECK-NEXT: [[Idx2:%.*]] = getelementptr inbounds i32, ptr %arrayidx, i32 1
-// CHECK-NEXT: store i32 6, ptr [[Idx2]], align 4
-// CHECK-NEXT: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign7v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[ARR:%.*]] = alloca [2 x [2 x i32]], align 4
+// CHECK-DXIL-NEXT:    [[ARR2:%.*]] = alloca [2 x [2 x i32]], align 4
+// 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:    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:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[ARR]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    store i32 6, ptr [[ARRAYIDX]], align 4
+// CHECK-DXIL-NEXT:    [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i32, ptr [[ARRAYIDX]], 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:%.*]] = alloca [2 x [2 x i32]], align 4
+// CHECK-SPIR-NEXT:    [[ARR2:%.*]] = alloca [2 x [2 x i32]], align 4
+// 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:    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:    [[ARRAYIDX:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[ARR]], i64 0, i64 0
+// CHECK-SPIR-NEXT:    store i32 6, ptr [[ARRAYIDX]], align 4
+// CHECK-SPIR-NEXT:    [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i32, ptr [[ARRAYIDX]], 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}};
@@ -138,16 +255,31 @@ void arr_assign7() {
 }
 
 // Verify you can assign from a cbuffer array
-
-// CHECK-LABEL: define hidden void {{.*}}arr_assign8
-// CHECK: [[C:%.*]] = alloca [2 x float], align 4
-// CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds [2 x float], ptr [[C]], i32 0
-// CHECK-NEXT: [[L0:%.*]] = load float, ptr addrspace(2) @c1, align 4
-// CHECK-NEXT: store float [[L0]], ptr [[V0]], align 4
-// CHECK-NEXT: [[V1:%.*]] = getelementptr inbounds [2 x float], ptr [[C]], i32 0, i32 1
-// CHECK-NEXT: [[L1:%.*]] = load float, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c1, i32 16), align 4
-// CHECK-NEXT: store float [[L1]], ptr [[V1]], align 4
-// CHECK-NEXT: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign8v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[C:%.*]] = alloca [2 x float], align 4
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = getelementptr inbounds [2 x float], ptr [[C]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD:%.*]] = load float, ptr addrspace(2) @c1, align 4
+// CHECK-DXIL-NEXT:    store float [[CBUF_LOAD]], ptr [[TMP0]], align 4
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [2 x float], ptr [[C]], i32 0, i32 1
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD1:%.*]] = load float, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c1, i32 16), align 4
+// CHECK-DXIL-NEXT:    store float [[CBUF_LOAD1]], ptr [[TMP1]], 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:%.*]] = alloca [2 x float], align 4
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [2 x float], ptr [[C]], i32 0, i32 0
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD:%.*]] = load float, ptr addrspace(12) @c1, align 4
+// CHECK-SPIR-NEXT:    store float [[CBUF_LOAD]], ptr [[TMP1]], align 4
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = getelementptr inbounds [2 x float], ptr [[C]], i32 0, i32 1
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD1:%.*]] = load float, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @c1, i64 16), align 4
+// CHECK-SPIR-NEXT:    store float [[CBUF_LOAD1]], ptr [[TMP2]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
 void arr_assign8() {
   float C[2];
   C = c1;
@@ -155,56 +287,123 @@ void arr_assign8() {
 
 // TODO: We should be able to just memcpy here.
 // See https://github.com/llvm/wg-hlsl/issues/351
+// CHECK-DXIL-LABEL: define hidden void @_Z11arr_assign9v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[C:%.*]] = alloca [2 x <4 x i32>], align 4
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = getelementptr inbounds [2 x <4 x i32>], ptr [[C]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD:%.*]] = load <4 x i32>, ptr addrspace(2) @c2, align 4
+// CHECK-DXIL-NEXT:    store <4 x i32> [[CBUF_LOAD]], ptr [[TMP0]], align 4
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [2 x <4 x i32>], ptr [[C]], i32 0, i32 1
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD1:%.*]] = load <4 x i32>, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c2, i32 16), align 4
+// CHECK-DXIL-NEXT:    store <4 x i32> [[CBUF_LOAD1]], ptr [[TMP1]], 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:%.*]] = alloca [2 x <4 x i32>], align 4
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [2 x <4 x i32>], ptr [[C]], i32 0, i32 0
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD:%.*]] = load <4 x i32>, ptr addrspace(12) @c2, align 4
+// CHECK-SPIR-NEXT:    store <4 x i32> [[CBUF_LOAD]], ptr [[TMP1]], align 4
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = getelementptr inbounds [2 x <4 x i32>], ptr [[C]], i32 0, i32 1
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD1:%.*]] = load <4 x i32>, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @c2, i64 16), align 4
+// CHECK-SPIR-NEXT:    store <4 x i32> [[CBUF_LOAD1]], ptr [[TMP2]], align 4
+// CHECK-SPIR-NEXT:    ret void
 //
-// CHECK-LABEL: define hidden void {{.*}}arr_assign9
-// CHECK: [[C:%.*]] = alloca [2 x <4 x i32>], align 4
-// CHECK-NEXT: [[V0:%.*]] = getelementptr inbounds [2 x <4 x i32>], ptr [[C]], i32 0
-// CHECK-NEXT: [[L0:%.*]] = load <4 x i32>, ptr addrspace(2) @c2, align 4
-// CHECK-NEXT: store <4 x i32> [[L0]], ptr [[V0]], align 4
-// CHECK-NEXT: [[V1:%.*]] = getelementptr inbounds [2 x <4 x i32>], ptr [[C]], i32 0, i32 1
-// CHECK-NEXT: [[L1:%.*]] = load <4 x i32>, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c2, i32 16), align 4
-// CHECK-NEXT: store <4 x i32> [[L1]], ptr [[V1]], align 4
-// CHECK-NEXT: ret void
 void arr_assign9() {
   int4 C[2];
   C = c2;
 }
 
-// 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: [[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: [[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: [[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: [[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: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z12arr_assign10v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[C:%.*]] = alloca [2 x [2 x i32]], align 4
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP0]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD:%.*]] = load i32, ptr addrspace(2) @c3, align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD]], ptr [[TMP1]], align 4
+// CHECK-DXIL-NEXT:    [[TMP2:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP0]], i32 0, i32 1
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD1:%.*]] = load i32, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c3, i32 16), align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD1]], ptr [[TMP2]], align 4
+// CHECK-DXIL-NEXT:    [[TMP3:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 1
+// CHECK-DXIL-NEXT:    [[TMP4:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP3]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c3, i32 32), align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD2]], ptr [[TMP4]], align 4
+// CHECK-DXIL-NEXT:    [[TMP5:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP3]], i32 0, i32 1
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD3:%.*]] = load i32, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c3, i32 48), align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD3]], ptr [[TMP5]], 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:%.*]] = alloca [2 x [2 x i32]], align 4
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 0
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP1]], i32 0, i32 0
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD:%.*]] = load i32, ptr addrspace(12) @c3, align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD]], ptr [[TMP2]], align 4
+// CHECK-SPIR-NEXT:    [[TMP3:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP1]], i32 0, i32 1
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD1:%.*]] = load i32, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @c3, i64 16), align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD1]], ptr [[TMP3]], align 4
+// CHECK-SPIR-NEXT:    [[TMP4:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[C]], i32 0, i32 1
+// CHECK-SPIR-NEXT:    [[TMP5:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP4]], i32 0, i32 0
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @c3, i64 32), align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD2]], ptr [[TMP5]], align 4
+// CHECK-SPIR-NEXT:    [[TMP6:%.*]] = getelementptr inbounds [2 x i32], ptr [[TMP4]], i32 0, i32 1
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD3:%.*]] = load i32, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @c3, i64 48), align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD3]], ptr [[TMP6]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
 void arr_assign10() {
   int C[2][2];
   C = c3;
 }
 
-// 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: [[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: [[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: [[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: [[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: ret void
+// CHECK-DXIL-LABEL: define hidden void @_Z12arr_assign11v(
+// CHECK-DXIL-SAME: ) #[[ATTR2]] {
+// CHECK-DXIL-NEXT:  [[ENTRY:.*:]]
+// CHECK-DXIL-NEXT:    [[C:%.*]] = alloca [2 x [[STRUCT_S:%.*]]], align 1
+// CHECK-DXIL-NEXT:    [[TMP0:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[C]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP0]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD:%.*]] = load i32, ptr addrspace(2) @c4, align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD]], ptr [[TMP1]], align 4
+// CHECK-DXIL-NEXT:    [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP0]], i32 0, i32 1
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD1:%.*]] = load float, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c4, i32 4), align 4
+// CHECK-DXIL-NEXT:    store float [[CBUF_LOAD1]], ptr [[TMP2]], align 4
+// CHECK-DXIL-NEXT:    [[TMP3:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[C]], i32 0, i32 1
+// CHECK-DXIL-NEXT:    [[TMP4:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP3]], i32 0, i32 0
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c4, i32 16), align 4
+// CHECK-DXIL-NEXT:    store i32 [[CBUF_LOAD2]], ptr [[TMP4]], align 4
+// CHECK-DXIL-NEXT:    [[TMP5:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP3]], i32 0, i32 1
+// CHECK-DXIL-NEXT:    [[CBUF_LOAD3:%.*]] = load float, ptr addrspace(2) getelementptr inbounds nuw (i8, ptr addrspace(2) @c4, i32 20), align 4
+// CHECK-DXIL-NEXT:    store float [[CBUF_LOAD3]], ptr [[TMP5]], 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:%.*]] = alloca [2 x [[STRUCT_S:%.*]]], align 1
+// CHECK-SPIR-NEXT:    [[TMP1:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[C]], i32 0, i32 0
+// CHECK-SPIR-NEXT:    [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP1]], i32 0, i32 0
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD:%.*]] = load i32, ptr addrspace(12) @c4, align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD]], ptr [[TMP2]], align 4
+// CHECK-SPIR-NEXT:    [[TMP3:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP1]], i32 0, i32 1
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD1:%.*]] = load float, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @c4, i64 4), align 4
+// CHECK-SPIR-NEXT:    store float [[CBUF_LOAD1]], ptr [[TMP3]], align 4
+// CHECK-SPIR-NEXT:    [[TMP4:%.*]] = getelementptr inbounds [2 x [[STRUCT_S]]], ptr [[C]], i32 0, i32 1
+// CHECK-SPIR-NEXT:    [[TMP5:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP4]], i32 0, i32 0
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @c4, i64 16), align 4
+// CHECK-SPIR-NEXT:    store i32 [[CBUF_LOAD2]], ptr [[TMP5]], align 4
+// CHECK-SPIR-NEXT:    [[TMP6:%.*]] = getelementptr inbounds [[STRUCT_S]], ptr [[TMP4]], i32 0, i32 1
+// CHECK-SPIR-NEXT:    [[CBUF_LOAD3:%.*]] = load float, ptr addrspace(12) getelementptr inbounds nuw (i8, ptr addrspace(12) @c4, i64 20), align 4
+// CHECK-SPIR-NEXT:    store float [[CBUF_LOAD3]], ptr [[TMP6]], align 4
+// CHECK-SPIR-NEXT:    ret void
+//
 void arr_assign11() {
   S C[2];
   C = c4;
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;
+}



More information about the cfe-commits mailing list