[clang] [HLSL] Implement HLSL intialization list support (PR #123141)

Chris B via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 11 20:53:33 PST 2025


https://github.com/llvm-beanz updated https://github.com/llvm/llvm-project/pull/123141

>From a5d0d86b71ac7d7083295f255b42d991859a62f1 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Wed, 15 Jan 2025 13:40:29 -0600
Subject: [PATCH 01/10] [HLSL] Implement HLSL intialization list support

This PR implements HLSL's initialization list behvaior as specified in
the draft language specifcation under
[*Decl.Init.Agg*](https://microsoft.github.io/hlsl-specs/specs/hlsl.html
#Decl.Init.Agg).

This behavior is a bit unusual for C/C++ because intermediate braces in
initializer lists are ignored and a whole array of additional
conversions occur unintuitively to how initializaiton works in C.

The implementaiton in this PR generates a valid C/C++ initialization
list AST for the HLSL initializer so that there are no changes required
to Clang's CodeGen to support this. This design will also allow us to
use Clang's rewrite to convert HLSL initializers to valid C/C++
initializers that are equivalent. It does have the downside that it
will generate often redundant accesses during codegen. The IR optimizer
is extremely good at eliminating those so this will have no impact on
the final executable performance.

There is some opportunity for optimizing the initializer list
generation that we could consider in subsequent commits. One notable
opportunity would be to identify aggregate objects that occur in the
same place in both initializers and do not require converison, those
aggregates could be initialized as aggregates rather than fully
scalarized.

Closes #56067
---
 .../clang/Basic/DiagnosticSemaKinds.td        |   3 +
 clang/include/clang/Sema/SemaHLSL.h           |   5 +
 clang/lib/Sema/SemaChecking.cpp               |   9 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 159 ++++
 clang/lib/Sema/SemaInit.cpp                   |   5 +
 clang/test/CodeGenHLSL/ArrayTemporary.hlsl    |   3 +-
 .../CodeGenHLSL/BasicFeatures/InitLists.hlsl  | 714 ++++++++++++++++++
 clang/test/SemaHLSL/ArrayTemporary.hlsl       |   2 +-
 clang/test/SemaHLSL/Language/InitLists.hlsl   |  69 ++
 9 files changed, 964 insertions(+), 5 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
 create mode 100644 clang/test/SemaHLSL/Language/InitLists.hlsl

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index cf390724b07a4..f29ad00dbe413 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12617,6 +12617,9 @@ def err_hlsl_pointers_unsupported : Error<
   "%select{pointers|references}0 are unsupported in HLSL">;
 def err_hlsl_missing_resource_class : Error<"HLSL resource needs to have [[hlsl::resource_class()]] attribute">;
 def err_hlsl_attribute_needs_intangible_type: Error<"attribute %0 can be used only on HLSL intangible type %1">;
+def err_hlsl_incorrect_num_initializers: Error<
+  "too %select{few|many}0 initializers in list for type %1 "
+  "(expected %2 but found %3)">;
 
 def err_hlsl_operator_unsupported : Error<
   "the '%select{&|*|->}0' operator is unsupported in HLSL">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 6e8ca2e4710de..0da4b87fc3f0a 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -26,6 +26,8 @@
 namespace clang {
 class AttributeCommonInfo;
 class IdentifierInfo;
+class InitializedEntity;
+class InitializationKind;
 class ParsedAttr;
 class Scope;
 class VarDecl;
@@ -148,6 +150,9 @@ class SemaHLSL : public SemaBase {
 
   QualType getInoutParameterType(QualType Ty);
 
+  bool TransformInitList(const InitializedEntity &Entity,
+                         const InitializationKind &Kind, InitListExpr *Init);
+
 private:
   // HLSL resource type attributes need to be processed all at once.
   // This is a list to collect them.
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 66c233de4ef30..e423b820c6032 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -11678,9 +11678,12 @@ static void AnalyzeImplicitConversions(
 
   // Propagate whether we are in a C++ list initialization expression.
   // If so, we do not issue warnings for implicit int-float conversion
-  // precision loss, because C++11 narrowing already handles it.
-  bool IsListInit = Item.IsListInit ||
-                    (isa<InitListExpr>(OrigE) && S.getLangOpts().CPlusPlus);
+  // precision loss, because C++11 narrowing already handles it. HLSL's
+  // initialization lists are special, so they shouldn't observe the C++
+  // behavior here.
+  bool IsListInit =
+      Item.IsListInit || (isa<InitListExpr>(OrigE) &&
+                          S.getLangOpts().CPlusPlus && !S.getLangOpts().HLSL);
 
   if (E->isTypeDependent() || E->isValueDependent())
     return;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ec6b5b45de42b..d474082827c15 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3010,3 +3010,162 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
     }
   }
 }
+
+static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
+                            llvm::SmallVectorImpl<Expr *> &List,
+                            llvm::SmallVectorImpl<QualType> &DestTypes) {
+  if (List.size() >= DestTypes.size())
+    return false;
+  InitializedEntity Entity =
+      InitializedEntity::InitializeParameter(Ctx, DestTypes[List.size()], false);
+  ExprResult Res =
+      S.PerformCopyInitialization(Entity, E->getBeginLoc(), E);
+  if (Res.isInvalid())
+    return false;
+  Expr *Init = Res.get();
+  List.push_back(Init);
+  return true;
+}
+
+static void BuildIntializerList(Sema &S, ASTContext &Ctx, Expr *E,
+                                llvm::SmallVectorImpl<Expr *> &List,
+                                llvm::SmallVectorImpl<QualType> &DestTypes,
+                                bool &ExcessInits) {
+  if (List.size() >= DestTypes.size()) {
+    ExcessInits = true;
+    return;
+  }
+
+  // If this is an initialization list, traverse the sub initializers.
+  if (auto *Init = dyn_cast<InitListExpr>(E)) {
+    for (auto *SubInit : Init->inits())
+      BuildIntializerList(S, Ctx, SubInit, List, DestTypes, ExcessInits);
+    return;
+  }
+
+  // If this is a scalar type, just enqueue the expression.
+  QualType Ty = E->getType();
+  if (Ty->isScalarType()) {
+    (void)CastInitializer(S, Ctx, E, List, DestTypes);
+    return;
+  }
+
+  if (auto *ATy = Ty->getAs<VectorType>()) {
+    uint64_t Size = ATy->getNumElements();
+
+    if (List.size() + Size > DestTypes.size()) {
+      ExcessInits = true;
+      return;
+    }
+    QualType SizeTy = Ctx.getSizeType();
+    uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
+    for (uint64_t I = 0; I < Size; ++I) {
+      auto *Idx = IntegerLiteral::Create(Ctx, llvm::APInt(SizeTySize, I),
+                                         SizeTy, SourceLocation());
+
+      ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
+          E, E->getBeginLoc(), Idx, E->getEndLoc());
+      if (ElExpr.isInvalid())
+        return;
+      if (!CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes))
+        return;
+    }
+    return;
+  }
+
+  if (auto *VTy = dyn_cast<ConstantArrayType>(Ty.getTypePtr())) {
+    uint64_t Size = VTy->getZExtSize();
+    QualType SizeTy = Ctx.getSizeType();
+    uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
+    for (uint64_t I = 0; I < Size; ++I) {
+      auto *Idx = IntegerLiteral::Create(Ctx, llvm::APInt(SizeTySize, I),
+                                         SizeTy, SourceLocation());
+      ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
+          E, E->getBeginLoc(), Idx, E->getEndLoc());
+      if (ElExpr.isInvalid())
+        return;
+      BuildIntializerList(S, Ctx, ElExpr.get(), List, DestTypes, ExcessInits);
+    }
+    return;
+  }
+
+  if (auto *RTy = Ty->getAs<RecordType>()) {
+    for (auto *FD : RTy->getDecl()->fields()) {
+      DeclAccessPair Found = DeclAccessPair::make(FD, FD->getAccess());
+      DeclarationNameInfo NameInfo(FD->getDeclName(), E->getBeginLoc());
+      ExprResult Res = S.BuildFieldReferenceExpr(
+          E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo);
+      if (Res.isInvalid())
+        return;
+      BuildIntializerList(S, Ctx, Res.get(), List, DestTypes, ExcessInits);
+    }
+  }
+}
+
+static Expr *GenerateInitLists(ASTContext &Ctx, QualType Ty,
+                               llvm::SmallVectorImpl<Expr *>::iterator &It) {
+  if (Ty->isScalarType()) {
+    return *(It++);
+  }
+  llvm::SmallVector<Expr *> Inits;
+  assert(!isa<MatrixType>(Ty) && "Matrix types not yet supported in HLSL");
+  if (Ty->isVectorType() || Ty->isConstantArrayType()) {
+    QualType ElTy;
+    uint64_t Size = 0;
+    if (auto *ATy = Ty->getAs<VectorType>()) {
+      ElTy = ATy->getElementType();
+      Size = ATy->getNumElements();
+    } else {
+      auto *VTy = cast<ConstantArrayType>(Ty.getTypePtr());
+      ElTy = VTy->getElementType();
+      Size = VTy->getZExtSize();
+    }
+    for (uint64_t I = 0; I < Size; ++I)
+      Inits.push_back(GenerateInitLists(Ctx, ElTy, It));
+  }
+  if (const RecordDecl *RD = Ty->getAsRecordDecl()) {
+    for (auto *FD : RD->fields()) {
+      Inits.push_back(GenerateInitLists(Ctx, FD->getType(), It));
+    }
+  }
+  auto *NewInit = new (Ctx) InitListExpr(Ctx, Inits.front()->getBeginLoc(),
+                                         Inits, Inits.back()->getEndLoc());
+  NewInit->setType(Ty);
+  return NewInit;
+}
+
+bool SemaHLSL::TransformInitList(const InitializedEntity &Entity,
+                                 const InitializationKind &Kind,
+                                 InitListExpr *Init) {
+  // If the initializer is a scalar, just return it.
+  if (Init->getType()->isScalarType())
+    return true;
+  ASTContext &Ctx = SemaRef.getASTContext();
+  llvm::SmallVector<QualType, 16> DestTypes;
+  // An initializer list might be attempting to initialize a reference or
+  // rvalue-reference. When checking the initializer we should look through the
+  // reference.
+  QualType InitTy = Entity.getType().getNonReferenceType();
+  BuildFlattenedTypeList(InitTy, DestTypes);
+
+  llvm::SmallVector<Expr *, 16> ArgExprs;
+  bool ExcessInits = false;
+  for (Expr *Arg : Init->inits())
+    BuildIntializerList(SemaRef, Ctx, Arg, ArgExprs, DestTypes, ExcessInits);
+
+  if (DestTypes.size() != ArgExprs.size() || ExcessInits) {
+    int TooManyOrFew = ExcessInits ? 1 : 0;
+    SemaRef.Diag(Init->getBeginLoc(), diag::err_hlsl_incorrect_num_initializers)
+        << TooManyOrFew << InitTy << DestTypes.size() << ArgExprs.size();
+    return false;
+  }
+
+  auto It = ArgExprs.begin();
+  // GenerateInitLists will always return an InitListExpr here, because the
+  // scalar case is handled above.
+  auto *NewInit = cast<InitListExpr>(GenerateInitLists(Ctx, InitTy, It));
+  Init->resizeInits(Ctx, NewInit->getNumInits());
+  for (unsigned I = 0; I < NewInit->getNumInits(); ++I)
+    Init->updateInit(Ctx, I, NewInit->getInit(I));
+  return true;
+}
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 308222a79d920..257072d5d7093 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -26,6 +26,7 @@
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Ownership.h"
+#include "clang/Sema/SemaHLSL.h"
 #include "clang/Sema/SemaObjC.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/FoldingSet.h"
@@ -4785,6 +4786,10 @@ static void TryListInitialization(Sema &S,
                                   bool TreatUnavailableAsInvalid) {
   QualType DestType = Entity.getType();
 
+  if (S.getLangOpts().HLSL &&
+      !S.HLSL().TransformInitList(Entity, Kind, InitList))
+    return;
+
   // C++ doesn't allow scalar initialization with more than one argument.
   // But C99 complex numbers are scalars and it makes sense there.
   if (S.getLangOpts().CPlusPlus && DestType->isScalarType() &&
diff --git a/clang/test/CodeGenHLSL/ArrayTemporary.hlsl b/clang/test/CodeGenHLSL/ArrayTemporary.hlsl
index e5db7eac37a42..91a283554459d 100644
--- a/clang/test/CodeGenHLSL/ArrayTemporary.hlsl
+++ b/clang/test/CodeGenHLSL/ArrayTemporary.hlsl
@@ -1,3 +1,4 @@
+
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
 
 void fn(float x[2]) { }
@@ -27,7 +28,7 @@ void fn2(Obj O[4]) { }
 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 32, i1 false)
 // CHECK: call void {{.*}}fn2{{.*}}(ptr noundef byval([4 x %struct.Obj]) align 4 [[Tmp]])
 void call2() {
-  Obj Arr[4] = {};
+  Obj Arr[4] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
   fn2(Arr);
 }
 
diff --git a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
new file mode 100644
index 0000000000000..e57724b0ec31f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
@@ -0,0 +1,714 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -disable-llvm-passes -emit-llvm -finclude-default-header -o - %s | FileCheck %s
+
+struct TwoFloats {
+  float X, Y;
+};
+
+struct TwoInts {
+  int Z, W;
+};
+
+struct Doggo {
+  int4 LegState;
+  int TailState;
+  float HairCount;
+  float4 EarDirection[2];
+};
+
+struct AnimalBits {
+  int Legs[4];
+  uint State;
+  int64_t Counter;
+  float4 LeftDir;
+  float4 RightDir;
+};
+
+struct Kitteh {
+  int4 Legs;
+  int TailState;
+  float HairCount;
+  float4 Claws[2];
+};
+
+struct Zoo {
+  Doggo Dogs[2];
+  Kitteh Cats[4];
+};
+
+// Case 1: Extraneous braces get ignored in literal instantiation.
+// CHECK-LABEL: define void @_Z5case1v(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOFLOATS:%.*]]) align 4 [[AGG_RESULT:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 @__const._Z5case1v.TF1, i32 8, i1 false)
+// CHECK-NEXT:    ret void
+//
+TwoFloats case1() {
+  TwoFloats TF1 = {{{1.0, 2}}};
+  return TF1;
+}
+
+// Case 2: Valid C/C++ initializer is handled appropriately.
+// CHECK-LABEL: define void @_Z5case2v(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOFLOATS:%.*]]) align 4 [[AGG_RESULT:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 @__const._Z5case2v.TF2, i32 8, i1 false)
+// CHECK-NEXT:    ret void
+//
+TwoFloats case2() {
+  TwoFloats TF2 = {1, 2};
+  return TF2;
+}
+
+// Case 3: Simple initialization with conversion of an argument.
+// CHECK-LABEL: define void @_Z5case3i(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOFLOATS:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[VAL_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT:    [[CONV:%.*]] = sitofp i32 [[TMP0]] to float
+// CHECK-NEXT:    store float [[CONV]], ptr [[X]], align 4
+// CHECK-NEXT:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    store float 2.000000e+00, ptr [[Y]], align 4
+// CHECK-NEXT:    ret void
+//
+TwoFloats case3(int Val) {
+  TwoFloats TF3 = {Val, 2};
+  return TF3;
+}
+
+// Case 4: Initialization from a scalarized vector into a structure with element
+// conversions.
+// CHECK-LABEL: define void @_Z5case4Dv2_i(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOFLOATS:%.*]]) align 4 [[AGG_RESULT:%.*]], <2 x i32> noundef [[TWOVALS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[TWOVALS_ADDR:%.*]] = alloca <2 x i32>, align 8
+// CHECK-NEXT:    store <2 x i32> [[TWOVALS]], ptr [[TWOVALS_ADDR]], align 8
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load <2 x i32>, ptr [[TWOVALS_ADDR]], align 8
+// CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <2 x i32> [[TMP0]], i64 0
+// CHECK-NEXT:    [[CONV:%.*]] = sitofp i32 [[VECEXT]] to float
+// CHECK-NEXT:    store float [[CONV]], ptr [[X]], align 4
+// CHECK-NEXT:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[TWOVALS_ADDR]], align 8
+// CHECK-NEXT:    [[VECEXT1:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1
+// CHECK-NEXT:    [[CONV2:%.*]] = sitofp i32 [[VECEXT1]] to float
+// CHECK-NEXT:    store float [[CONV2]], ptr [[Y]], align 4
+// CHECK-NEXT:    ret void
+//
+TwoFloats case4(int2 TwoVals) {
+  TwoFloats TF4 = {TwoVals};
+  return TF4;
+}
+
+// Case 5: Initialization from a scalarized vector of matching type.
+// CHECK-LABEL: define void @_Z5case5Dv2_i(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOINTS:%.*]]) align 4 [[AGG_RESULT:%.*]], <2 x i32> noundef [[TWOVALS:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[TWOVALS_ADDR:%.*]] = alloca <2 x i32>, align 8
+// CHECK-NEXT:    store <2 x i32> [[TWOVALS]], ptr [[TWOVALS_ADDR]], align 8
+// CHECK-NEXT:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load <2 x i32>, ptr [[TWOVALS_ADDR]], align 8
+// CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <2 x i32> [[TMP0]], i64 0
+// CHECK-NEXT:    store i32 [[VECEXT]], ptr [[Z]], align 4
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load <2 x i32>, ptr [[TWOVALS_ADDR]], align 8
+// CHECK-NEXT:    [[VECEXT1:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1
+// CHECK-NEXT:    store i32 [[VECEXT1]], ptr [[W]], align 4
+// CHECK-NEXT:    ret void
+//
+TwoInts case5(int2 TwoVals) {
+  TwoInts TI1 = {TwoVals};
+  return TI1;
+}
+
+// Case 6: Initialization from a scalarized structure of different type with
+// different element types.
+// CHECK-LABEL: define void @_Z5case69TwoFloats(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOINTS:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS:%.*]]) align 4 [[TF4:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF4]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load float, ptr [[X]], align 4
+// CHECK-NEXT:    [[CONV:%.*]] = fptosi float [[TMP0]] to i32
+// CHECK-NEXT:    store i32 [[CONV]], ptr [[Z]], align 4
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF4]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load float, ptr [[Y]], align 4
+// CHECK-NEXT:    [[CONV1:%.*]] = fptosi float [[TMP1]] to i32
+// CHECK-NEXT:    store i32 [[CONV1]], ptr [[W]], align 4
+// CHECK-NEXT:    ret void
+//
+TwoInts case6(TwoFloats TF4) {
+  TwoInts TI2 = {TF4};
+  return TI2;
+}
+
+// Case 7: Initialization of a complex structue, with bogus braces and element
+// conversions from a collection of scalar values, and structures.
+// CHECK-LABEL: define void @_Z5case77TwoIntsS_i9TwoFloatsS0_S0_S0_(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_DOGGO:%.*]]) align 16 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_TWOINTS:%.*]]) align 4 [[TI1:%.*]], ptr noundef byval([[STRUCT_TWOINTS]]) align 4 [[TI2:%.*]], i32 noundef [[VAL:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS:%.*]]) align 4 [[TF1:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS]]) align 4 [[TF2:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS]]) align 4 [[TF3:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS]]) align 4 [[TF4:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[VAL_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT:    [[LEGSTATE:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[TI1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[Z]], align 4
+// CHECK-NEXT:    [[VECINIT:%.*]] = insertelement <4 x i32> poison, i32 [[TMP0]], i32 0
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[TI1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[W]], align 4
+// CHECK-NEXT:    [[VECINIT1:%.*]] = insertelement <4 x i32> [[VECINIT]], i32 [[TMP1]], i32 1
+// CHECK-NEXT:    [[Z2:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[TI2]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[Z2]], align 4
+// CHECK-NEXT:    [[VECINIT3:%.*]] = insertelement <4 x i32> [[VECINIT1]], i32 [[TMP2]], i32 2
+// CHECK-NEXT:    [[W4:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[TI2]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr [[W4]], align 4
+// CHECK-NEXT:    [[VECINIT5:%.*]] = insertelement <4 x i32> [[VECINIT3]], i32 [[TMP3]], i32 3
+// CHECK-NEXT:    store <4 x i32> [[VECINIT5]], ptr [[LEGSTATE]], align 16
+// CHECK-NEXT:    [[TAILSTATE:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[TMP4]], ptr [[TAILSTATE]], align 16
+// CHECK-NEXT:    [[HAIRCOUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[AGG_RESULT]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP5:%.*]] = load i32, ptr [[VAL_ADDR]], align 4
+// CHECK-NEXT:    [[CONV:%.*]] = sitofp i32 [[TMP5]] to float
+// CHECK-NEXT:    store float [[CONV]], ptr [[HAIRCOUNT]], align 4
+// CHECK-NEXT:    [[EARDIRECTION:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[AGG_RESULT]], i32 0, i32 3
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP6:%.*]] = load float, ptr [[X]], align 4
+// CHECK-NEXT:    [[VECINIT6:%.*]] = insertelement <4 x float> poison, float [[TMP6]], i32 0
+// CHECK-NEXT:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP7:%.*]] = load float, ptr [[Y]], align 4
+// CHECK-NEXT:    [[VECINIT7:%.*]] = insertelement <4 x float> [[VECINIT6]], float [[TMP7]], i32 1
+// CHECK-NEXT:    [[X8:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF2]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP8:%.*]] = load float, ptr [[X8]], align 4
+// CHECK-NEXT:    [[VECINIT9:%.*]] = insertelement <4 x float> [[VECINIT7]], float [[TMP8]], i32 2
+// CHECK-NEXT:    [[Y10:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF2]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP9:%.*]] = load float, ptr [[Y10]], align 4
+// CHECK-NEXT:    [[VECINIT11:%.*]] = insertelement <4 x float> [[VECINIT9]], float [[TMP9]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT11]], ptr [[EARDIRECTION]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds <4 x float>, ptr [[EARDIRECTION]], i32 1
+// CHECK-NEXT:    [[X12:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF3]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP10:%.*]] = load float, ptr [[X12]], align 4
+// CHECK-NEXT:    [[VECINIT13:%.*]] = insertelement <4 x float> poison, float [[TMP10]], i32 0
+// CHECK-NEXT:    [[Y14:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF3]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP11:%.*]] = load float, ptr [[Y14]], align 4
+// CHECK-NEXT:    [[VECINIT15:%.*]] = insertelement <4 x float> [[VECINIT13]], float [[TMP11]], i32 1
+// CHECK-NEXT:    [[X16:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF4]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP12:%.*]] = load float, ptr [[X16]], align 4
+// CHECK-NEXT:    [[VECINIT17:%.*]] = insertelement <4 x float> [[VECINIT15]], float [[TMP12]], i32 2
+// CHECK-NEXT:    [[Y18:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF4]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP13:%.*]] = load float, ptr [[Y18]], align 4
+// CHECK-NEXT:    [[VECINIT19:%.*]] = insertelement <4 x float> [[VECINIT17]], float [[TMP13]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT19]], ptr [[ARRAYINIT_ELEMENT]], align 16
+// CHECK-NEXT:    ret void
+//
+Doggo case7(TwoInts TI1, TwoInts TI2, int Val, TwoFloats TF1, TwoFloats TF2,
+            TwoFloats TF3, TwoFloats TF4) {
+  Doggo D1 = {TI1, TI2, {Val, Val}, {{TF1, TF2}, {TF3, TF4}}};
+  return D1;
+}
+
+// Case 8: Initialization of a structure from a different structure with
+// significantly different element types and grouping.
+// CHECK-LABEL: define void @_Z5case85Doggo(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_ANIMALBITS:%.*]]) align 16 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_DOGGO:%.*]]) align 16 [[D1:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[LEGS:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[LEGSTATE:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr [[LEGSTATE]], align 16
+// CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <4 x i32> [[TMP0]], i64 0
+// CHECK-NEXT:    store i32 [[VECEXT]], ptr [[LEGS]], align 4
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds i32, ptr [[LEGS]], i32 1
+// CHECK-NEXT:    [[LEGSTATE1:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP1:%.*]] = load <4 x i32>, ptr [[LEGSTATE1]], align 16
+// CHECK-NEXT:    [[VECEXT2:%.*]] = extractelement <4 x i32> [[TMP1]], i64 1
+// CHECK-NEXT:    store i32 [[VECEXT2]], ptr [[ARRAYINIT_ELEMENT]], align 4
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT3:%.*]] = getelementptr inbounds i32, ptr [[LEGS]], i32 2
+// CHECK-NEXT:    [[LEGSTATE4:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP2:%.*]] = load <4 x i32>, ptr [[LEGSTATE4]], align 16
+// CHECK-NEXT:    [[VECEXT5:%.*]] = extractelement <4 x i32> [[TMP2]], i64 2
+// CHECK-NEXT:    store i32 [[VECEXT5]], ptr [[ARRAYINIT_ELEMENT3]], align 4
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT6:%.*]] = getelementptr inbounds i32, ptr [[LEGS]], i32 3
+// CHECK-NEXT:    [[LEGSTATE7:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP3:%.*]] = load <4 x i32>, ptr [[LEGSTATE7]], align 16
+// CHECK-NEXT:    [[VECEXT8:%.*]] = extractelement <4 x i32> [[TMP3]], i64 3
+// CHECK-NEXT:    store i32 [[VECEXT8]], ptr [[ARRAYINIT_ELEMENT6]], align 4
+// CHECK-NEXT:    [[STATE:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[TAILSTATE:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TAILSTATE]], align 16
+// CHECK-NEXT:    store i32 [[TMP4]], ptr [[STATE]], align 16
+// CHECK-NEXT:    [[COUNTER:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[AGG_RESULT]], i32 0, i32 2
+// CHECK-NEXT:    [[HAIRCOUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP5:%.*]] = load float, ptr [[HAIRCOUNT]], align 4
+// CHECK-NEXT:    [[CONV:%.*]] = fptosi float [[TMP5]] to i64
+// CHECK-NEXT:    store i64 [[CONV]], ptr [[COUNTER]], align 8
+// CHECK-NEXT:    [[LEFTDIR:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[AGG_RESULT]], i32 0, i32 3
+// CHECK-NEXT:    [[EARDIRECTION:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP6:%.*]] = load <4 x float>, ptr [[ARRAYIDX]], align 16
+// CHECK-NEXT:    [[VECEXT9:%.*]] = extractelement <4 x float> [[TMP6]], i64 0
+// CHECK-NEXT:    [[VECINIT:%.*]] = insertelement <4 x float> poison, float [[VECEXT9]], i32 0
+// CHECK-NEXT:    [[EARDIRECTION10:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX11:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION10]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP7:%.*]] = load <4 x float>, ptr [[ARRAYIDX11]], align 16
+// CHECK-NEXT:    [[VECEXT12:%.*]] = extractelement <4 x float> [[TMP7]], i64 1
+// CHECK-NEXT:    [[VECINIT13:%.*]] = insertelement <4 x float> [[VECINIT]], float [[VECEXT12]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION14:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX15:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION14]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP8:%.*]] = load <4 x float>, ptr [[ARRAYIDX15]], align 16
+// CHECK-NEXT:    [[VECEXT16:%.*]] = extractelement <4 x float> [[TMP8]], i64 2
+// CHECK-NEXT:    [[VECINIT17:%.*]] = insertelement <4 x float> [[VECINIT13]], float [[VECEXT16]], i32 2
+// CHECK-NEXT:    [[EARDIRECTION18:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX19:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION18]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP9:%.*]] = load <4 x float>, ptr [[ARRAYIDX19]], align 16
+// CHECK-NEXT:    [[VECEXT20:%.*]] = extractelement <4 x float> [[TMP9]], i64 3
+// CHECK-NEXT:    [[VECINIT21:%.*]] = insertelement <4 x float> [[VECINIT17]], float [[VECEXT20]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT21]], ptr [[LEFTDIR]], align 16
+// CHECK-NEXT:    [[RIGHTDIR:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[AGG_RESULT]], i32 0, i32 4
+// CHECK-NEXT:    [[EARDIRECTION22:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX23:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION22]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP10:%.*]] = load <4 x float>, ptr [[ARRAYIDX23]], align 16
+// CHECK-NEXT:    [[VECEXT24:%.*]] = extractelement <4 x float> [[TMP10]], i64 0
+// CHECK-NEXT:    [[VECINIT25:%.*]] = insertelement <4 x float> poison, float [[VECEXT24]], i32 0
+// CHECK-NEXT:    [[EARDIRECTION26:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX27:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION26]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP11:%.*]] = load <4 x float>, ptr [[ARRAYIDX27]], align 16
+// CHECK-NEXT:    [[VECEXT28:%.*]] = extractelement <4 x float> [[TMP11]], i64 1
+// CHECK-NEXT:    [[VECINIT29:%.*]] = insertelement <4 x float> [[VECINIT25]], float [[VECEXT28]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION30:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX31:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION30]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP12:%.*]] = load <4 x float>, ptr [[ARRAYIDX31]], align 16
+// CHECK-NEXT:    [[VECEXT32:%.*]] = extractelement <4 x float> [[TMP12]], i64 2
+// CHECK-NEXT:    [[VECINIT33:%.*]] = insertelement <4 x float> [[VECINIT29]], float [[VECEXT32]], i32 2
+// CHECK-NEXT:    [[EARDIRECTION34:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX35:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION34]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP13:%.*]] = load <4 x float>, ptr [[ARRAYIDX35]], align 16
+// CHECK-NEXT:    [[VECEXT36:%.*]] = extractelement <4 x float> [[TMP13]], i64 3
+// CHECK-NEXT:    [[VECINIT37:%.*]] = insertelement <4 x float> [[VECINIT33]], float [[VECEXT36]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT37]], ptr [[RIGHTDIR]], align 16
+// CHECK-NEXT:    ret void
+//
+AnimalBits case8(Doggo D1) {
+  AnimalBits A1 = {D1};
+  return A1;
+}
+
+// Case 9: Everything everywhere all at once... Initializing mismatched
+// structures from different layouts, different component groupings, with no
+// top-level bracing separation.
+// CHECK-LABEL: define void @_Z5case95Doggo10AnimalBits(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_ZOO:%.*]]) align 16 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_DOGGO:%.*]]) align 16 [[D1:%.*]], ptr noundef byval([[STRUCT_ANIMALBITS:%.*]]) align 16 [[A1:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[DOGS:%.*]] = getelementptr inbounds nuw [[STRUCT_ZOO]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[LEGSTATE:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[DOGS]], i32 0, i32 0
+// CHECK-NEXT:    [[LEGSTATE1:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr [[LEGSTATE1]], align 16
+// CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <4 x i32> [[TMP0]], i64 0
+// CHECK-NEXT:    [[VECINIT:%.*]] = insertelement <4 x i32> poison, i32 [[VECEXT]], i32 0
+// CHECK-NEXT:    [[LEGSTATE2:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP1:%.*]] = load <4 x i32>, ptr [[LEGSTATE2]], align 16
+// CHECK-NEXT:    [[VECEXT3:%.*]] = extractelement <4 x i32> [[TMP1]], i64 1
+// CHECK-NEXT:    [[VECINIT4:%.*]] = insertelement <4 x i32> [[VECINIT]], i32 [[VECEXT3]], i32 1
+// CHECK-NEXT:    [[LEGSTATE5:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP2:%.*]] = load <4 x i32>, ptr [[LEGSTATE5]], align 16
+// CHECK-NEXT:    [[VECEXT6:%.*]] = extractelement <4 x i32> [[TMP2]], i64 2
+// CHECK-NEXT:    [[VECINIT7:%.*]] = insertelement <4 x i32> [[VECINIT4]], i32 [[VECEXT6]], i32 2
+// CHECK-NEXT:    [[LEGSTATE8:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP3:%.*]] = load <4 x i32>, ptr [[LEGSTATE8]], align 16
+// CHECK-NEXT:    [[VECEXT9:%.*]] = extractelement <4 x i32> [[TMP3]], i64 3
+// CHECK-NEXT:    [[VECINIT10:%.*]] = insertelement <4 x i32> [[VECINIT7]], i32 [[VECEXT9]], i32 3
+// CHECK-NEXT:    store <4 x i32> [[VECINIT10]], ptr [[LEGSTATE]], align 16
+// CHECK-NEXT:    [[TAILSTATE:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[DOGS]], i32 0, i32 1
+// CHECK-NEXT:    [[TAILSTATE11:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr [[TAILSTATE11]], align 16
+// CHECK-NEXT:    store i32 [[TMP4]], ptr [[TAILSTATE]], align 16
+// CHECK-NEXT:    [[HAIRCOUNT:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[DOGS]], i32 0, i32 2
+// CHECK-NEXT:    [[HAIRCOUNT12:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP5:%.*]] = load float, ptr [[HAIRCOUNT12]], align 4
+// CHECK-NEXT:    store float [[TMP5]], ptr [[HAIRCOUNT]], align 4
+// CHECK-NEXT:    [[EARDIRECTION:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[DOGS]], i32 0, i32 3
+// CHECK-NEXT:    [[EARDIRECTION13:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION13]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP6:%.*]] = load <4 x float>, ptr [[ARRAYIDX]], align 16
+// CHECK-NEXT:    [[VECEXT14:%.*]] = extractelement <4 x float> [[TMP6]], i64 0
+// CHECK-NEXT:    [[VECINIT15:%.*]] = insertelement <4 x float> poison, float [[VECEXT14]], i32 0
+// CHECK-NEXT:    [[EARDIRECTION16:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX17:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION16]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP7:%.*]] = load <4 x float>, ptr [[ARRAYIDX17]], align 16
+// CHECK-NEXT:    [[VECEXT18:%.*]] = extractelement <4 x float> [[TMP7]], i64 1
+// CHECK-NEXT:    [[VECINIT19:%.*]] = insertelement <4 x float> [[VECINIT15]], float [[VECEXT18]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION20:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX21:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION20]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP8:%.*]] = load <4 x float>, ptr [[ARRAYIDX21]], align 16
+// CHECK-NEXT:    [[VECEXT22:%.*]] = extractelement <4 x float> [[TMP8]], i64 2
+// CHECK-NEXT:    [[VECINIT23:%.*]] = insertelement <4 x float> [[VECINIT19]], float [[VECEXT22]], i32 2
+// CHECK-NEXT:    [[EARDIRECTION24:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX25:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION24]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP9:%.*]] = load <4 x float>, ptr [[ARRAYIDX25]], align 16
+// CHECK-NEXT:    [[VECEXT26:%.*]] = extractelement <4 x float> [[TMP9]], i64 3
+// CHECK-NEXT:    [[VECINIT27:%.*]] = insertelement <4 x float> [[VECINIT23]], float [[VECEXT26]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT27]], ptr [[EARDIRECTION]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT:%.*]] = getelementptr inbounds <4 x float>, ptr [[EARDIRECTION]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION28:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX29:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION28]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP10:%.*]] = load <4 x float>, ptr [[ARRAYIDX29]], align 16
+// CHECK-NEXT:    [[VECEXT30:%.*]] = extractelement <4 x float> [[TMP10]], i64 0
+// CHECK-NEXT:    [[VECINIT31:%.*]] = insertelement <4 x float> poison, float [[VECEXT30]], i32 0
+// CHECK-NEXT:    [[EARDIRECTION32:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX33:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION32]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP11:%.*]] = load <4 x float>, ptr [[ARRAYIDX33]], align 16
+// CHECK-NEXT:    [[VECEXT34:%.*]] = extractelement <4 x float> [[TMP11]], i64 1
+// CHECK-NEXT:    [[VECINIT35:%.*]] = insertelement <4 x float> [[VECINIT31]], float [[VECEXT34]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION36:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX37:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION36]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP12:%.*]] = load <4 x float>, ptr [[ARRAYIDX37]], align 16
+// CHECK-NEXT:    [[VECEXT38:%.*]] = extractelement <4 x float> [[TMP12]], i64 2
+// CHECK-NEXT:    [[VECINIT39:%.*]] = insertelement <4 x float> [[VECINIT35]], float [[VECEXT38]], i32 2
+// CHECK-NEXT:    [[EARDIRECTION40:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX41:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION40]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP13:%.*]] = load <4 x float>, ptr [[ARRAYIDX41]], align 16
+// CHECK-NEXT:    [[VECEXT42:%.*]] = extractelement <4 x float> [[TMP13]], i64 3
+// CHECK-NEXT:    [[VECINIT43:%.*]] = insertelement <4 x float> [[VECINIT39]], float [[VECEXT42]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT43]], ptr [[ARRAYINIT_ELEMENT]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT44:%.*]] = getelementptr inbounds [[STRUCT_DOGGO]], ptr [[DOGS]], i32 1
+// CHECK-NEXT:    [[LEGSTATE45:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[ARRAYINIT_ELEMENT44]], i32 0, i32 0
+// CHECK-NEXT:    [[LEGS:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX46:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP14:%.*]] = load i32, ptr [[ARRAYIDX46]], align 16
+// CHECK-NEXT:    [[VECINIT47:%.*]] = insertelement <4 x i32> poison, i32 [[TMP14]], i32 0
+// CHECK-NEXT:    [[LEGS48:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX49:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS48]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP15:%.*]] = load i32, ptr [[ARRAYIDX49]], align 4
+// CHECK-NEXT:    [[VECINIT50:%.*]] = insertelement <4 x i32> [[VECINIT47]], i32 [[TMP15]], i32 1
+// CHECK-NEXT:    [[LEGS51:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX52:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS51]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP16:%.*]] = load i32, ptr [[ARRAYIDX52]], align 8
+// CHECK-NEXT:    [[VECINIT53:%.*]] = insertelement <4 x i32> [[VECINIT50]], i32 [[TMP16]], i32 2
+// CHECK-NEXT:    [[LEGS54:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX55:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS54]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP17:%.*]] = load i32, ptr [[ARRAYIDX55]], align 4
+// CHECK-NEXT:    [[VECINIT56:%.*]] = insertelement <4 x i32> [[VECINIT53]], i32 [[TMP17]], i32 3
+// CHECK-NEXT:    store <4 x i32> [[VECINIT56]], ptr [[LEGSTATE45]], align 16
+// CHECK-NEXT:    [[TAILSTATE57:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[ARRAYINIT_ELEMENT44]], i32 0, i32 1
+// CHECK-NEXT:    [[STATE:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP18:%.*]] = load i32, ptr [[STATE]], align 16
+// CHECK-NEXT:    store i32 [[TMP18]], ptr [[TAILSTATE57]], align 16
+// CHECK-NEXT:    [[HAIRCOUNT58:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[ARRAYINIT_ELEMENT44]], i32 0, i32 2
+// CHECK-NEXT:    [[COUNTER:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP19:%.*]] = load i64, ptr [[COUNTER]], align 8
+// CHECK-NEXT:    [[CONV:%.*]] = sitofp i64 [[TMP19]] to float
+// CHECK-NEXT:    store float [[CONV]], ptr [[HAIRCOUNT58]], align 4
+// CHECK-NEXT:    [[EARDIRECTION59:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[ARRAYINIT_ELEMENT44]], i32 0, i32 3
+// CHECK-NEXT:    [[LEFTDIR:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP20:%.*]] = load <4 x float>, ptr [[LEFTDIR]], align 16
+// CHECK-NEXT:    [[VECEXT60:%.*]] = extractelement <4 x float> [[TMP20]], i64 0
+// CHECK-NEXT:    [[VECINIT61:%.*]] = insertelement <4 x float> poison, float [[VECEXT60]], i32 0
+// CHECK-NEXT:    [[LEFTDIR62:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP21:%.*]] = load <4 x float>, ptr [[LEFTDIR62]], align 16
+// CHECK-NEXT:    [[VECEXT63:%.*]] = extractelement <4 x float> [[TMP21]], i64 1
+// CHECK-NEXT:    [[VECINIT64:%.*]] = insertelement <4 x float> [[VECINIT61]], float [[VECEXT63]], i32 1
+// CHECK-NEXT:    [[LEFTDIR65:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP22:%.*]] = load <4 x float>, ptr [[LEFTDIR65]], align 16
+// CHECK-NEXT:    [[VECEXT66:%.*]] = extractelement <4 x float> [[TMP22]], i64 2
+// CHECK-NEXT:    [[VECINIT67:%.*]] = insertelement <4 x float> [[VECINIT64]], float [[VECEXT66]], i32 2
+// CHECK-NEXT:    [[LEFTDIR68:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP23:%.*]] = load <4 x float>, ptr [[LEFTDIR68]], align 16
+// CHECK-NEXT:    [[VECEXT69:%.*]] = extractelement <4 x float> [[TMP23]], i64 3
+// CHECK-NEXT:    [[VECINIT70:%.*]] = insertelement <4 x float> [[VECINIT67]], float [[VECEXT69]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT70]], ptr [[EARDIRECTION59]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT71:%.*]] = getelementptr inbounds <4 x float>, ptr [[EARDIRECTION59]], i32 1
+// CHECK-NEXT:    [[RIGHTDIR:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP24:%.*]] = load <4 x float>, ptr [[RIGHTDIR]], align 16
+// CHECK-NEXT:    [[VECEXT72:%.*]] = extractelement <4 x float> [[TMP24]], i64 0
+// CHECK-NEXT:    [[VECINIT73:%.*]] = insertelement <4 x float> poison, float [[VECEXT72]], i32 0
+// CHECK-NEXT:    [[RIGHTDIR74:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP25:%.*]] = load <4 x float>, ptr [[RIGHTDIR74]], align 16
+// CHECK-NEXT:    [[VECEXT75:%.*]] = extractelement <4 x float> [[TMP25]], i64 1
+// CHECK-NEXT:    [[VECINIT76:%.*]] = insertelement <4 x float> [[VECINIT73]], float [[VECEXT75]], i32 1
+// CHECK-NEXT:    [[RIGHTDIR77:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP26:%.*]] = load <4 x float>, ptr [[RIGHTDIR77]], align 16
+// CHECK-NEXT:    [[VECEXT78:%.*]] = extractelement <4 x float> [[TMP26]], i64 2
+// CHECK-NEXT:    [[VECINIT79:%.*]] = insertelement <4 x float> [[VECINIT76]], float [[VECEXT78]], i32 2
+// CHECK-NEXT:    [[RIGHTDIR80:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP27:%.*]] = load <4 x float>, ptr [[RIGHTDIR80]], align 16
+// CHECK-NEXT:    [[VECEXT81:%.*]] = extractelement <4 x float> [[TMP27]], i64 3
+// CHECK-NEXT:    [[VECINIT82:%.*]] = insertelement <4 x float> [[VECINIT79]], float [[VECEXT81]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT82]], ptr [[ARRAYINIT_ELEMENT71]], align 16
+// CHECK-NEXT:    [[CATS:%.*]] = getelementptr inbounds nuw [[STRUCT_ZOO]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[LEGS83:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH:%.*]], ptr [[CATS]], i32 0, i32 0
+// CHECK-NEXT:    [[LEGSTATE84:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP28:%.*]] = load <4 x i32>, ptr [[LEGSTATE84]], align 16
+// CHECK-NEXT:    [[VECEXT85:%.*]] = extractelement <4 x i32> [[TMP28]], i64 0
+// CHECK-NEXT:    [[VECINIT86:%.*]] = insertelement <4 x i32> poison, i32 [[VECEXT85]], i32 0
+// CHECK-NEXT:    [[LEGSTATE87:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP29:%.*]] = load <4 x i32>, ptr [[LEGSTATE87]], align 16
+// CHECK-NEXT:    [[VECEXT88:%.*]] = extractelement <4 x i32> [[TMP29]], i64 1
+// CHECK-NEXT:    [[VECINIT89:%.*]] = insertelement <4 x i32> [[VECINIT86]], i32 [[VECEXT88]], i32 1
+// CHECK-NEXT:    [[LEGSTATE90:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP30:%.*]] = load <4 x i32>, ptr [[LEGSTATE90]], align 16
+// CHECK-NEXT:    [[VECEXT91:%.*]] = extractelement <4 x i32> [[TMP30]], i64 2
+// CHECK-NEXT:    [[VECINIT92:%.*]] = insertelement <4 x i32> [[VECINIT89]], i32 [[VECEXT91]], i32 2
+// CHECK-NEXT:    [[LEGSTATE93:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP31:%.*]] = load <4 x i32>, ptr [[LEGSTATE93]], align 16
+// CHECK-NEXT:    [[VECEXT94:%.*]] = extractelement <4 x i32> [[TMP31]], i64 3
+// CHECK-NEXT:    [[VECINIT95:%.*]] = insertelement <4 x i32> [[VECINIT92]], i32 [[VECEXT94]], i32 3
+// CHECK-NEXT:    store <4 x i32> [[VECINIT95]], ptr [[LEGS83]], align 16
+// CHECK-NEXT:    [[TAILSTATE96:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[CATS]], i32 0, i32 1
+// CHECK-NEXT:    [[TAILSTATE97:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP32:%.*]] = load i32, ptr [[TAILSTATE97]], align 16
+// CHECK-NEXT:    store i32 [[TMP32]], ptr [[TAILSTATE96]], align 16
+// CHECK-NEXT:    [[HAIRCOUNT98:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[CATS]], i32 0, i32 2
+// CHECK-NEXT:    [[HAIRCOUNT99:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP33:%.*]] = load float, ptr [[HAIRCOUNT99]], align 4
+// CHECK-NEXT:    store float [[TMP33]], ptr [[HAIRCOUNT98]], align 4
+// CHECK-NEXT:    [[CLAWS:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[CATS]], i32 0, i32 3
+// CHECK-NEXT:    [[EARDIRECTION100:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX101:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION100]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP34:%.*]] = load <4 x float>, ptr [[ARRAYIDX101]], align 16
+// CHECK-NEXT:    [[VECEXT102:%.*]] = extractelement <4 x float> [[TMP34]], i64 0
+// CHECK-NEXT:    [[VECINIT103:%.*]] = insertelement <4 x float> poison, float [[VECEXT102]], i32 0
+// CHECK-NEXT:    [[EARDIRECTION104:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX105:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION104]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP35:%.*]] = load <4 x float>, ptr [[ARRAYIDX105]], align 16
+// CHECK-NEXT:    [[VECEXT106:%.*]] = extractelement <4 x float> [[TMP35]], i64 1
+// CHECK-NEXT:    [[VECINIT107:%.*]] = insertelement <4 x float> [[VECINIT103]], float [[VECEXT106]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION108:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX109:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION108]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP36:%.*]] = load <4 x float>, ptr [[ARRAYIDX109]], align 16
+// CHECK-NEXT:    [[VECEXT110:%.*]] = extractelement <4 x float> [[TMP36]], i64 2
+// CHECK-NEXT:    [[VECINIT111:%.*]] = insertelement <4 x float> [[VECINIT107]], float [[VECEXT110]], i32 2
+// CHECK-NEXT:    [[EARDIRECTION112:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX113:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION112]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP37:%.*]] = load <4 x float>, ptr [[ARRAYIDX113]], align 16
+// CHECK-NEXT:    [[VECEXT114:%.*]] = extractelement <4 x float> [[TMP37]], i64 3
+// CHECK-NEXT:    [[VECINIT115:%.*]] = insertelement <4 x float> [[VECINIT111]], float [[VECEXT114]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT115]], ptr [[CLAWS]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT116:%.*]] = getelementptr inbounds <4 x float>, ptr [[CLAWS]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION117:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX118:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION117]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP38:%.*]] = load <4 x float>, ptr [[ARRAYIDX118]], align 16
+// CHECK-NEXT:    [[VECEXT119:%.*]] = extractelement <4 x float> [[TMP38]], i64 0
+// CHECK-NEXT:    [[VECINIT120:%.*]] = insertelement <4 x float> poison, float [[VECEXT119]], i32 0
+// CHECK-NEXT:    [[EARDIRECTION121:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX122:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION121]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP39:%.*]] = load <4 x float>, ptr [[ARRAYIDX122]], align 16
+// CHECK-NEXT:    [[VECEXT123:%.*]] = extractelement <4 x float> [[TMP39]], i64 1
+// CHECK-NEXT:    [[VECINIT124:%.*]] = insertelement <4 x float> [[VECINIT120]], float [[VECEXT123]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION125:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX126:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION125]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP40:%.*]] = load <4 x float>, ptr [[ARRAYIDX126]], align 16
+// CHECK-NEXT:    [[VECEXT127:%.*]] = extractelement <4 x float> [[TMP40]], i64 2
+// CHECK-NEXT:    [[VECINIT128:%.*]] = insertelement <4 x float> [[VECINIT124]], float [[VECEXT127]], i32 2
+// CHECK-NEXT:    [[EARDIRECTION129:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX130:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION129]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP41:%.*]] = load <4 x float>, ptr [[ARRAYIDX130]], align 16
+// CHECK-NEXT:    [[VECEXT131:%.*]] = extractelement <4 x float> [[TMP41]], i64 3
+// CHECK-NEXT:    [[VECINIT132:%.*]] = insertelement <4 x float> [[VECINIT128]], float [[VECEXT131]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT132]], ptr [[ARRAYINIT_ELEMENT116]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT133:%.*]] = getelementptr inbounds [[STRUCT_KITTEH]], ptr [[CATS]], i32 1
+// CHECK-NEXT:    [[LEGS134:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT133]], i32 0, i32 0
+// CHECK-NEXT:    [[LEGS135:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX136:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS135]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP42:%.*]] = load i32, ptr [[ARRAYIDX136]], align 16
+// CHECK-NEXT:    [[VECINIT137:%.*]] = insertelement <4 x i32> poison, i32 [[TMP42]], i32 0
+// CHECK-NEXT:    [[LEGS138:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX139:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS138]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP43:%.*]] = load i32, ptr [[ARRAYIDX139]], align 4
+// CHECK-NEXT:    [[VECINIT140:%.*]] = insertelement <4 x i32> [[VECINIT137]], i32 [[TMP43]], i32 1
+// CHECK-NEXT:    [[LEGS141:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX142:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS141]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP44:%.*]] = load i32, ptr [[ARRAYIDX142]], align 8
+// CHECK-NEXT:    [[VECINIT143:%.*]] = insertelement <4 x i32> [[VECINIT140]], i32 [[TMP44]], i32 2
+// CHECK-NEXT:    [[LEGS144:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX145:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS144]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP45:%.*]] = load i32, ptr [[ARRAYIDX145]], align 4
+// CHECK-NEXT:    [[VECINIT146:%.*]] = insertelement <4 x i32> [[VECINIT143]], i32 [[TMP45]], i32 3
+// CHECK-NEXT:    store <4 x i32> [[VECINIT146]], ptr [[LEGS134]], align 16
+// CHECK-NEXT:    [[TAILSTATE147:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT133]], i32 0, i32 1
+// CHECK-NEXT:    [[STATE148:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP46:%.*]] = load i32, ptr [[STATE148]], align 16
+// CHECK-NEXT:    store i32 [[TMP46]], ptr [[TAILSTATE147]], align 16
+// CHECK-NEXT:    [[HAIRCOUNT149:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT133]], i32 0, i32 2
+// CHECK-NEXT:    [[COUNTER150:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP47:%.*]] = load i64, ptr [[COUNTER150]], align 8
+// CHECK-NEXT:    [[CONV151:%.*]] = sitofp i64 [[TMP47]] to float
+// CHECK-NEXT:    store float [[CONV151]], ptr [[HAIRCOUNT149]], align 4
+// CHECK-NEXT:    [[CLAWS152:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT133]], i32 0, i32 3
+// CHECK-NEXT:    [[LEFTDIR153:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP48:%.*]] = load <4 x float>, ptr [[LEFTDIR153]], align 16
+// CHECK-NEXT:    [[VECEXT154:%.*]] = extractelement <4 x float> [[TMP48]], i64 0
+// CHECK-NEXT:    [[VECINIT155:%.*]] = insertelement <4 x float> poison, float [[VECEXT154]], i32 0
+// CHECK-NEXT:    [[LEFTDIR156:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP49:%.*]] = load <4 x float>, ptr [[LEFTDIR156]], align 16
+// CHECK-NEXT:    [[VECEXT157:%.*]] = extractelement <4 x float> [[TMP49]], i64 1
+// CHECK-NEXT:    [[VECINIT158:%.*]] = insertelement <4 x float> [[VECINIT155]], float [[VECEXT157]], i32 1
+// CHECK-NEXT:    [[LEFTDIR159:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP50:%.*]] = load <4 x float>, ptr [[LEFTDIR159]], align 16
+// CHECK-NEXT:    [[VECEXT160:%.*]] = extractelement <4 x float> [[TMP50]], i64 2
+// CHECK-NEXT:    [[VECINIT161:%.*]] = insertelement <4 x float> [[VECINIT158]], float [[VECEXT160]], i32 2
+// CHECK-NEXT:    [[LEFTDIR162:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP51:%.*]] = load <4 x float>, ptr [[LEFTDIR162]], align 16
+// CHECK-NEXT:    [[VECEXT163:%.*]] = extractelement <4 x float> [[TMP51]], i64 3
+// CHECK-NEXT:    [[VECINIT164:%.*]] = insertelement <4 x float> [[VECINIT161]], float [[VECEXT163]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT164]], ptr [[CLAWS152]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT165:%.*]] = getelementptr inbounds <4 x float>, ptr [[CLAWS152]], i32 1
+// CHECK-NEXT:    [[RIGHTDIR166:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP52:%.*]] = load <4 x float>, ptr [[RIGHTDIR166]], align 16
+// CHECK-NEXT:    [[VECEXT167:%.*]] = extractelement <4 x float> [[TMP52]], i64 0
+// CHECK-NEXT:    [[VECINIT168:%.*]] = insertelement <4 x float> poison, float [[VECEXT167]], i32 0
+// CHECK-NEXT:    [[RIGHTDIR169:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP53:%.*]] = load <4 x float>, ptr [[RIGHTDIR169]], align 16
+// CHECK-NEXT:    [[VECEXT170:%.*]] = extractelement <4 x float> [[TMP53]], i64 1
+// CHECK-NEXT:    [[VECINIT171:%.*]] = insertelement <4 x float> [[VECINIT168]], float [[VECEXT170]], i32 1
+// CHECK-NEXT:    [[RIGHTDIR172:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP54:%.*]] = load <4 x float>, ptr [[RIGHTDIR172]], align 16
+// CHECK-NEXT:    [[VECEXT173:%.*]] = extractelement <4 x float> [[TMP54]], i64 2
+// CHECK-NEXT:    [[VECINIT174:%.*]] = insertelement <4 x float> [[VECINIT171]], float [[VECEXT173]], i32 2
+// CHECK-NEXT:    [[RIGHTDIR175:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP55:%.*]] = load <4 x float>, ptr [[RIGHTDIR175]], align 16
+// CHECK-NEXT:    [[VECEXT176:%.*]] = extractelement <4 x float> [[TMP55]], i64 3
+// CHECK-NEXT:    [[VECINIT177:%.*]] = insertelement <4 x float> [[VECINIT174]], float [[VECEXT176]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT177]], ptr [[ARRAYINIT_ELEMENT165]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT178:%.*]] = getelementptr inbounds [[STRUCT_KITTEH]], ptr [[CATS]], i32 2
+// CHECK-NEXT:    [[LEGS179:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT178]], i32 0, i32 0
+// CHECK-NEXT:    [[LEGSTATE180:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP56:%.*]] = load <4 x i32>, ptr [[LEGSTATE180]], align 16
+// CHECK-NEXT:    [[VECEXT181:%.*]] = extractelement <4 x i32> [[TMP56]], i64 0
+// CHECK-NEXT:    [[VECINIT182:%.*]] = insertelement <4 x i32> poison, i32 [[VECEXT181]], i32 0
+// CHECK-NEXT:    [[LEGSTATE183:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP57:%.*]] = load <4 x i32>, ptr [[LEGSTATE183]], align 16
+// CHECK-NEXT:    [[VECEXT184:%.*]] = extractelement <4 x i32> [[TMP57]], i64 1
+// CHECK-NEXT:    [[VECINIT185:%.*]] = insertelement <4 x i32> [[VECINIT182]], i32 [[VECEXT184]], i32 1
+// CHECK-NEXT:    [[LEGSTATE186:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP58:%.*]] = load <4 x i32>, ptr [[LEGSTATE186]], align 16
+// CHECK-NEXT:    [[VECEXT187:%.*]] = extractelement <4 x i32> [[TMP58]], i64 2
+// CHECK-NEXT:    [[VECINIT188:%.*]] = insertelement <4 x i32> [[VECINIT185]], i32 [[VECEXT187]], i32 2
+// CHECK-NEXT:    [[LEGSTATE189:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP59:%.*]] = load <4 x i32>, ptr [[LEGSTATE189]], align 16
+// CHECK-NEXT:    [[VECEXT190:%.*]] = extractelement <4 x i32> [[TMP59]], i64 3
+// CHECK-NEXT:    [[VECINIT191:%.*]] = insertelement <4 x i32> [[VECINIT188]], i32 [[VECEXT190]], i32 3
+// CHECK-NEXT:    store <4 x i32> [[VECINIT191]], ptr [[LEGS179]], align 16
+// CHECK-NEXT:    [[TAILSTATE192:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT178]], i32 0, i32 1
+// CHECK-NEXT:    [[TAILSTATE193:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP60:%.*]] = load i32, ptr [[TAILSTATE193]], align 16
+// CHECK-NEXT:    store i32 [[TMP60]], ptr [[TAILSTATE192]], align 16
+// CHECK-NEXT:    [[HAIRCOUNT194:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT178]], i32 0, i32 2
+// CHECK-NEXT:    [[HAIRCOUNT195:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP61:%.*]] = load float, ptr [[HAIRCOUNT195]], align 4
+// CHECK-NEXT:    store float [[TMP61]], ptr [[HAIRCOUNT194]], align 4
+// CHECK-NEXT:    [[CLAWS196:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT178]], i32 0, i32 3
+// CHECK-NEXT:    [[EARDIRECTION197:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX198:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION197]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP62:%.*]] = load <4 x float>, ptr [[ARRAYIDX198]], align 16
+// CHECK-NEXT:    [[VECEXT199:%.*]] = extractelement <4 x float> [[TMP62]], i64 0
+// CHECK-NEXT:    [[VECINIT200:%.*]] = insertelement <4 x float> poison, float [[VECEXT199]], i32 0
+// CHECK-NEXT:    [[EARDIRECTION201:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX202:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION201]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP63:%.*]] = load <4 x float>, ptr [[ARRAYIDX202]], align 16
+// CHECK-NEXT:    [[VECEXT203:%.*]] = extractelement <4 x float> [[TMP63]], i64 1
+// CHECK-NEXT:    [[VECINIT204:%.*]] = insertelement <4 x float> [[VECINIT200]], float [[VECEXT203]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION205:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX206:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION205]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP64:%.*]] = load <4 x float>, ptr [[ARRAYIDX206]], align 16
+// CHECK-NEXT:    [[VECEXT207:%.*]] = extractelement <4 x float> [[TMP64]], i64 2
+// CHECK-NEXT:    [[VECINIT208:%.*]] = insertelement <4 x float> [[VECINIT204]], float [[VECEXT207]], i32 2
+// CHECK-NEXT:    [[EARDIRECTION209:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX210:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION209]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP65:%.*]] = load <4 x float>, ptr [[ARRAYIDX210]], align 16
+// CHECK-NEXT:    [[VECEXT211:%.*]] = extractelement <4 x float> [[TMP65]], i64 3
+// CHECK-NEXT:    [[VECINIT212:%.*]] = insertelement <4 x float> [[VECINIT208]], float [[VECEXT211]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT212]], ptr [[CLAWS196]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT213:%.*]] = getelementptr inbounds <4 x float>, ptr [[CLAWS196]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION214:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX215:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION214]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP66:%.*]] = load <4 x float>, ptr [[ARRAYIDX215]], align 16
+// CHECK-NEXT:    [[VECEXT216:%.*]] = extractelement <4 x float> [[TMP66]], i64 0
+// CHECK-NEXT:    [[VECINIT217:%.*]] = insertelement <4 x float> poison, float [[VECEXT216]], i32 0
+// CHECK-NEXT:    [[EARDIRECTION218:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX219:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION218]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP67:%.*]] = load <4 x float>, ptr [[ARRAYIDX219]], align 16
+// CHECK-NEXT:    [[VECEXT220:%.*]] = extractelement <4 x float> [[TMP67]], i64 1
+// CHECK-NEXT:    [[VECINIT221:%.*]] = insertelement <4 x float> [[VECINIT217]], float [[VECEXT220]], i32 1
+// CHECK-NEXT:    [[EARDIRECTION222:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX223:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION222]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP68:%.*]] = load <4 x float>, ptr [[ARRAYIDX223]], align 16
+// CHECK-NEXT:    [[VECEXT224:%.*]] = extractelement <4 x float> [[TMP68]], i64 2
+// CHECK-NEXT:    [[VECINIT225:%.*]] = insertelement <4 x float> [[VECINIT221]], float [[VECEXT224]], i32 2
+// CHECK-NEXT:    [[EARDIRECTION226:%.*]] = getelementptr inbounds nuw [[STRUCT_DOGGO]], ptr [[D1]], i32 0, i32 3
+// CHECK-NEXT:    [[ARRAYIDX227:%.*]] = getelementptr inbounds nuw [2 x <4 x float>], ptr [[EARDIRECTION226]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP69:%.*]] = load <4 x float>, ptr [[ARRAYIDX227]], align 16
+// CHECK-NEXT:    [[VECEXT228:%.*]] = extractelement <4 x float> [[TMP69]], i64 3
+// CHECK-NEXT:    [[VECINIT229:%.*]] = insertelement <4 x float> [[VECINIT225]], float [[VECEXT228]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT229]], ptr [[ARRAYINIT_ELEMENT213]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT230:%.*]] = getelementptr inbounds [[STRUCT_KITTEH]], ptr [[CATS]], i32 3
+// CHECK-NEXT:    [[LEGS231:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT230]], i32 0, i32 0
+// CHECK-NEXT:    [[LEGS232:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX233:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS232]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP70:%.*]] = load i32, ptr [[ARRAYIDX233]], align 16
+// CHECK-NEXT:    [[VECINIT234:%.*]] = insertelement <4 x i32> poison, i32 [[TMP70]], i32 0
+// CHECK-NEXT:    [[LEGS235:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX236:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS235]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP71:%.*]] = load i32, ptr [[ARRAYIDX236]], align 4
+// CHECK-NEXT:    [[VECINIT237:%.*]] = insertelement <4 x i32> [[VECINIT234]], i32 [[TMP71]], i32 1
+// CHECK-NEXT:    [[LEGS238:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX239:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS238]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP72:%.*]] = load i32, ptr [[ARRAYIDX239]], align 8
+// CHECK-NEXT:    [[VECINIT240:%.*]] = insertelement <4 x i32> [[VECINIT237]], i32 [[TMP72]], i32 2
+// CHECK-NEXT:    [[LEGS241:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 0
+// CHECK-NEXT:    [[ARRAYIDX242:%.*]] = getelementptr inbounds nuw [4 x i32], ptr [[LEGS241]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP73:%.*]] = load i32, ptr [[ARRAYIDX242]], align 4
+// CHECK-NEXT:    [[VECINIT243:%.*]] = insertelement <4 x i32> [[VECINIT240]], i32 [[TMP73]], i32 3
+// CHECK-NEXT:    store <4 x i32> [[VECINIT243]], ptr [[LEGS231]], align 16
+// CHECK-NEXT:    [[TAILSTATE244:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT230]], i32 0, i32 1
+// CHECK-NEXT:    [[STATE245:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP74:%.*]] = load i32, ptr [[STATE245]], align 16
+// CHECK-NEXT:    store i32 [[TMP74]], ptr [[TAILSTATE244]], align 16
+// CHECK-NEXT:    [[HAIRCOUNT246:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT230]], i32 0, i32 2
+// CHECK-NEXT:    [[COUNTER247:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP75:%.*]] = load i64, ptr [[COUNTER247]], align 8
+// CHECK-NEXT:    [[CONV248:%.*]] = sitofp i64 [[TMP75]] to float
+// CHECK-NEXT:    store float [[CONV248]], ptr [[HAIRCOUNT246]], align 4
+// CHECK-NEXT:    [[CLAWS249:%.*]] = getelementptr inbounds nuw [[STRUCT_KITTEH]], ptr [[ARRAYINIT_ELEMENT230]], i32 0, i32 3
+// CHECK-NEXT:    [[LEFTDIR250:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP76:%.*]] = load <4 x float>, ptr [[LEFTDIR250]], align 16
+// CHECK-NEXT:    [[VECEXT251:%.*]] = extractelement <4 x float> [[TMP76]], i64 0
+// CHECK-NEXT:    [[VECINIT252:%.*]] = insertelement <4 x float> poison, float [[VECEXT251]], i32 0
+// CHECK-NEXT:    [[LEFTDIR253:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP77:%.*]] = load <4 x float>, ptr [[LEFTDIR253]], align 16
+// CHECK-NEXT:    [[VECEXT254:%.*]] = extractelement <4 x float> [[TMP77]], i64 1
+// CHECK-NEXT:    [[VECINIT255:%.*]] = insertelement <4 x float> [[VECINIT252]], float [[VECEXT254]], i32 1
+// CHECK-NEXT:    [[LEFTDIR256:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP78:%.*]] = load <4 x float>, ptr [[LEFTDIR256]], align 16
+// CHECK-NEXT:    [[VECEXT257:%.*]] = extractelement <4 x float> [[TMP78]], i64 2
+// CHECK-NEXT:    [[VECINIT258:%.*]] = insertelement <4 x float> [[VECINIT255]], float [[VECEXT257]], i32 2
+// CHECK-NEXT:    [[LEFTDIR259:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 3
+// CHECK-NEXT:    [[TMP79:%.*]] = load <4 x float>, ptr [[LEFTDIR259]], align 16
+// CHECK-NEXT:    [[VECEXT260:%.*]] = extractelement <4 x float> [[TMP79]], i64 3
+// CHECK-NEXT:    [[VECINIT261:%.*]] = insertelement <4 x float> [[VECINIT258]], float [[VECEXT260]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT261]], ptr [[CLAWS249]], align 16
+// CHECK-NEXT:    [[ARRAYINIT_ELEMENT262:%.*]] = getelementptr inbounds <4 x float>, ptr [[CLAWS249]], i32 1
+// CHECK-NEXT:    [[RIGHTDIR263:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP80:%.*]] = load <4 x float>, ptr [[RIGHTDIR263]], align 16
+// CHECK-NEXT:    [[VECEXT264:%.*]] = extractelement <4 x float> [[TMP80]], i64 0
+// CHECK-NEXT:    [[VECINIT265:%.*]] = insertelement <4 x float> poison, float [[VECEXT264]], i32 0
+// CHECK-NEXT:    [[RIGHTDIR266:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP81:%.*]] = load <4 x float>, ptr [[RIGHTDIR266]], align 16
+// CHECK-NEXT:    [[VECEXT267:%.*]] = extractelement <4 x float> [[TMP81]], i64 1
+// CHECK-NEXT:    [[VECINIT268:%.*]] = insertelement <4 x float> [[VECINIT265]], float [[VECEXT267]], i32 1
+// CHECK-NEXT:    [[RIGHTDIR269:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP82:%.*]] = load <4 x float>, ptr [[RIGHTDIR269]], align 16
+// CHECK-NEXT:    [[VECEXT270:%.*]] = extractelement <4 x float> [[TMP82]], i64 2
+// CHECK-NEXT:    [[VECINIT271:%.*]] = insertelement <4 x float> [[VECINIT268]], float [[VECEXT270]], i32 2
+// CHECK-NEXT:    [[RIGHTDIR272:%.*]] = getelementptr inbounds nuw [[STRUCT_ANIMALBITS]], ptr [[A1]], i32 0, i32 4
+// CHECK-NEXT:    [[TMP83:%.*]] = load <4 x float>, ptr [[RIGHTDIR272]], align 16
+// CHECK-NEXT:    [[VECEXT273:%.*]] = extractelement <4 x float> [[TMP83]], i64 3
+// CHECK-NEXT:    [[VECINIT274:%.*]] = insertelement <4 x float> [[VECINIT271]], float [[VECEXT273]], i32 3
+// CHECK-NEXT:    store <4 x float> [[VECINIT274]], ptr [[ARRAYINIT_ELEMENT262]], align 16
+// CHECK-NEXT:    ret void
+//
+Zoo case9(Doggo D1, AnimalBits A1) {
+  Zoo Z1 = {D1, A1, D1, A1, D1, A1};
+  return Z1;
+}
diff --git a/clang/test/SemaHLSL/ArrayTemporary.hlsl b/clang/test/SemaHLSL/ArrayTemporary.hlsl
index 0266a198e7ec9..3d713a89adf3b 100644
--- a/clang/test/SemaHLSL/ArrayTemporary.hlsl
+++ b/clang/test/SemaHLSL/ArrayTemporary.hlsl
@@ -25,7 +25,7 @@ void fn2(Obj O[4]) { }
 // CHECK-NEXT: ImplicitCastExpr {{.*}} 'Obj[4]' <HLSLArrayRValue>
 
 void call2() {
-  Obj Arr[4] = {};
+  Obj Arr[4] = {0, 0, 0, 0, 0, 0, 0, 0};
   fn2(Arr);
 }
 
diff --git a/clang/test/SemaHLSL/Language/InitLists.hlsl b/clang/test/SemaHLSL/Language/InitLists.hlsl
new file mode 100644
index 0000000000000..0059e5b44035f
--- /dev/null
+++ b/clang/test/SemaHLSL/Language/InitLists.hlsl
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -finclude-default-header -verify -Wdouble-promotion -Wconversion %s
+
+struct TwoFloats {
+  float X, Y;
+};
+
+struct TwoInts {
+  int Z, W;
+};
+
+struct Doggo {
+  int4 LegState;
+  int TailState;
+  float HairCount;
+  float4 EarDirection[2];
+};
+
+struct AnimalBits {
+  int Legs[4];
+  uint State;
+  int64_t Counter;
+  float4 LeftDir;
+  float4 RightDir;
+};
+
+struct Kitteh {
+  int4 Legs;
+  int TailState;
+  float HairCount;
+  float4 Claws[2];
+};
+
+struct Zoo {
+  Doggo Dogs[2];
+  Kitteh Cats[4];
+};
+
+void fn() {
+  TwoFloats TF1 = {{{1.0, 2}}};
+  TwoFloats TF2 = {1,2};
+  int Val = 1;
+  TwoFloats TF3 = {Val, 2}; // expected-warning{{implicit conversion from 'int' to 'float' may lose precision}}
+  int2 TwoVals = 1.xx;
+  int2 Something = 1.xxx; // expected-warning{{implicit conversion truncates vector: 'vector<int, 3>' (vector of 3 'int' values) to 'vector<int, 2>' (vector of 2 'int' values)}}
+  TwoFloats TF4 = {TwoVals}; // expected-warning{{implicit conversion from 'int' to 'float' may lose precision}} expected-warning{{implicit conversion from 'int' to 'float' may lose precision}}
+
+  TwoInts TI1 = {TwoVals};
+  TwoInts TI2 = {TF4}; // expected-warning{{implicit conversion turns floating-point number into integer: 'float' to 'int'}} expected-warning{{implicit conversion turns floating-point number into integer: 'float' to 'int'}}
+
+  Doggo D1 = {TI1, TI2, {Val, Val}, {{TF1, TF2}, {TF3, TF4}}}; // expected-warning{{implicit conversion from 'int' to 'float' may lose precision}}
+  AnimalBits A1 = {D1}; // expected-warning{{implicit conversion turns floating-point number into integer: 'float' to 'long'}} expected-warning{{implicit conversion changes signedness: 'int' to 'unsigned int'}}
+
+  Zoo Z1 = {D1, A1, D1, A1, D1, A1}; // #insanity
+
+  // expected-warning@#insanity{{implicit conversion from 'int64_t' (aka 'long') to 'float' may lose precision}}
+  // expected-warning@#insanity{{implicit conversion changes signedness: 'uint' (aka 'unsigned int') to 'int'}}
+  // expected-warning@#insanity{{implicit conversion from 'int64_t' (aka 'long') to 'float' may lose precision}}
+  // expected-warning@#insanity{{implicit conversion changes signedness: 'uint' (aka 'unsigned int') to 'int'}}
+  // expected-warning@#insanity{{implicit conversion from 'int64_t' (aka 'long') to 'float' may lose precision}}
+  // expected-warning@#insanity{{implicit conversion changes signedness: 'uint' (aka 'unsigned int') to 'int'}}
+}
+
+void Errs() {
+  TwoFloats F1 = {}; // expected-error{{too few initializers in list for type 'TwoFloats' (expected 2 but found 0)}}
+  TwoFloats F2 = {1}; // expected-error{{too few initializers in list for type 'TwoFloats' (expected 2 but found 1)}}
+  TwoFloats F3 = {1,2,3}; // expected-error{{too many initializers in list for type 'TwoFloats' (expected 2 but found 2)}}
+
+  int2 Something = {1.xxx}; // expected-error{{too many initializers in list for type 'int2' (aka 'vector<int, 2>') (expected 2 but found 0)}}
+}

>From 8b9863657778c8d12e73c224635639d636132af4 Mon Sep 17 00:00:00 2001
From: Chris B <cbieneman at microsoft.com>
Date: Mon, 10 Feb 2025 15:01:47 -0600
Subject: [PATCH 02/10] Update clang/test/SemaHLSL/Language/InitLists.hlsl

Doh!

Co-authored-by: Finn Plummer <50529406+inbelic at users.noreply.github.com>
---
 clang/test/SemaHLSL/Language/InitLists.hlsl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaHLSL/Language/InitLists.hlsl b/clang/test/SemaHLSL/Language/InitLists.hlsl
index 0059e5b44035f..1f7baf35ed2e3 100644
--- a/clang/test/SemaHLSL/Language/InitLists.hlsl
+++ b/clang/test/SemaHLSL/Language/InitLists.hlsl
@@ -63,7 +63,7 @@ void fn() {
 void Errs() {
   TwoFloats F1 = {}; // expected-error{{too few initializers in list for type 'TwoFloats' (expected 2 but found 0)}}
   TwoFloats F2 = {1}; // expected-error{{too few initializers in list for type 'TwoFloats' (expected 2 but found 1)}}
-  TwoFloats F3 = {1,2,3}; // expected-error{{too many initializers in list for type 'TwoFloats' (expected 2 but found 2)}}
+  TwoFloats F3 = {1,2,3}; // expected-error{{too many initializers in list for type 'TwoFloats' (expected 2 but found 3)}}
 
   int2 Something = {1.xxx}; // expected-error{{too many initializers in list for type 'int2' (aka 'vector<int, 2>') (expected 2 but found 0)}}
 }

>From 29aca992f5fedd384e93d27b092a154297443113 Mon Sep 17 00:00:00 2001
From: Chris B <cbieneman at microsoft.com>
Date: Mon, 10 Feb 2025 15:02:21 -0600
Subject: [PATCH 03/10] Update clang/lib/Sema/SemaHLSL.cpp

I swear I can spell...

Co-authored-by: Helena Kotas <hekotas at microsoft.com>
---
 clang/lib/Sema/SemaHLSL.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index d474082827c15..1184a5f8b3054 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3027,7 +3027,7 @@ static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
   return true;
 }
 
-static void BuildIntializerList(Sema &S, ASTContext &Ctx, Expr *E,
+static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
                                 llvm::SmallVectorImpl<Expr *> &List,
                                 llvm::SmallVectorImpl<QualType> &DestTypes,
                                 bool &ExcessInits) {

>From eacd2b576e5c96700b50a884b8819b8a7da0e88b Mon Sep 17 00:00:00 2001
From: Chris B <cbieneman at microsoft.com>
Date: Mon, 10 Feb 2025 15:06:25 -0600
Subject: [PATCH 04/10] Update clang/test/SemaHLSL/Language/InitLists.hlsl

Co-authored-by: Finn Plummer <50529406+inbelic at users.noreply.github.com>
---
 clang/test/SemaHLSL/Language/InitLists.hlsl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/SemaHLSL/Language/InitLists.hlsl b/clang/test/SemaHLSL/Language/InitLists.hlsl
index 1f7baf35ed2e3..79ba126758589 100644
--- a/clang/test/SemaHLSL/Language/InitLists.hlsl
+++ b/clang/test/SemaHLSL/Language/InitLists.hlsl
@@ -65,5 +65,5 @@ void Errs() {
   TwoFloats F2 = {1}; // expected-error{{too few initializers in list for type 'TwoFloats' (expected 2 but found 1)}}
   TwoFloats F3 = {1,2,3}; // expected-error{{too many initializers in list for type 'TwoFloats' (expected 2 but found 3)}}
 
-  int2 Something = {1.xxx}; // expected-error{{too many initializers in list for type 'int2' (aka 'vector<int, 2>') (expected 2 but found 0)}}
+  int2 Something = {1.xxx}; // expected-error{{too many initializers in list for type 'int2' (aka 'vector<int, 2>') (expected 2 but found 3)}}
 }

>From bbcf82b2a5c0854e1f780cbff775664669fb2172 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Mon, 10 Feb 2025 17:25:38 -0600
Subject: [PATCH 05/10] Updates to fix diagnostics and adjust to new upstream
 changes

More updates coming to handle additional PR review.
---
 clang/lib/Sema/SemaHLSL.cpp | 39 +++++++++++++++++--------------------
 1 file changed, 18 insertions(+), 21 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 1184a5f8b3054..9e311193784ee 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3014,12 +3014,13 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
 static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
                             llvm::SmallVectorImpl<Expr *> &List,
                             llvm::SmallVectorImpl<QualType> &DestTypes) {
-  if (List.size() >= DestTypes.size())
-    return false;
-  InitializedEntity Entity =
-      InitializedEntity::InitializeParameter(Ctx, DestTypes[List.size()], false);
-  ExprResult Res =
-      S.PerformCopyInitialization(Entity, E->getBeginLoc(), E);
+  if (List.size() >= DestTypes.size()) {
+    List.push_back(E);
+    return true;
+  }
+  InitializedEntity Entity = InitializedEntity::InitializeParameter(
+      Ctx, DestTypes[List.size()], false);
+  ExprResult Res = S.PerformCopyInitialization(Entity, E->getBeginLoc(), E);
   if (Res.isInvalid())
     return false;
   Expr *Init = Res.get();
@@ -3028,18 +3029,16 @@ static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
 }
 
 static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
-                                llvm::SmallVectorImpl<Expr *> &List,
-                                llvm::SmallVectorImpl<QualType> &DestTypes,
-                                bool &ExcessInits) {
-  if (List.size() >= DestTypes.size()) {
+                                 llvm::SmallVectorImpl<Expr *> &List,
+                                 llvm::SmallVectorImpl<QualType> &DestTypes,
+                                 bool &ExcessInits) {
+  if (List.size() >= DestTypes.size())
     ExcessInits = true;
-    return;
-  }
 
   // If this is an initialization list, traverse the sub initializers.
   if (auto *Init = dyn_cast<InitListExpr>(E)) {
     for (auto *SubInit : Init->inits())
-      BuildIntializerList(S, Ctx, SubInit, List, DestTypes, ExcessInits);
+      BuildInitializerList(S, Ctx, SubInit, List, DestTypes, ExcessInits);
     return;
   }
 
@@ -3053,10 +3052,8 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
   if (auto *ATy = Ty->getAs<VectorType>()) {
     uint64_t Size = ATy->getNumElements();
 
-    if (List.size() + Size > DestTypes.size()) {
+    if (List.size() + Size > DestTypes.size())
       ExcessInits = true;
-      return;
-    }
     QualType SizeTy = Ctx.getSizeType();
     uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
     for (uint64_t I = 0; I < Size; ++I) {
@@ -3067,8 +3064,7 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
           E, E->getBeginLoc(), Idx, E->getEndLoc());
       if (ElExpr.isInvalid())
         return;
-      if (!CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes))
-        return;
+      CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes);
     }
     return;
   }
@@ -3084,7 +3080,7 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
           E, E->getBeginLoc(), Idx, E->getEndLoc());
       if (ElExpr.isInvalid())
         return;
-      BuildIntializerList(S, Ctx, ElExpr.get(), List, DestTypes, ExcessInits);
+      BuildInitializerList(S, Ctx, ElExpr.get(), List, DestTypes, ExcessInits);
     }
     return;
   }
@@ -3097,7 +3093,7 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
           E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo);
       if (Res.isInvalid())
         return;
-      BuildIntializerList(S, Ctx, Res.get(), List, DestTypes, ExcessInits);
+      BuildInitializerList(S, Ctx, Res.get(), List, DestTypes, ExcessInits);
     }
   }
 }
@@ -3109,6 +3105,7 @@ static Expr *GenerateInitLists(ASTContext &Ctx, QualType Ty,
   }
   llvm::SmallVector<Expr *> Inits;
   assert(!isa<MatrixType>(Ty) && "Matrix types not yet supported in HLSL");
+  Ty = Ty.getDesugaredType(Ctx);
   if (Ty->isVectorType() || Ty->isConstantArrayType()) {
     QualType ElTy;
     uint64_t Size = 0;
@@ -3151,7 +3148,7 @@ bool SemaHLSL::TransformInitList(const InitializedEntity &Entity,
   llvm::SmallVector<Expr *, 16> ArgExprs;
   bool ExcessInits = false;
   for (Expr *Arg : Init->inits())
-    BuildIntializerList(SemaRef, Ctx, Arg, ArgExprs, DestTypes, ExcessInits);
+    BuildInitializerList(SemaRef, Ctx, Arg, ArgExprs, DestTypes, ExcessInits);
 
   if (DestTypes.size() != ArgExprs.size() || ExcessInits) {
     int TooManyOrFew = ExcessInits ? 1 : 0;

>From ec23490f41d562a648d78cd4ee5c024163eae2d7 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Tue, 11 Feb 2025 14:44:59 -0600
Subject: [PATCH 06/10] Update to support classes with base classes

Also added tests for bitfield members to verify correct code generation
for initializing bitfield members or initializing new objects from
bitfields.

../clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
---
 clang/lib/AST/DeclCXX.cpp                     |   8 +
 clang/lib/Sema/SemaHLSL.cpp                   |  44 +++-
 .../CodeGenHLSL/BasicFeatures/InitLists.hlsl  | 189 ++++++++++++++++++
 clang/test/SemaHLSL/Language/InitLists.hlsl   |  20 ++
 4 files changed, 250 insertions(+), 11 deletions(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index e394e0515e599..64499576af955 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1462,6 +1462,14 @@ void CXXRecordDecl::addedMember(Decl *D) {
     if (Using->getDeclName().getCXXOverloadedOperator() == OO_Equal)
       data().HasInheritedAssignment = true;
   }
+
+  // HLSL: All user-defined data types are aggregates and use aggregate
+  // initialization. This _needs_ to change in the future. There are two
+  // relevant HLSL feature proposals that will depend on this changing:
+  // * 0005-strict-initializer-lists.md
+  // * https://github.com/microsoft/hlsl-specs/pull/325
+  if (getLangOpts().HLSL && !isImplicit())
+    data().Aggregate = true;
 }
 
 bool CXXRecordDecl::isLiteral() const {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 9e311193784ee..754e44933f350 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3086,14 +3086,25 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
   }
 
   if (auto *RTy = Ty->getAs<RecordType>()) {
-    for (auto *FD : RTy->getDecl()->fields()) {
-      DeclAccessPair Found = DeclAccessPair::make(FD, FD->getAccess());
-      DeclarationNameInfo NameInfo(FD->getDeclName(), E->getBeginLoc());
-      ExprResult Res = S.BuildFieldReferenceExpr(
-          E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo);
-      if (Res.isInvalid())
-        return;
-      BuildInitializerList(S, Ctx, Res.get(), List, DestTypes, ExcessInits);
+    llvm::SmallVector<const RecordType*> RecordTypes;
+    RecordTypes.push_back(RTy);
+    while(RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) {
+      CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl();
+      assert(D->getNumBases() == 1 && "HLSL doesn't support multiple inheritance");
+      RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>());
+    }
+    while (!RecordTypes.empty()) {
+      const RecordType* RT = RecordTypes.back();
+      RecordTypes.pop_back();
+      for (auto *FD : RT->getDecl()->fields()) {
+        DeclAccessPair Found = DeclAccessPair::make(FD, FD->getAccess());
+        DeclarationNameInfo NameInfo(FD->getDeclName(), E->getBeginLoc());
+        ExprResult Res = S.BuildFieldReferenceExpr(
+            E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo);
+        if (Res.isInvalid())
+          return;
+        BuildInitializerList(S, Ctx, Res.get(), List, DestTypes, ExcessInits);
+      }
     }
   }
 }
@@ -3120,9 +3131,20 @@ static Expr *GenerateInitLists(ASTContext &Ctx, QualType Ty,
     for (uint64_t I = 0; I < Size; ++I)
       Inits.push_back(GenerateInitLists(Ctx, ElTy, It));
   }
-  if (const RecordDecl *RD = Ty->getAsRecordDecl()) {
-    for (auto *FD : RD->fields()) {
-      Inits.push_back(GenerateInitLists(Ctx, FD->getType(), It));
+  if (auto *RTy = Ty->getAs<RecordType>()) {
+    llvm::SmallVector<const RecordType*> RecordTypes;
+    RecordTypes.push_back(RTy);
+    while(RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) {
+      CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl();
+      assert(D->getNumBases() == 1 && "HLSL doesn't support multiple inheritance");
+      RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>());
+    }
+    while (!RecordTypes.empty()) {
+      const RecordType* RT = RecordTypes.back();
+      RecordTypes.pop_back();
+      for (auto *FD : RT->getDecl()->fields()) {
+        Inits.push_back(GenerateInitLists(Ctx, FD->getType(), It));
+      }
     }
   }
   auto *NewInit = new (Ctx) InitListExpr(Ctx, Inits.front()->getBeginLoc(),
diff --git a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
index e57724b0ec31f..97baac3272a66 100644
--- a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
+++ b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
@@ -36,6 +36,15 @@ struct Zoo {
   Kitteh Cats[4];
 };
 
+struct FourFloats : TwoFloats {
+  float Z, W;
+};
+
+struct SlicyBits {
+  int Z : 8;
+  int W : 8;
+};
+
 // Case 1: Extraneous braces get ignored in literal instantiation.
 // CHECK-LABEL: define void @_Z5case1v(
 // CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOFLOATS:%.*]]) align 4 [[AGG_RESULT:%.*]]) #[[ATTR0:[0-9]+]] {
@@ -712,3 +721,183 @@ Zoo case9(Doggo D1, AnimalBits A1) {
   Zoo Z1 = {D1, A1, D1, A1, D1, A1};
   return Z1;
 }
+
+// Case 10: Initialize an object with a base class from two objects.
+// CHECK-LABEL: define void @_Z6case109TwoFloatsS_(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_FOURFLOATS:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS:%.*]]) align 4 [[TF1:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS]]) align 4 [[TF2:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[X1:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load float, ptr [[X1]], align 4
+// CHECK-NEXT:    store float [[TMP0]], ptr [[X]], align 4
+// CHECK-NEXT:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[Y2:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP1:%.*]] = load float, ptr [[Y2]], align 4
+// CHECK-NEXT:    store float [[TMP1]], ptr [[Y]], align 4
+// CHECK-NEXT:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_FOURFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[X3:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF2]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP2:%.*]] = load float, ptr [[X3]], align 4
+// CHECK-NEXT:    store float [[TMP2]], ptr [[Z]], align 4
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds nuw [[STRUCT_FOURFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 2
+// CHECK-NEXT:    [[Y4:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[TF2]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP3:%.*]] = load float, ptr [[Y4]], align 4
+// CHECK-NEXT:    store float [[TMP3]], ptr [[W]], align 4
+// CHECK-NEXT:    ret void
+//
+FourFloats case10(TwoFloats TF1, TwoFloats TF2) {
+  FourFloats FF1 = {TF1, TF2};
+  return FF1;
+}
+
+// Case 11: Initialize an object with a base class from a vector splat.
+// CHECK-LABEL: define void @_Z6case11f(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_FOURFLOATS:%.*]]) align 4 [[AGG_RESULT:%.*]], float noundef nofpclass(nan inf) [[F:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[F_ADDR:%.*]] = alloca float, align 4
+// CHECK-NEXT:    [[REF_TMP:%.*]] = alloca <4 x float>, align 16
+// CHECK-NEXT:    [[REF_TMP1:%.*]] = alloca <4 x float>, align 16
+// CHECK-NEXT:    [[REF_TMP4:%.*]] = alloca <4 x float>, align 16
+// CHECK-NEXT:    [[REF_TMP7:%.*]] = alloca <4 x float>, align 16
+// CHECK-NEXT:    store float [[F]], ptr [[F_ADDR]], align 4
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS:%.*]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load float, ptr [[F_ADDR]], align 4
+// CHECK-NEXT:    [[CAST_SPLAT:%.*]] = insertelement <1 x float> poison, float [[TMP0]], i64 0
+// CHECK-NEXT:    [[TMP1:%.*]] = shufflevector <1 x float> [[CAST_SPLAT]], <1 x float> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    store <4 x float> [[TMP1]], ptr [[REF_TMP]], align 16
+// CHECK-NEXT:    [[TMP2:%.*]] = load <4 x float>, ptr [[REF_TMP]], align 16
+// CHECK-NEXT:    [[VECEXT:%.*]] = extractelement <4 x float> [[TMP2]], i64 0
+// CHECK-NEXT:    store float [[VECEXT]], ptr [[X]], align 4
+// CHECK-NEXT:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP3:%.*]] = load float, ptr [[F_ADDR]], align 4
+// CHECK-NEXT:    [[CAST_SPLAT2:%.*]] = insertelement <1 x float> poison, float [[TMP3]], i64 0
+// CHECK-NEXT:    [[TMP4:%.*]] = shufflevector <1 x float> [[CAST_SPLAT2]], <1 x float> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    store <4 x float> [[TMP4]], ptr [[REF_TMP1]], align 16
+// CHECK-NEXT:    [[TMP5:%.*]] = load <4 x float>, ptr [[REF_TMP1]], align 16
+// CHECK-NEXT:    [[VECEXT3:%.*]] = extractelement <4 x float> [[TMP5]], i64 1
+// CHECK-NEXT:    store float [[VECEXT3]], ptr [[Y]], align 4
+// CHECK-NEXT:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_FOURFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP6:%.*]] = load float, ptr [[F_ADDR]], align 4
+// CHECK-NEXT:    [[CAST_SPLAT5:%.*]] = insertelement <1 x float> poison, float [[TMP6]], i64 0
+// CHECK-NEXT:    [[TMP7:%.*]] = shufflevector <1 x float> [[CAST_SPLAT5]], <1 x float> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    store <4 x float> [[TMP7]], ptr [[REF_TMP4]], align 16
+// CHECK-NEXT:    [[TMP8:%.*]] = load <4 x float>, ptr [[REF_TMP4]], align 16
+// CHECK-NEXT:    [[VECEXT6:%.*]] = extractelement <4 x float> [[TMP8]], i64 2
+// CHECK-NEXT:    store float [[VECEXT6]], ptr [[Z]], align 4
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds nuw [[STRUCT_FOURFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 2
+// CHECK-NEXT:    [[TMP9:%.*]] = load float, ptr [[F_ADDR]], align 4
+// CHECK-NEXT:    [[CAST_SPLAT8:%.*]] = insertelement <1 x float> poison, float [[TMP9]], i64 0
+// CHECK-NEXT:    [[TMP10:%.*]] = shufflevector <1 x float> [[CAST_SPLAT8]], <1 x float> poison, <4 x i32> zeroinitializer
+// CHECK-NEXT:    store <4 x float> [[TMP10]], ptr [[REF_TMP7]], align 16
+// CHECK-NEXT:    [[TMP11:%.*]] = load <4 x float>, ptr [[REF_TMP7]], align 16
+// CHECK-NEXT:    [[VECEXT9:%.*]] = extractelement <4 x float> [[TMP11]], i64 3
+// CHECK-NEXT:    store float [[VECEXT9]], ptr [[W]], align 4
+// CHECK-NEXT:    ret void
+//
+FourFloats case11(float F) {
+  FourFloats FF1 = {F.xxxx};
+  return FF1;
+}
+
+// Case 12: Initialize bitfield from two integers.
+// CHECK-LABEL: define void @_Z6case12ii(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_SLICYBITS:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[I:%.*]], i32 noundef [[J:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[I_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[J_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store i32 [[I]], ptr [[I_ADDR]], align 4
+// CHECK-NEXT:    store i32 [[J]], ptr [[J_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[I_ADDR]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = trunc i32 [[TMP0]] to i16
+// CHECK-NEXT:    [[BF_LOAD:%.*]] = load i16, ptr [[AGG_RESULT]], align 4
+// CHECK-NEXT:    [[BF_VALUE:%.*]] = and i16 [[TMP1]], 255
+// CHECK-NEXT:    [[BF_CLEAR:%.*]] = and i16 [[BF_LOAD]], -256
+// CHECK-NEXT:    [[BF_SET:%.*]] = or i16 [[BF_CLEAR]], [[BF_VALUE]]
+// CHECK-NEXT:    store i16 [[BF_SET]], ptr [[AGG_RESULT]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[J_ADDR]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = trunc i32 [[TMP2]] to i16
+// CHECK-NEXT:    [[BF_LOAD1:%.*]] = load i16, ptr [[AGG_RESULT]], align 4
+// CHECK-NEXT:    [[BF_VALUE2:%.*]] = and i16 [[TMP3]], 255
+// CHECK-NEXT:    [[BF_SHL:%.*]] = shl i16 [[BF_VALUE2]], 8
+// CHECK-NEXT:    [[BF_CLEAR3:%.*]] = and i16 [[BF_LOAD1]], 255
+// CHECK-NEXT:    [[BF_SET4:%.*]] = or i16 [[BF_CLEAR3]], [[BF_SHL]]
+// CHECK-NEXT:    store i16 [[BF_SET4]], ptr [[AGG_RESULT]], align 4
+// CHECK-NEXT:    ret void
+//
+SlicyBits case12(int I, int J) {
+  SlicyBits SB = {I, J};
+  return SB;
+}
+
+// Case 13: Initialize bitfield from a struct of two ints.
+// CHECK-LABEL: define void @_Z6case137TwoInts(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_SLICYBITS:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_TWOINTS:%.*]]) align 4 [[TI:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[TI]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, ptr [[Z]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = trunc i32 [[TMP0]] to i16
+// CHECK-NEXT:    [[BF_LOAD:%.*]] = load i16, ptr [[AGG_RESULT]], align 4
+// CHECK-NEXT:    [[BF_VALUE:%.*]] = and i16 [[TMP1]], 255
+// CHECK-NEXT:    [[BF_CLEAR:%.*]] = and i16 [[BF_LOAD]], -256
+// CHECK-NEXT:    [[BF_SET:%.*]] = or i16 [[BF_CLEAR]], [[BF_VALUE]]
+// CHECK-NEXT:    store i16 [[BF_SET]], ptr [[AGG_RESULT]], align 4
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[TI]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr [[W]], align 4
+// CHECK-NEXT:    [[TMP3:%.*]] = trunc i32 [[TMP2]] to i16
+// CHECK-NEXT:    [[BF_LOAD1:%.*]] = load i16, ptr [[AGG_RESULT]], align 4
+// CHECK-NEXT:    [[BF_VALUE2:%.*]] = and i16 [[TMP3]], 255
+// CHECK-NEXT:    [[BF_SHL:%.*]] = shl i16 [[BF_VALUE2]], 8
+// CHECK-NEXT:    [[BF_CLEAR3:%.*]] = and i16 [[BF_LOAD1]], 255
+// CHECK-NEXT:    [[BF_SET4:%.*]] = or i16 [[BF_CLEAR3]], [[BF_SHL]]
+// CHECK-NEXT:    store i16 [[BF_SET4]], ptr [[AGG_RESULT]], align 4
+// CHECK-NEXT:    ret void
+//
+SlicyBits case13(TwoInts TI) {
+  SlicyBits SB = {TI};
+  return SB;
+}
+
+// Case 14: Initialize struct of ints from struct with bitfields.
+// CHECK-LABEL: define void @_Z6case149SlicyBits(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOINTS:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_SLICYBITS:%.*]]) align 4 [[SB:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[Z:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[BF_LOAD:%.*]] = load i16, ptr [[SB]], align 4
+// CHECK-NEXT:    [[BF_SHL:%.*]] = shl i16 [[BF_LOAD]], 8
+// CHECK-NEXT:    [[BF_ASHR:%.*]] = ashr i16 [[BF_SHL]], 8
+// CHECK-NEXT:    [[BF_CAST:%.*]] = sext i16 [[BF_ASHR]] to i32
+// CHECK-NEXT:    store i32 [[BF_CAST]], ptr [[Z]], align 4
+// CHECK-NEXT:    [[W:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOINTS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[BF_LOAD1:%.*]] = load i16, ptr [[SB]], align 4
+// CHECK-NEXT:    [[BF_ASHR2:%.*]] = ashr i16 [[BF_LOAD1]], 8
+// CHECK-NEXT:    [[BF_CAST3:%.*]] = sext i16 [[BF_ASHR2]] to i32
+// CHECK-NEXT:    store i32 [[BF_CAST3]], ptr [[W]], align 4
+// CHECK-NEXT:    ret void
+//
+TwoInts case14(SlicyBits SB) {
+  TwoInts TI = {SB};
+  return TI;
+}
+
+// Case 15: Initialize struct of floats from struct with bitfields.
+// CHECK-LABEL: define void @_Z6case159SlicyBits(
+// CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_TWOFLOATS:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_SLICYBITS:%.*]]) align 4 [[SB:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 0
+// CHECK-NEXT:    [[BF_LOAD:%.*]] = load i16, ptr [[SB]], align 4
+// CHECK-NEXT:    [[BF_SHL:%.*]] = shl i16 [[BF_LOAD]], 8
+// CHECK-NEXT:    [[BF_ASHR:%.*]] = ashr i16 [[BF_SHL]], 8
+// CHECK-NEXT:    [[BF_CAST:%.*]] = sext i16 [[BF_ASHR]] to i32
+// CHECK-NEXT:    [[CONV:%.*]] = sitofp i32 [[BF_CAST]] to float
+// CHECK-NEXT:    store float [[CONV]], ptr [[X]], align 4
+// CHECK-NEXT:    [[Y:%.*]] = getelementptr inbounds nuw [[STRUCT_TWOFLOATS]], ptr [[AGG_RESULT]], i32 0, i32 1
+// CHECK-NEXT:    [[BF_LOAD1:%.*]] = load i16, ptr [[SB]], align 4
+// CHECK-NEXT:    [[BF_ASHR2:%.*]] = ashr i16 [[BF_LOAD1]], 8
+// CHECK-NEXT:    [[BF_CAST3:%.*]] = sext i16 [[BF_ASHR2]] to i32
+// CHECK-NEXT:    [[CONV4:%.*]] = sitofp i32 [[BF_CAST3]] to float
+// CHECK-NEXT:    store float [[CONV4]], ptr [[Y]], align 4
+// CHECK-NEXT:    ret void
+//
+TwoFloats case15(SlicyBits SB) {
+  TwoFloats TI = {SB};
+  return TI;
+}
diff --git a/clang/test/SemaHLSL/Language/InitLists.hlsl b/clang/test/SemaHLSL/Language/InitLists.hlsl
index 79ba126758589..5555e899ecb5d 100644
--- a/clang/test/SemaHLSL/Language/InitLists.hlsl
+++ b/clang/test/SemaHLSL/Language/InitLists.hlsl
@@ -35,6 +35,15 @@ struct Zoo {
   Kitteh Cats[4];
 };
 
+struct FourFloats : TwoFloats {
+  float Z, W;
+};
+
+struct SlicyBits {
+  int Z : 8;
+  int W : 8;
+};
+
 void fn() {
   TwoFloats TF1 = {{{1.0, 2}}};
   TwoFloats TF2 = {1,2};
@@ -60,6 +69,17 @@ void fn() {
   // expected-warning@#insanity{{implicit conversion changes signedness: 'uint' (aka 'unsigned int') to 'int'}}
 }
 
+void fn2() {
+  TwoFloats TF2 = {1,2};
+  FourFloats FF1 = {TF2, TF2};
+  FourFloats FF2 = {1,2,3,4};
+  FourFloats FF3 = {1.xxx, 2};
+
+  SlicyBits SB1 = {1,2};
+  TwoInts TI1 = {SB1};
+  SlicyBits SB2 = {TI1};
+}
+
 void Errs() {
   TwoFloats F1 = {}; // expected-error{{too few initializers in list for type 'TwoFloats' (expected 2 but found 0)}}
   TwoFloats F2 = {1}; // expected-error{{too few initializers in list for type 'TwoFloats' (expected 2 but found 1)}}

>From 2a335b979e5b69096bbd571c5d8f0574aaee66ca Mon Sep 17 00:00:00 2001
From: Chris B <cbieneman at microsoft.com>
Date: Tue, 11 Feb 2025 16:25:49 -0600
Subject: [PATCH 07/10] Update
 clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl

Co-authored-by: Helena Kotas <hekotas at microsoft.com>
---
 clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
index 97baac3272a66..48041f2e4853c 100644
--- a/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
+++ b/clang/test/CodeGenHLSL/BasicFeatures/InitLists.hlsl
@@ -155,7 +155,7 @@ TwoInts case6(TwoFloats TF4) {
   return TI2;
 }
 
-// Case 7: Initialization of a complex structue, with bogus braces and element
+// Case 7: Initialization of a complex structure, with bogus braces and element
 // conversions from a collection of scalar values, and structures.
 // CHECK-LABEL: define void @_Z5case77TwoIntsS_i9TwoFloatsS0_S0_S0_(
 // CHECK-SAME: ptr dead_on_unwind noalias writable sret([[STRUCT_DOGGO:%.*]]) align 16 [[AGG_RESULT:%.*]], ptr noundef byval([[STRUCT_TWOINTS:%.*]]) align 4 [[TI1:%.*]], ptr noundef byval([[STRUCT_TWOINTS]]) align 4 [[TI2:%.*]], i32 noundef [[VAL:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS:%.*]]) align 4 [[TF1:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS]]) align 4 [[TF2:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS]]) align 4 [[TF3:%.*]], ptr noundef byval([[STRUCT_TWOFLOATS]]) align 4 [[TF4:%.*]]) #[[ATTR0]] {

>From 3d06531d1b1d96d41ff85766a516e3118b0d4bf1 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Tue, 11 Feb 2025 16:35:17 -0600
Subject: [PATCH 08/10] Fix formatting

---
 clang/lib/Sema/SemaHLSL.cpp | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 754e44933f350..07fd228307054 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3086,15 +3086,16 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
   }
 
   if (auto *RTy = Ty->getAs<RecordType>()) {
-    llvm::SmallVector<const RecordType*> RecordTypes;
+    llvm::SmallVector<const RecordType *> RecordTypes;
     RecordTypes.push_back(RTy);
-    while(RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) {
+    while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) {
       CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl();
-      assert(D->getNumBases() == 1 && "HLSL doesn't support multiple inheritance");
+      assert(D->getNumBases() == 1 &&
+             "HLSL doesn't support multiple inheritance");
       RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>());
     }
     while (!RecordTypes.empty()) {
-      const RecordType* RT = RecordTypes.back();
+      const RecordType *RT = RecordTypes.back();
       RecordTypes.pop_back();
       for (auto *FD : RT->getDecl()->fields()) {
         DeclAccessPair Found = DeclAccessPair::make(FD, FD->getAccess());
@@ -3132,15 +3133,16 @@ static Expr *GenerateInitLists(ASTContext &Ctx, QualType Ty,
       Inits.push_back(GenerateInitLists(Ctx, ElTy, It));
   }
   if (auto *RTy = Ty->getAs<RecordType>()) {
-    llvm::SmallVector<const RecordType*> RecordTypes;
+    llvm::SmallVector<const RecordType *> RecordTypes;
     RecordTypes.push_back(RTy);
-    while(RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) {
+    while (RecordTypes.back()->getAsCXXRecordDecl()->getNumBases()) {
       CXXRecordDecl *D = RecordTypes.back()->getAsCXXRecordDecl();
-      assert(D->getNumBases() == 1 && "HLSL doesn't support multiple inheritance");
+      assert(D->getNumBases() == 1 &&
+             "HLSL doesn't support multiple inheritance");
       RecordTypes.push_back(D->bases_begin()->getType()->getAs<RecordType>());
     }
     while (!RecordTypes.empty()) {
-      const RecordType* RT = RecordTypes.back();
+      const RecordType *RT = RecordTypes.back();
       RecordTypes.pop_back();
       for (auto *FD : RT->getDecl()->fields()) {
         Inits.push_back(GenerateInitLists(Ctx, FD->getType(), It));

>From 9ed701f76c414c3808b9930b6f813dd5f9854949 Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Tue, 11 Feb 2025 16:44:52 -0600
Subject: [PATCH 09/10] Split HLSL's comment from C++'s

---
 clang/lib/Sema/SemaChecking.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index e423b820c6032..aae61f612a4bc 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -11678,8 +11678,9 @@ static void AnalyzeImplicitConversions(
 
   // Propagate whether we are in a C++ list initialization expression.
   // If so, we do not issue warnings for implicit int-float conversion
-  // precision loss, because C++11 narrowing already handles it. HLSL's
-  // initialization lists are special, so they shouldn't observe the C++
+  // precision loss, because C++11 narrowing already handles it.
+  //
+  // HLSL's initialization lists are special, so they shouldn't observe the C++
   // behavior here.
   bool IsListInit =
       Item.IsListInit || (isa<InitListExpr>(OrigE) &&

>From 5a3c824522aeb37dbdc295a50bd0c26891f16b2a Mon Sep 17 00:00:00 2001
From: Chris Bieneman <chris.bieneman at me.com>
Date: Tue, 11 Feb 2025 22:12:13 -0600
Subject: [PATCH 10/10] Fix initialization with resources in structs

This was a bit tricker than I expected, but I think I came up with a
reasonably clever solution.

In HLSL, user-defined data types have aggregate initialization, not
constructors _except_ that some of the builtin types we do model
constructors for. This is actually useful!

In my earlier change I updated DeclCXX so that HLSL non-implicit
classes are always marged as Aggregates. This ends up being not
quite right, because there are some implicit types that should be
aggregates, and others that shouldn't. For example, the implicit
cbuffer-layout types should be aggregates so that we can flatten
them, but resources shouldn't be because we really don't want to
flatten them.

In the update, whether or not an HLSL type is an aggregate is keyed
off having non-implicit "special" members (constructors & operators).
This is more correct.

Aggregate types get flattened out for casting and initialization, while
non-Aggregate types (basically just resources) get left unflattened and
we attempt copy-initialization on them.

Next problem: I was getting some odd conflicting diagnostics when
argument conversion or copy-initialization fails, so I refactored
BuildInitializerList and CastInitializer to return success/failure so
that we can propagate that up and fail if the argument->destination
type fails without then also complaining about the number of
initializers.
---
 clang/lib/AST/DeclCXX.cpp                   |  4 +-
 clang/lib/Sema/SemaHLSL.cpp                 | 73 +++++++++++----------
 clang/test/SemaHLSL/Language/InitLists.hlsl | 24 +++++++
 3 files changed, 64 insertions(+), 37 deletions(-)

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 64499576af955..c601b6c6dbd6a 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1468,8 +1468,8 @@ void CXXRecordDecl::addedMember(Decl *D) {
   // relevant HLSL feature proposals that will depend on this changing:
   // * 0005-strict-initializer-lists.md
   // * https://github.com/microsoft/hlsl-specs/pull/325
-  if (getLangOpts().HLSL && !isImplicit())
-    data().Aggregate = true;
+  if (getLangOpts().HLSL)
+    data().Aggregate = data().UserDeclaredSpecialMembers == 0;
 }
 
 bool CXXRecordDecl::isLiteral() const {
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 07fd228307054..66589c9572a65 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2582,17 +2582,20 @@ static void BuildFlattenedTypeList(QualType BaseTy,
       continue;
     }
     if (const auto *RT = dyn_cast<RecordType>(T)) {
-      const RecordDecl *RD = RT->getDecl();
-      if (RD->isUnion()) {
+      const CXXRecordDecl *RD = RT->getAsCXXRecordDecl();
+      assert(RD && "HLSL record types should all be CXXRecordDecls!");
+
+      if (RD->isStandardLayout())
+        RD = RD->getStandardLayoutBaseWithFields();
+
+      // For types that we shouldn't decompose (unios and non-aggregates), just
+      // add the type itself to the list.
+      if (RD->isUnion() || !RD->isAggregate()) {
         List.push_back(T);
         continue;
       }
-      const CXXRecordDecl *CXXD = dyn_cast<CXXRecordDecl>(RD);
 
       llvm::SmallVector<QualType, 16> FieldTypes;
-      if (CXXD && CXXD->isStandardLayout())
-        RD = CXXD->getStandardLayoutBaseWithFields();
-
       for (const auto *FD : RD->fields())
         FieldTypes.push_back(FD->getType());
       // Reverse the newly added sub-range.
@@ -2601,9 +2604,9 @@ static void BuildFlattenedTypeList(QualType BaseTy,
 
       // If this wasn't a standard layout type we may also have some base
       // classes to deal with.
-      if (CXXD && !CXXD->isStandardLayout()) {
+      if (!RD->isStandardLayout()) {
         FieldTypes.clear();
-        for (const auto &Base : CXXD->bases())
+        for (const auto &Base : RD->bases())
           FieldTypes.push_back(Base.getType());
         std::reverse(FieldTypes.begin(), FieldTypes.end());
         WorkList.insert(WorkList.end(), FieldTypes.begin(), FieldTypes.end());
@@ -3016,6 +3019,8 @@ static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
                             llvm::SmallVectorImpl<QualType> &DestTypes) {
   if (List.size() >= DestTypes.size()) {
     List.push_back(E);
+    // This is odd, but it isn't technically a failure due to conversion, we
+    // handle mismatched counts of arguments differently.
     return true;
   }
   InitializedEntity Entity = InitializedEntity::InitializeParameter(
@@ -3028,32 +3033,26 @@ static bool CastInitializer(Sema &S, ASTContext &Ctx, Expr *E,
   return true;
 }
 
-static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
+static bool BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
                                  llvm::SmallVectorImpl<Expr *> &List,
-                                 llvm::SmallVectorImpl<QualType> &DestTypes,
-                                 bool &ExcessInits) {
-  if (List.size() >= DestTypes.size())
-    ExcessInits = true;
-
+                                 llvm::SmallVectorImpl<QualType> &DestTypes) {
   // If this is an initialization list, traverse the sub initializers.
   if (auto *Init = dyn_cast<InitListExpr>(E)) {
     for (auto *SubInit : Init->inits())
-      BuildInitializerList(S, Ctx, SubInit, List, DestTypes, ExcessInits);
-    return;
+      if (!BuildInitializerList(S, Ctx, SubInit, List, DestTypes))
+        return false;
+    return true;
   }
 
   // If this is a scalar type, just enqueue the expression.
   QualType Ty = E->getType();
-  if (Ty->isScalarType()) {
-    (void)CastInitializer(S, Ctx, E, List, DestTypes);
-    return;
-  }
+
+  if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType()))
+    return CastInitializer(S, Ctx, E, List, DestTypes);
 
   if (auto *ATy = Ty->getAs<VectorType>()) {
     uint64_t Size = ATy->getNumElements();
 
-    if (List.size() + Size > DestTypes.size())
-      ExcessInits = true;
     QualType SizeTy = Ctx.getSizeType();
     uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
     for (uint64_t I = 0; I < Size; ++I) {
@@ -3063,10 +3062,11 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
       ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
           E, E->getBeginLoc(), Idx, E->getEndLoc());
       if (ElExpr.isInvalid())
-        return;
-      CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes);
+        return false;
+      if (!CastInitializer(S, Ctx, ElExpr.get(), List, DestTypes))
+        return false;
     }
-    return;
+    return true;
   }
 
   if (auto *VTy = dyn_cast<ConstantArrayType>(Ty.getTypePtr())) {
@@ -3079,10 +3079,11 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
       ExprResult ElExpr = S.CreateBuiltinArraySubscriptExpr(
           E, E->getBeginLoc(), Idx, E->getEndLoc());
       if (ElExpr.isInvalid())
-        return;
-      BuildInitializerList(S, Ctx, ElExpr.get(), List, DestTypes, ExcessInits);
+        return false;
+      if (!BuildInitializerList(S, Ctx, ElExpr.get(), List, DestTypes))
+        return false;
     }
-    return;
+    return true;
   }
 
   if (auto *RTy = Ty->getAs<RecordType>()) {
@@ -3103,16 +3104,18 @@ static void BuildInitializerList(Sema &S, ASTContext &Ctx, Expr *E,
         ExprResult Res = S.BuildFieldReferenceExpr(
             E, false, E->getBeginLoc(), CXXScopeSpec(), FD, Found, NameInfo);
         if (Res.isInvalid())
-          return;
-        BuildInitializerList(S, Ctx, Res.get(), List, DestTypes, ExcessInits);
+          return false;
+        if (!BuildInitializerList(S, Ctx, Res.get(), List, DestTypes))
+          return false;
       }
     }
   }
+  return true;
 }
 
 static Expr *GenerateInitLists(ASTContext &Ctx, QualType Ty,
                                llvm::SmallVectorImpl<Expr *>::iterator &It) {
-  if (Ty->isScalarType()) {
+  if (Ty->isScalarType() || (Ty->isRecordType() && !Ty->isAggregateType())) {
     return *(It++);
   }
   llvm::SmallVector<Expr *> Inits;
@@ -3170,12 +3173,12 @@ bool SemaHLSL::TransformInitList(const InitializedEntity &Entity,
   BuildFlattenedTypeList(InitTy, DestTypes);
 
   llvm::SmallVector<Expr *, 16> ArgExprs;
-  bool ExcessInits = false;
   for (Expr *Arg : Init->inits())
-    BuildInitializerList(SemaRef, Ctx, Arg, ArgExprs, DestTypes, ExcessInits);
+    if (!BuildInitializerList(SemaRef, Ctx, Arg, ArgExprs, DestTypes))
+      return false;
 
-  if (DestTypes.size() != ArgExprs.size() || ExcessInits) {
-    int TooManyOrFew = ExcessInits ? 1 : 0;
+  if (DestTypes.size() != ArgExprs.size()) {
+    int TooManyOrFew = ArgExprs.size() > DestTypes.size() ? 1 : 0;
     SemaRef.Diag(Init->getBeginLoc(), diag::err_hlsl_incorrect_num_initializers)
         << TooManyOrFew << InitTy << DestTypes.size() << ArgExprs.size();
     return false;
diff --git a/clang/test/SemaHLSL/Language/InitLists.hlsl b/clang/test/SemaHLSL/Language/InitLists.hlsl
index 5555e899ecb5d..80f1dbe7bf12b 100644
--- a/clang/test/SemaHLSL/Language/InitLists.hlsl
+++ b/clang/test/SemaHLSL/Language/InitLists.hlsl
@@ -44,6 +44,16 @@ struct SlicyBits {
   int W : 8;
 };
 
+struct ContainsResource { // #ContainsResource
+  int X;
+  RWBuffer<float4> B;
+};
+
+struct ContainsResourceInverted {
+  RWBuffer<float4> B;
+  int X;
+};
+
 void fn() {
   TwoFloats TF1 = {{{1.0, 2}}};
   TwoFloats TF2 = {1,2};
@@ -87,3 +97,17 @@ void Errs() {
 
   int2 Something = {1.xxx}; // expected-error{{too many initializers in list for type 'int2' (aka 'vector<int, 2>') (expected 2 but found 3)}}
 }
+
+void Err2(RWBuffer<float4> B) {
+  ContainsResource RS1 = {1, B};
+  ContainsResource RS2 = (1.xx); // expected-error{{no viable conversion from 'vector<int, 2>' (vector of 2 'int' values) to 'ContainsResource'}}
+  ContainsResource RS3 = {B, 1}; // expected-error{{no viable conversion from 'RWBuffer<float4>' (aka 'RWBuffer<vector<float, 4>>') to 'int'}}
+  ContainsResourceInverted IR = {RS1}; // expected-error{{no viable conversion from 'int' to 'hlsl::RWBuffer<vector<float, 4>>'}}
+}
+
+// expected-note@#ContainsResource{{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'vector<int, 2>' (vector of 2 'int' values) to 'const ContainsResource &' for 1st argument}}
+// expected-note@#ContainsResource{{candidate constructor (the implicit move constructor) not viable: no known conversion from 'vector<int, 2>' (vector of 2 'int' values) to 'ContainsResource &&' for 1st argument}}
+
+// These notes refer to the RWBuffer constructors that do not have source locations
+// expected-note@*{{candidate constructor (the implicit copy constructor) not viable}}
+// expected-note@*{{candidate constructor (the implicit move constructor) not viable}}



More information about the cfe-commits mailing list