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

Nathan Gauër via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 04:00:32 PDT 2026


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

>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 1/5] [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;
+}

>From f2ac0627f1275795ce594f4a3b74bb9387314a5f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Wed, 22 Apr 2026 15:09:38 +0200
Subject: [PATCH 2/5] remove unrelated module flag:

---
 clang/lib/CodeGen/CodeGenModule.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 28937f4a5382e..c635a6c175b25 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -484,10 +484,6 @@ 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 ||

>From 64baff3d052a8b90f6145b958cef864e127a7725 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Wed, 22 Apr 2026 16:46:05 +0200
Subject: [PATCH 3/5] revert test change

---
 clang/test/CodeGenHLSL/ArrayAssignable.hlsl | 461 ++++++--------------
 1 file changed, 131 insertions(+), 330 deletions(-)

diff --git a/clang/test/CodeGenHLSL/ArrayAssignable.hlsl b/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
index acd1e1a3e0724..9a568fe3371d0 100644
--- a/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
+++ b/clang/test/CodeGenHLSL/ArrayAssignable.hlsl
@@ -1,12 +1,18 @@
-// 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
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
 
 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];
@@ -14,75 +20,31 @@ cbuffer CBArrays : register(b0) {
   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:%.*]] = 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
-//
+// 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
 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:%.*]] = 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
-//
+// 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
 void arr_assign2() {
   int Arr[2] = {0, 1};
   int Arr2[2] = {0, 0};
@@ -90,95 +52,49 @@ void arr_assign2() {
   Arr = Arr2 = Arr3;
 }
 
-// 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
-//
+// 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
 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:%.*]] = 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
-//
+// 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
 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:%.*]] = 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
-//
+// 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
 void arr_assign5() {
   int Arr[2] = {0, 1};
   int Arr2[2] = {0, 0};
@@ -186,68 +102,35 @@ void arr_assign5() {
   (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:%.*]] = 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
-//
+// 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
 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:%.*]] = 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
-//
+// 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
 void arr_assign7() {
   int Arr[2][2] = {{0, 1}, {2, 3}};
   int Arr2[2][2] = {{0, 0}, {1, 1}};
@@ -255,31 +138,16 @@ void arr_assign7() {
 }
 
 // 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:%.*]] = 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
-//
+
+// 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
 void arr_assign8() {
   float C[2];
   C = c1;
@@ -287,123 +155,56 @@ 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-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
-//
+// 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
 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:%.*]] = 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
-//
+// 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
 void arr_assign11() {
   S C[2];
   C = c4;

>From a407d23a8340b725134f3ef1d0a5814c3d76597c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Wed, 22 Apr 2026 16:53:12 +0200
Subject: [PATCH 4/5] update test without reformat/generator

---
 clang/test/CodeGenHLSL/ArrayAssignable.hlsl | 34 ++++++++++++---------
 1 file changed, 19 insertions(+), 15 deletions(-)

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];

>From 2cbc96adde0ff56589fa96f85a35b94ab205ddc9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Thu, 23 Apr 2026 11:14:44 +0200
Subject: [PATCH 5/5] skip padding on structs

---
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 34 +++++++++++----
 .../resources/cbuffer_struct_passing.hlsl     | 38 +++++++++++++++++
 .../cbuffer_struct_passing.logical.hlsl       | 42 +++++++++++++++++++
 3 files changed, 106 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.hlsl
 create mode 100644 clang/test/CodeGenHLSL/resources/cbuffer_struct_passing.logical.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index a42b2c3b7b596..5643f6917bf97 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -1551,14 +1551,32 @@ class HLSLBufferCopyEmitter {
       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));
+    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;
+      }
+
+      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;
     }
   }
 
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