From a6061d975cd7df7acda70a076619df22207e9583 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 16 Apr 2026 15:37:48 -0700 Subject: [PATCH] Compute type layouts in SemIR / Check (#7066) Instead of allowing lower to pick whatever type layout it desires, compute the layouts of types as part of completing the type, and make lower build types that match that representation. For now we assume that all pointers are 64-bit, since we don't have access to target information. We allow tail padding reuse for structs and tuple types (and by extension, for classes, since they use structs as their object representation), but not for arrays. In order to build matching LLVM types, we create LLVM packed structs where necessary, and we insert inter-field padding on the end of the previous field so that GEP indexes still always match Carbon's ElementIndexes. We don't yet use the computed alignment much in LLVM IR generation -- in particular, `alloca`s, `load`s, and `store`s should probably use the computed type alignment, but don't. Assisted-by: Gemini via Antigravity --- toolchain/check/convert.cpp | 2 +- toolchain/check/cpp/import.cpp | 14 +- toolchain/check/custom_witness.cpp | 2 +- .../basics/raw_sem_ir/builtins.carbon | 12 + .../basics/raw_sem_ir/cpp_interop.carbon | 51 +++ .../basics/raw_sem_ir/multifile.carbon | 39 ++ .../multifile_with_textual_ir.carbon | 39 ++ .../basics/raw_sem_ir/one_file.carbon | 63 +++ .../one_file_with_textual_ir.carbon | 24 + .../interop/cpp/function/overloads.carbon | 32 +- toolchain/check/type_completion.cpp | 173 +++++-- toolchain/driver/testdata/stdin.carbon | 12 + toolchain/lower/aggregate.cpp | 6 +- toolchain/lower/constant.cpp | 33 +- toolchain/lower/file_context.cpp | 185 +++++++- toolchain/lower/function_context.cpp | 1 + toolchain/lower/handle_call.cpp | 2 +- toolchain/lower/testdata/array/iterate.carbon | 22 +- toolchain/lower/testdata/array/layout.carbon | 147 ++++++ toolchain/lower/testdata/class/field.carbon | 6 +- toolchain/lower/testdata/class/virtual.carbon | 18 +- toolchain/lower/testdata/for/bindings.carbon | 14 +- .../lower/testdata/for/break_continue.carbon | 22 +- toolchain/lower/testdata/for/for.carbon | 22 +- .../lower/testdata/primitives/optional.carbon | 60 +-- toolchain/lower/testdata/struct/layout.carbon | 433 ++++++++++++++++++ .../testdata/struct/member_access.carbon | 12 +- toolchain/sem_ir/file.cpp | 11 +- toolchain/sem_ir/file.h | 19 +- toolchain/sem_ir/inst_fingerprinter.cpp | 4 +- toolchain/sem_ir/type.h | 9 + toolchain/sem_ir/type_info.cpp | 13 +- toolchain/sem_ir/type_info.h | 144 ++++++ 33 files changed, 1456 insertions(+), 190 deletions(-) create mode 100644 toolchain/lower/testdata/array/layout.carbon create mode 100644 toolchain/lower/testdata/struct/layout.carbon diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 9d3147969de7..138b2b9a2722 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -264,7 +264,7 @@ static auto ConvertTupleToArray(Context& context, SemIR::TupleType tuple_type, // Check that the tuple is the right size. std::optional array_bound = - sem_ir.GetArrayBoundValue(array_type.bound_id); + sem_ir.GetZExtIntValue(array_type.bound_id); if (!array_bound) { // TODO: Should this fall back to using `ImplicitAs`? if (target.diagnose) { diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index 842bf075e8fc..57648edf82f8 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -64,6 +64,7 @@ #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/name_scope.h" #include "toolchain/sem_ir/pattern.h" +#include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -450,14 +451,16 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id, const auto& clang_layout = context.ast_context().getASTRecordLayout(clang_def); - llvm::SmallVector layout; + llvm::SmallVector layout; llvm::SmallVector fields; static_assert(SemIR::CustomLayoutId::SizeIndex == 0); - layout.push_back(clang_layout.getSize().getQuantity()); + layout.push_back( + SemIR::ObjectSize::Bytes(clang_layout.getSize().getQuantity())); static_assert(SemIR::CustomLayoutId::AlignIndex == 1); - layout.push_back(clang_layout.getAlignment().getQuantity()); + layout.push_back( + SemIR::ObjectSize::Bytes(clang_layout.getAlignment().getQuantity())); static_assert(SemIR::CustomLayoutId::FirstFieldIndex == 2); @@ -529,7 +532,7 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id, auto base_offset = base.isVirtual() ? clang_layout.getVBaseClassOffset(base_class) : clang_layout.getBaseClassOffset(base_class); - layout.push_back(base_offset.getQuantity()); + layout.push_back(SemIR::ObjectSize::Bytes(base_offset.getQuantity())); fields.push_back( {.name_id = SemIR::NameId::Base, .type_inst_id = base_type_inst_id}); } @@ -602,8 +605,7 @@ static auto ImportClassObjectRepr(Context& context, SemIR::ClassId class_id, offset += inner_layout.getFieldOffset(inner_field->getFieldIndex()); } - layout.push_back( - context.ast_context().toCharUnitsFromBits(offset).getQuantity()); + layout.push_back(SemIR::ObjectSize::Bits(offset)); fields.push_back( {.name_id = field_name_id, .type_inst_id = field_type_inst_id}); } diff --git a/toolchain/check/custom_witness.cpp b/toolchain/check/custom_witness.cpp index b43d9b3a851b..25ce07283f13 100644 --- a/toolchain/check/custom_witness.cpp +++ b/toolchain/check/custom_witness.cpp @@ -200,7 +200,7 @@ static auto CanDestroyType( case CARBON_KIND(SemIR::ArrayType array_type): { // A zero element array is always trivially destructible. if (auto int_bound = - context.sem_ir().GetArrayBoundValue(array_type.bound_id); + context.sem_ir().GetZExtIntValue(array_type.bound_id); !int_bound || *int_bound == 0) { return DestroyFormat::Trivial; } diff --git a/toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon b/toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon index fee94cb616ad..233930b4ab69 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/builtins.carbon @@ -37,12 +37,24 @@ // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: 'inst(TypeType)': {kind: TypeType, type: type(TypeType)} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon b/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon index 4871c9631bb2..8d612519b3bb 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon @@ -96,38 +96,89 @@ fn G(x: Cpp.X) { // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(InstType))': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000013)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst5000001E)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000020)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst50000020)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(inst(WitnessType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(WitnessType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000027)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst50000027)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(inst50000024)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(inst50000027)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(inst50000015)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(inst50000027)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(inst50000029)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst5000002E)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000031)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst5000003C)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000044)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000013)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: 'inst(TypeType)': {kind: TypeType, type: type(TypeType)} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon b/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon index 552c5662595f..068cacbe0763 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon @@ -55,16 +55,34 @@ fn B() { // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000011)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000012)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000012)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000012)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} @@ -138,18 +156,39 @@ fn B() { // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst70000013)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst70000014)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(InstType))': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon b/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon index 08d089b98584..4d9c74aef516 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon @@ -55,16 +55,34 @@ fn B() { // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000011)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000012)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000012)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000012)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} @@ -157,18 +175,39 @@ fn B() { // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst70000013)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst70000014)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(InstType))': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst70000014)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon b/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon index 3b1b891e6e42..596f0faba9cc 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon @@ -417,46 +417,109 @@ fn Foo[T:! type](p: T*) -> (T*, ()) { // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst78000026)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst7800002D)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst7800002D)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(inst78000029)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(inst7800002D)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst7800003F)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(symbolic_constant78000003)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(symbolic_constant78000003)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(inst(WitnessType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(WitnessType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(symbolic_constant78000011)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(symbolic_constant78000011)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(symbolic_constant78000009)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(symbolic_constant78000011)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(symbolic_constant78000004)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(symbolic_constant78000004)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(symbolic_constant7800000A)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(symbolic_constant78000011)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(inst(InstType))': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst78000050)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst78000050)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(SpecificFunctionType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(SpecificFunctionType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(RequireSpecificDefinitionType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(RequireSpecificDefinitionType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(symbolic_constant78000145)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(symbolic_constant7800014A)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst78000026)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(BoundMethodType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(BoundMethodType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: // CHECK:STDOUT: facet_type78000000: {} // CHECK:STDOUT: facet_type78000001: {extends interface: interface78000000} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon b/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon index 4d257b73d801..942f664a8aea 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon @@ -47,20 +47,44 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst50000010)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000010)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst5000001D)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst5000001D)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 8 +// CHECK:STDOUT: alignment: 8 // CHECK:STDOUT: 'type(inst5000001A)': // CHECK:STDOUT: value_repr: {kind: pointer, type: type(inst5000001D)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst5000002B)': // CHECK:STDOUT: value_repr: {kind: none, type: type(inst50000010)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} diff --git a/toolchain/check/testdata/interop/cpp/function/overloads.carbon b/toolchain/check/testdata/interop/cpp/function/overloads.carbon index 65ad992c35d8..5d7ec58a58a0 100644 --- a/toolchain/check/testdata/interop/cpp/function/overloads.carbon +++ b/toolchain/check/testdata/interop/cpp/function/overloads.carbon @@ -2577,8 +2577,8 @@ fn F() { // CHECK:STDOUT: %PassThreeFields__carbon_thunk.type: type = fn_type @PassThreeFields__carbon_thunk [concrete] // CHECK:STDOUT: %PassThreeFields__carbon_thunk: %PassThreeFields__carbon_thunk.type = struct_value () [concrete] // CHECK:STDOUT: %ThreeFields.elem: type = unbound_element_type %ThreeFields.942, %i32 [concrete] -// CHECK:STDOUT: %.c77: type = custom_layout_type {size=12, align=4, .a@0: %i32, .b@4: %i32, .c@8: %i32} [concrete] -// CHECK:STDOUT: %complete_type.702: = complete_type_witness %.c77 [concrete] +// CHECK:STDOUT: %.b0d: type = custom_layout_type {size=12, align=4, .a@0: %i32, .b@4: %i32, .c@8: %i32} [concrete] +// CHECK:STDOUT: %complete_type.54f: = complete_type_witness %.b0d [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: %struct_type.b.c.a: type = struct_type {.b: Core.IntLiteral, .c: Core.IntLiteral, .a: Core.IntLiteral} [concrete] @@ -2669,8 +2669,8 @@ fn F() { // CHECK:STDOUT: %i32.5: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %i32.6: type = type_literal %i32.5 [concrete = constants.%i32] // CHECK:STDOUT: %.3: %ThreeFields.elem = field_decl c, element2 [concrete] -// CHECK:STDOUT: %.4: type = custom_layout_type {size=12, align=4, .a@0: %i32, .b@4: %i32, .c@8: %i32} [concrete = constants.%.c77] -// CHECK:STDOUT: %complete_type: = complete_type_witness %.4 [concrete = constants.%complete_type.702] +// CHECK:STDOUT: %.4: type = custom_layout_type {size=12, align=4, .a@0: %i32, .b@4: %i32, .c@8: %i32} [concrete = constants.%.b0d] +// CHECK:STDOUT: %complete_type: = complete_type_witness %.4 [concrete = constants.%complete_type.54f] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: @@ -2880,8 +2880,8 @@ fn F() { // CHECK:STDOUT: %PassThreeFields__carbon_thunk.type: type = fn_type @PassThreeFields__carbon_thunk [concrete] // CHECK:STDOUT: %PassThreeFields__carbon_thunk: %PassThreeFields__carbon_thunk.type = struct_value () [concrete] // CHECK:STDOUT: %ThreeFields.elem: type = unbound_element_type %ThreeFields.942, %i32 [concrete] -// CHECK:STDOUT: %.c77: type = custom_layout_type {size=12, align=4, .a@0: %i32, .b@4: %i32, .c@8: %i32} [concrete] -// CHECK:STDOUT: %complete_type.702: = complete_type_witness %.c77 [concrete] +// CHECK:STDOUT: %.b0d: type = custom_layout_type {size=12, align=4, .a@0: %i32, .b@4: %i32, .c@8: %i32} [concrete] +// CHECK:STDOUT: %complete_type.54f: = complete_type_witness %.b0d [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { @@ -3020,8 +3020,8 @@ fn F() { // CHECK:STDOUT: %i32.5: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %i32.6: type = type_literal %i32.5 [concrete = constants.%i32] // CHECK:STDOUT: %.3: %ThreeFields.elem = field_decl c, element2 [concrete] -// CHECK:STDOUT: %.4: type = custom_layout_type {size=12, align=4, .a@0: %i32, .b@4: %i32, .c@8: %i32} [concrete = constants.%.c77] -// CHECK:STDOUT: %complete_type: = complete_type_witness %.4 [concrete = constants.%complete_type.702] +// CHECK:STDOUT: %.4: type = custom_layout_type {size=12, align=4, .a@0: %i32, .b@4: %i32, .c@8: %i32} [concrete = constants.%.b0d] +// CHECK:STDOUT: %complete_type: = complete_type_witness %.4 [concrete = constants.%complete_type.54f] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: @@ -3344,11 +3344,11 @@ fn F() { // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %B.elem.c76: type = unbound_element_type %B, %i32 [concrete] -// CHECK:STDOUT: %.e7f: type = custom_layout_type {size=12, align=4, .a@0: %A, .n@8: %i32} [concrete] -// CHECK:STDOUT: %complete_type.b39: = complete_type_witness %.e7f [concrete] +// CHECK:STDOUT: %.344: type = custom_layout_type {size=12, align=4, .a@0: %A, .n@8: %i32} [concrete] +// CHECK:STDOUT: %complete_type.e04: = complete_type_witness %.344 [concrete] // CHECK:STDOUT: %A.elem: type = unbound_element_type %A, %i32 [concrete] -// CHECK:STDOUT: %.7b6: type = custom_layout_type {size=8, align=4, .x@0: %i32, .y@4: %i32} [concrete] -// CHECK:STDOUT: %complete_type.1e0: = complete_type_witness %.7b6 [concrete] +// CHECK:STDOUT: %.171: type = custom_layout_type {size=8, align=4, .x@0: %i32, .y@4: %i32} [concrete] +// CHECK:STDOUT: %complete_type.2e6: = complete_type_witness %.171 [concrete] // CHECK:STDOUT: %ImplicitAs.type.cc7: type = generic_interface_type @ImplicitAs [concrete] // CHECK:STDOUT: %ImplicitAs.generic: %ImplicitAs.type.cc7 = struct_value () [concrete] // CHECK:STDOUT: } @@ -3395,8 +3395,8 @@ fn F() { // CHECK:STDOUT: %i32.1: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %i32.2: type = type_literal %i32.1 [concrete = constants.%i32] // CHECK:STDOUT: %.2: %B.elem.c76 = field_decl n, element1 [concrete] -// CHECK:STDOUT: %.3: type = custom_layout_type {size=12, align=4, .a@0: %A, .n@8: %i32} [concrete = constants.%.e7f] -// CHECK:STDOUT: %complete_type: = complete_type_witness %.3 [concrete = constants.%complete_type.b39] +// CHECK:STDOUT: %.3: type = custom_layout_type {size=12, align=4, .a@0: %A, .n@8: %i32} [concrete = constants.%.344] +// CHECK:STDOUT: %complete_type: = complete_type_witness %.3 [concrete = constants.%complete_type.e04] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: @@ -3412,8 +3412,8 @@ fn F() { // CHECK:STDOUT: %i32.3: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %i32.4: type = type_literal %i32.3 [concrete = constants.%i32] // CHECK:STDOUT: %.2: %A.elem = field_decl y, element1 [concrete] -// CHECK:STDOUT: %.3: type = custom_layout_type {size=8, align=4, .x@0: %i32, .y@4: %i32} [concrete = constants.%.7b6] -// CHECK:STDOUT: %complete_type: = complete_type_witness %.3 [concrete = constants.%complete_type.1e0] +// CHECK:STDOUT: %.3: type = custom_layout_type {size=8, align=4, .x@0: %i32, .y@4: %i32} [concrete = constants.%.171] +// CHECK:STDOUT: %complete_type: = complete_type_witness %.3 [concrete = constants.%complete_type.2e6] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index cc7d79b29e65..1744f7ea8f52 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -222,12 +222,14 @@ class TypeCompleter { // `type_inst` to be complete to our work list. auto AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool; - // Makes an empty value representation, which is used for types that have no - // state, such as empty structs and tuples. - auto MakeEmptyValueRepr() const -> SemIR::ValueRepr; + // Makes type info for a type with an empty value representation, which is + // used for types that have no state, such as empty structs and tuples. + auto MakeEmptyTypeInfo() const -> SemIR::CompleteTypeInfo; - // Makes a dependent value representation, which is used for symbolic types. - auto MakeDependentValueRepr(SemIR::TypeId type_id) const -> SemIR::ValueRepr; + // Makes type info for a type with a dependent value representation, which is + // used for symbolic types. + auto MakeDependentTypeInfo(SemIR::TypeId type_id) const + -> SemIR::CompleteTypeInfo; // Makes a value representation that uses pass-by-copy, copying the given // type. @@ -243,25 +245,34 @@ class TypeCompleter { SemIR::ValueRepr::NotAggregate) const -> SemIR::ValueRepr; - // Gets the value representation of a nested type, which should already be - // complete. + // Gets the type info for a nested type, which should already be complete. auto GetNestedInfo(SemIR::TypeId nested_type_id) const -> SemIR::CompleteTypeInfo; template requires(InstT::Kind.template IsAnyOf< - SemIR::AutoType, SemIR::BoolType, SemIR::BoundMethodType, - SemIR::CharLiteralType, SemIR::ErrorInst, SemIR::FacetType, - SemIR::FloatLiteralType, SemIR::FloatType, SemIR::FormType, - SemIR::IntType, SemIR::IntLiteralType, SemIR::NamespaceType, - SemIR::PatternType, SemIR::PointerType, - SemIR::RequireSpecificDefinitionType, SemIR::SpecificFunctionType, - SemIR::TypeType, SemIR::VtableType, SemIR::WitnessType>()) + SemIR::AutoType, SemIR::BoundMethodType, SemIR::CharLiteralType, + SemIR::ErrorInst, SemIR::FacetType, SemIR::FloatLiteralType, + SemIR::FormType, SemIR::IntLiteralType, SemIR::NamespaceType, + SemIR::PatternType, SemIR::RequireSpecificDefinitionType, + SemIR::SpecificFunctionType, SemIR::TypeType, SemIR::VtableType, + SemIR::WitnessType>()) auto BuildInfoForInst(SemIR::TypeId type_id, InstT /*inst*/) const -> SemIR::CompleteTypeInfo { - return {.value_repr = MakeCopyValueRepr(type_id)}; + // These types are empty at runtime but have values to copy at compile time. + return {.value_repr = MakeCopyValueRepr(type_id), + .object_layout = SemIR::ObjectLayout::Empty()}; } + auto BuildInfoForInst(SemIR::TypeId type_id, SemIR::BoolType inst) const + -> SemIR::CompleteTypeInfo; + auto BuildInfoForInst(SemIR::TypeId type_id, SemIR::PointerType inst) const + -> SemIR::CompleteTypeInfo; + auto BuildInfoForInst(SemIR::TypeId type_id, SemIR::IntType inst) const + -> SemIR::CompleteTypeInfo; + auto BuildInfoForInst(SemIR::TypeId type_id, SemIR::FloatType inst) const + -> SemIR::CompleteTypeInfo; + auto BuildStructOrTupleValueRepr(size_t num_elements, SemIR::TypeId elementwise_rep, bool same_as_object_rep) const @@ -297,7 +308,7 @@ class TypeCompleter { // - For an interface, we could use a witness. // - For an associated entity, we could use an index into the witness. // - For an unbound element, we could use an index or offset. - return {.value_repr = MakeEmptyValueRepr()}; + return MakeEmptyTypeInfo(); } auto BuildInfoForInst(SemIR::TypeId /*type_id*/, SemIR::ConstType inst) const @@ -330,7 +341,7 @@ class TypeCompleter { requires(InstT::Kind.is_symbolic_when_type()) auto BuildInfoForInst(SemIR::TypeId type_id, InstT /*inst*/) const -> SemIR::CompleteTypeInfo { - return {.value_repr = MakeDependentValueRepr(type_id)}; + return MakeDependentTypeInfo(type_id); } // Builds and returns the `CompleteTypeInfo` for the given type. All nested @@ -501,14 +512,17 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool { return true; } -auto TypeCompleter::MakeEmptyValueRepr() const -> SemIR::ValueRepr { - return {.kind = SemIR::ValueRepr::None, - .type_id = GetTupleType(*context_, {})}; +auto TypeCompleter::MakeEmptyTypeInfo() const -> SemIR::CompleteTypeInfo { + return {.value_repr = {.kind = SemIR::ValueRepr::None, + .type_id = GetTupleType(*context_, {})}, + .object_layout = SemIR::ObjectLayout::Empty()}; } -auto TypeCompleter::MakeDependentValueRepr(SemIR::TypeId type_id) const - -> SemIR::ValueRepr { - return {.kind = SemIR::ValueRepr::Dependent, .type_id = type_id}; +auto TypeCompleter::MakeDependentTypeInfo(SemIR::TypeId type_id) const + -> SemIR::CompleteTypeInfo { + return { + .value_repr = {.kind = SemIR::ValueRepr::Dependent, .type_id = type_id}, + .object_layout = SemIR::ObjectLayout()}; } auto TypeCompleter::MakeCopyValueRepr( @@ -565,7 +579,7 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, -> SemIR::CompleteTypeInfo { auto fields = context_->struct_type_fields().Get(struct_type.fields_id); if (fields.empty()) { - return {.value_repr = MakeEmptyValueRepr()}; + return MakeEmptyTypeInfo(); } // Find the value representation for each field, and construct a struct @@ -574,6 +588,7 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, value_rep_fields.reserve(fields.size()); bool same_as_object_rep = true; SemIR::ClassId abstract_class_id = SemIR::ClassId::None; + SemIR::ObjectLayout layout = SemIR::ObjectLayout::Empty(); for (auto field : fields) { auto field_type_id = context_->types().GetTypeIdForTypeInstId(field.type_inst_id); @@ -590,6 +605,8 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, !abstract_class_id.has_value()) { abstract_class_id = field_info.abstract_class_id; } + // Accumulate layout. + layout.TryAppendField(field_info.object_layout); } auto value_rep = @@ -600,6 +617,7 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, context_->struct_type_fields().AddCanonical(value_rep_fields)); return {.value_repr = BuildStructOrTupleValueRepr(fields.size(), value_rep, same_as_object_rep), + .object_layout = layout, .abstract_class_id = abstract_class_id}; } @@ -609,7 +627,7 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, // TODO: Share more code with structs. auto elements = context_->inst_blocks().Get(tuple_type.type_elements_id); if (elements.empty()) { - return {.value_repr = MakeEmptyValueRepr()}; + return MakeEmptyTypeInfo(); } // Find the value representation for each element, and construct a tuple @@ -618,6 +636,7 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, value_rep_elements.reserve(elements.size()); bool same_as_object_rep = true; SemIR::ClassId abstract_class_id = SemIR::ClassId::None; + SemIR::ObjectLayout layout = SemIR::ObjectLayout::Empty(); for (auto element_type_id : context_->types().GetBlockAsTypeIds(elements)) { auto element_info = GetNestedInfo(element_type_id); if (!element_info.value_repr.IsCopyOfObjectRepr(context_->sem_ir(), @@ -631,6 +650,8 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, !abstract_class_id.has_value()) { abstract_class_id = element_info.abstract_class_id; } + // Accumulate layout. + layout.TryAppendField(element_info.object_layout); } auto value_rep = same_as_object_rep @@ -638,17 +659,80 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, : GetTupleType(*context_, value_rep_elements); return {.value_repr = BuildStructOrTupleValueRepr(elements.size(), value_rep, same_as_object_rep), + .object_layout = layout, .abstract_class_id = abstract_class_id}; } auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, - SemIR::ArrayType /*inst*/) const + SemIR::ArrayType array_type) const -> SemIR::CompleteTypeInfo { // For arrays, it's convenient to always use a pointer representation, // even when the array has zero or one element, in order to support // indexing. + auto element_type_id = + context_->types().GetTypeIdForTypeInstId(array_type.element_type_inst_id); + auto element_info = GetNestedInfo(element_type_id); + + SemIR::ObjectLayout layout; + if (element_info.object_layout.has_value()) { + if (auto bound = context_->sem_ir().GetZExtIntValue(array_type.bound_id)) { + layout = + SemIR::ObjectLayout::ForArray(element_info.object_layout, *bound); + } + } + return {.value_repr = - MakePointerValueRepr(type_id, SemIR::ValueRepr::ObjectAggregate)}; + MakePointerValueRepr(type_id, SemIR::ValueRepr::ObjectAggregate), + .object_layout = layout}; +} + +auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, + SemIR::BoolType /*inst*/) const + -> SemIR::CompleteTypeInfo { + // TODO: Decide whether to use a size of `1` here, like `Core.Int(1)`. One + // concern is that {.a: bool, .b: bool} would be laid out with the bools at + // offsets 0 and 8, which on big-endian systems would imply the MSB stores the + // value not the LSB. Consider right-aligning fields on big-endian systems + // instead (except when bit-packing, by whatever means we support that). + return {.value_repr = MakeCopyValueRepr(type_id), + .object_layout = {.size = SemIR::ObjectSize::Bytes(1), + .alignment = SemIR::ObjectSize::Bytes(1)}}; +} + +auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, + SemIR::PointerType /*inst*/) const + -> SemIR::CompleteTypeInfo { + return {.value_repr = MakeCopyValueRepr(type_id), + .object_layout = context_->sem_ir().GetPointerLayout()}; +} + +auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, + SemIR::IntType inst) const + -> SemIR::CompleteTypeInfo { + SemIR::ObjectLayout layout; + if (auto bit_width = context_->sem_ir().GetZExtIntValue(inst.bit_width_id)) { + auto size = SemIR::ObjectSize::Bits(*bit_width); + // TODO: The upper bound for alignment here should be target-specific. + auto align = SemIR::ObjectSize::Bits( + std::clamp(llvm::PowerOf2Ceil(*bit_width), 8, 256)); + layout = {.size = size, .alignment = align}; + } + return {.value_repr = MakeCopyValueRepr(type_id), .object_layout = layout}; +} + +auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, + SemIR::FloatType inst) const + -> SemIR::CompleteTypeInfo { + SemIR::ObjectLayout layout; + if (auto bit_width = context_->sem_ir().GetZExtIntValue(inst.bit_width_id)) { + auto size = SemIR::ObjectSize::Bits(*bit_width); + // TODO: Pick a suitable alignment here. For some targets, we may want to + // use 32-bit alignment for f64 (and f80). For now we round up to a power + // of 2. + auto align = SemIR::ObjectSize::Bits(llvm::PowerOf2Ceil(*bit_width)); + layout = {.size = size, .alignment = align}; + } + return {.value_repr = MakeCopyValueRepr(type_id), .object_layout = layout}; } auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, @@ -660,8 +744,8 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, ? inst.class_id : SemIR::ClassId::None; - // The value representation of an adapter is the value representation of - // its adapted type. + // The object and value representation of an adapter are the object and value + // representation of its adapted type. if (auto adapted_type_id = class_info.GetAdaptedType(context_->sem_ir(), inst.specific_id); adapted_type_id.has_value()) { @@ -670,29 +754,35 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, return info; } // Otherwise, the value representation for a class is a pointer to the - // object representation. + // object representation, which was computed when the class was defined. + auto object_repr_type_id = + class_info.GetObjectRepr(context_->sem_ir(), inst.specific_id); // TODO: Support customized value representations for classes. // TODO: Pick a better value representation when possible. - return {.value_repr = MakePointerValueRepr( - class_info.GetObjectRepr(context_->sem_ir(), inst.specific_id), - SemIR::ValueRepr::ObjectAggregate), + return {.value_repr = MakePointerValueRepr(object_repr_type_id, + SemIR::ValueRepr::ObjectAggregate), + .object_layout = GetNestedInfo(object_repr_type_id).object_layout, .abstract_class_id = abstract_class_id}; } auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, SemIR::ConstType inst) const -> SemIR::CompleteTypeInfo { - // The value representation of `const T` is the same as that of `T`. - // Objects are not modifiable through their value representations. + // The object and value representation of `const T` are the same as those of + // `T`. Objects are not modifiable through their value representations. return GetNestedInfo(context_->types().GetTypeIdForTypeInstId(inst.inner_id)); } auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, - SemIR::CustomLayoutType /*inst*/) const + SemIR::CustomLayoutType inst) const -> SemIR::CompleteTypeInfo { // TODO: Should we support other value representations for custom layout // types? - return {.value_repr = MakePointerValueRepr(type_id)}; + const auto& layout = context_->custom_layouts().Get(inst.layout_id); + return {.value_repr = MakePointerValueRepr(type_id), + .object_layout = { + .size = layout[SemIR::CustomLayoutId::SizeIndex], + .alignment = layout[SemIR::CustomLayoutId::AlignIndex]}}; } auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, @@ -726,11 +816,10 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, // The value representation of `partial T` is the same as that of `T`. // Objects are not modifiable through their value representations. However, // `partial T` is never abstract. - return { - .value_repr = - GetNestedInfo(context_->types().GetTypeIdForTypeInstId(inst.inner_id)) - .value_repr, - .abstract_class_id = SemIR::ClassId::None}; + auto inner_type_id = context_->types().GetTypeIdForTypeInstId(inst.inner_id); + auto info = GetNestedInfo(inner_type_id); + info.abstract_class_id = SemIR::ClassId::None; + return info; } auto TypeCompleter::BuildInfoForInst( diff --git a/toolchain/driver/testdata/stdin.carbon b/toolchain/driver/testdata/stdin.carbon index e16a8f07cc80..5bc246b5a150 100644 --- a/toolchain/driver/testdata/stdin.carbon +++ b/toolchain/driver/testdata/stdin.carbon @@ -49,12 +49,24 @@ // CHECK:STDOUT: types: // CHECK:STDOUT: 'type(TypeType)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(TypeType)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(FormType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(FormType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(Error)': // CHECK:STDOUT: value_repr: {kind: copy, type: type(Error)} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: 'type(inst(NamespaceType))': // CHECK:STDOUT: value_repr: {kind: copy, type: type(inst(NamespaceType))} +// CHECK:STDOUT: object_layout: +// CHECK:STDOUT: size: 0 +// CHECK:STDOUT: alignment: 1 // CHECK:STDOUT: facet_types: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instF: {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(inst(NamespaceType))} diff --git a/toolchain/lower/aggregate.cpp b/toolchain/lower/aggregate.cpp index 25681b7ffb27..076a653ca351 100644 --- a/toolchain/lower/aggregate.cpp +++ b/toolchain/lower/aggregate.cpp @@ -7,6 +7,7 @@ #include "llvm/ADT/STLExtras.h" #include "toolchain/sem_ir/expr_info.h" #include "toolchain/sem_ir/inst.h" +#include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Lower { @@ -28,9 +29,12 @@ static auto GetElementIndex(FunctionContext::TypeInFile type, // For custom layout types, we form an array of i8 as the LLVM type, so the // offset in the type is the getelementptr index. // TODO: This offset might not fit into an `unsigned int`. - return type.file->custom_layouts().Get( + auto offset = type.file->custom_layouts().Get( custom_layout_type ->layout_id)[SemIR::CustomLayoutId::FirstFieldIndex + idx.index]; + CARBON_CHECK(offset == offset.AlignedTo(SemIR::ObjectSize::Bytes(1)), + "Element offset not byte-aligned: {0}", offset); + return offset.bytes(); } // For now, struct and tuple types map directly into LLVM struct types with diff --git a/toolchain/lower/constant.cpp b/toolchain/lower/constant.cpp index cf76a0f0aee7..86830f4367bc 100644 --- a/toolchain/lower/constant.cpp +++ b/toolchain/lower/constant.cpp @@ -110,6 +110,34 @@ class ConstantContext { SemIR::InstId current_constant_inst_id_ = SemIR::InstId::None; }; +// Get the element type at a given index from a struct. +static auto GetElementType(llvm::StructType* struct_type, int index) + -> llvm::Type* { + return struct_type->getElementType(index); +} + +// Get the element type from an array. +static auto GetElementType(llvm::ArrayType* array_type, int /*index*/) + -> llvm::Type* { + return array_type->getElementType(); +} + +// Add padding if necessary to convert the given constant to the specified, +// possibly-padded LLVM type. The type of the constant will either already be +// `llvm_type`, or will be a tail-padded type `<{llvm_type, [i8 x N]}>`. +static auto PadToType(llvm::Constant* constant, llvm::Type* llvm_type) + -> llvm::Constant* { + if (constant->getType() == llvm_type) { + return constant; + } + auto* padded = cast(llvm_type); + CARBON_CHECK(padded->getNumElements() == 2 && + padded->getElementType(0) == constant->getType(), + "Unexpected type {0} for constant {1}", *llvm_type, *constant); + return llvm::ConstantStruct::get( + padded, constant, llvm::PoisonValue::get(padded->getElementType(1))); +} + // Emits an aggregate constant of LLVM type `Type` whose elements are the // contents of `refs_id`. template @@ -119,9 +147,10 @@ static auto EmitAggregateConstant(ConstantContext& context, auto refs = context.sem_ir().inst_blocks().Get(refs_id); llvm::SmallVector elements; elements.reserve(refs.size()); - for (auto ref : refs) { + for (auto [i, ref] : llvm::enumerate(refs)) { if (auto* constant = context.GetConstant(ref)) { - elements.push_back(constant); + auto* elem_type = GetElementType(llvm_type, i); + elements.push_back(PadToType(constant, elem_type)); } else { return nullptr; } diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 107386dd86a8..8f067344f3c1 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -38,6 +38,7 @@ #include "toolchain/sem_ir/mangler.h" #include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/stringify.h" +#include "toolchain/sem_ir/type_info.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Lower { @@ -1191,6 +1192,22 @@ auto FileContext::BuildDISubprogram(const SemIR::Function& function, return subprogram; } +// Given an LLVM type, build a corresponding type with `padding_bytes` bytes of +// explicit tail padding. +static auto BuildTailPaddedType(llvm::Type* subtype, int64_t padding_bytes) + -> llvm::Type* { + if (padding_bytes == 0) { + return subtype; + } + // Build the type `<{subtype, [i8 x padding_bytes]}>`. + llvm::Type* type_with_padding[2] = { + subtype, + llvm::ArrayType::get(llvm::Type::getInt8Ty(subtype->getContext()), + padding_bytes)}; + return llvm::StructType::get(subtype->getContext(), type_with_padding, + /*isPacked=*/true); +} + // BuildTypeForInst is used to construct types for FileContext::BuildType below. // Implementations return the LLVM type for the instruction. This first overload // is the fallback handler for non-type instructions. @@ -1211,10 +1228,25 @@ static auto BuildTypeForInst(FileContext& context, InstT /*inst*/) static auto BuildTypeForInst(FileContext& context, SemIR::ArrayType inst) -> FileContext::LoweredTypes { + auto elem_type_id = context.sem_ir().types().GetTypeIdForTypeInstId( + inst.element_type_inst_id); + auto stride = context.sem_ir() + .types() + .GetCompleteTypeInfo(elem_type_id) + .object_layout.ArrayStride(); + + auto* elem_type = context.GetType(elem_type_id); + auto elem_size = SemIR::ObjectSize::Bytes( + context.llvm_module().getDataLayout().getTypeAllocSize(elem_type)); + + if (elem_size != stride) { + CARBON_CHECK(elem_size < stride, "Array element type too large"); + elem_type = BuildTailPaddedType(context.GetType(elem_type_id), + stride.bytes() - elem_size.bytes()); + } + return {llvm::ArrayType::get( - context.GetType(context.sem_ir().types().GetTypeIdForTypeInstId( - inst.element_type_inst_id)), - *context.sem_ir().GetArrayBoundValue(inst.bound_id)), + elem_type, *context.sem_ir().GetZExtIntValue(inst.bound_id)), nullptr}; } @@ -1246,9 +1278,10 @@ static auto BuildTypeForInst(FileContext& context, InstT inst) static auto BuildTypeForInst(FileContext& context, SemIR::CustomLayoutType inst) -> FileContext::LoweredTypes { auto layout = context.sem_ir().custom_layouts().Get(inst.layout_id); - return {llvm::ArrayType::get(llvm::Type::getInt8Ty(context.llvm_context()), - layout[SemIR::CustomLayoutId::SizeIndex]), - nullptr}; + return { + llvm::ArrayType::get(llvm::Type::getInt8Ty(context.llvm_context()), + layout[SemIR::CustomLayoutId::SizeIndex].bytes()), + nullptr}; } static auto BuildTypeForInst(FileContext& context, @@ -1296,16 +1329,99 @@ static auto BuildTypeForInst(FileContext& /*context*/, CARBON_FATAL("Unexpected pattern type in lowering"); } +// Builds an LLVM packed struct type whose layout matches the Carbon layout for +// an aggregate with the given field types and field layouts. +static auto BuildPackedStructType(FileContext& context, + llvm::MutableArrayRef subtypes, + llvm::ArrayRef layouts) + -> llvm::StructType* { + const auto& data_layout = context.llvm_module().getDataLayout(); + auto struct_layout = SemIR::ObjectLayout::Empty(); + auto size_so_far = SemIR::ObjectSize::Zero(); + + llvm::Type** previous_type = nullptr; + for (auto [type, layout] : llvm::zip_equal(subtypes, layouts)) { + auto offset = struct_layout.FieldOffset(layout); + // If this field has padding before it, represent that padding explicitly as + // part of the previous field. This allows us to always use GEP indexes that + // match the field indexes. + if (offset != size_so_far) { + CARBON_CHECK(previous_type, "Padding before first field?"); + CARBON_CHECK(offset > size_so_far, "Extraneous padding after field {0}", + **previous_type); + int64_t padding_bytes = offset.bytes() - struct_layout.size.bytes(); + *previous_type = BuildTailPaddedType(*previous_type, padding_bytes); + size_so_far += SemIR::ObjectSize::Bytes(padding_bytes); + CARBON_CHECK(offset == size_so_far, "Field at non-byte offset"); + } + + size_so_far += SemIR::ObjectSize::Bytes(data_layout.getTypeAllocSize(type)); + struct_layout.AppendField(layout); + previous_type = &type; + } + return llvm::StructType::get(context.llvm_context(), subtypes, + /*isPacked=*/true); +} + +// Returns whether the given LLVM layout matches the expected Carbon layout for +// an aggregate with the given field layouts. +static auto StructLayoutMatches(llvm::ArrayRef layouts, + const llvm::StructLayout& llvm_layout) -> bool { + auto struct_layout = SemIR::ObjectLayout::Empty(); + + // Check each field is at the right offset. + for (auto [i, layout] : llvm::enumerate(layouts)) { + if (static_cast(llvm_layout.getElementOffsetInBits(i)) != + struct_layout.FieldOffset(layout).bits()) { + return false; + } + struct_layout.AppendField(layout); + } + + // Treat the LLVM layout as being acceptable if it's the right byte size and + // does not require more alignment than the Carbon type. We could ignore the + // alignment, but an overaligned LLVM type will prevent the type from being + // used in non-packed structs in more situations. + return static_cast(llvm_layout.getSizeInBytes()) == + struct_layout.size.bytes() && + llvm_layout.getAlignment() <= + llvm::Align(struct_layout.alignment.bytes()); +} + +// Builds an LLVM struct type whose layout matches the Carbon layout for an +// aggregate with the given field types and field layouts. +static auto BuildStructType(FileContext& context, + llvm::MutableArrayRef subtypes, + llvm::ArrayRef layouts) + -> FileContext::LoweredTypes { + // Opportunistically try building an llvm StructType from the subtypes. If it + // has the right layout, we're done. We prefer to use a non-packed struct type + // where possible to produce a smaller LLVM IR representation for the type and + // for constant values of the type, and to improve the readability of the IR. + auto* struct_type = llvm::StructType::get(context.llvm_context(), subtypes); + if (!StructLayoutMatches( + layouts, *context.llvm_module().getDataLayout().getStructLayout( + struct_type))) { + struct_type = BuildPackedStructType(context, subtypes, layouts); + } + return {struct_type, nullptr}; +} + static auto BuildTypeForInst(FileContext& context, SemIR::StructType inst) -> FileContext::LoweredTypes { auto fields = context.sem_ir().struct_type_fields().Get(inst.fields_id); llvm::SmallVector subtypes; + llvm::SmallVector layouts; subtypes.reserve(fields.size()); + layouts.reserve(fields.size()); for (auto field : fields) { - subtypes.push_back(context.GetType( - context.sem_ir().types().GetTypeIdForTypeInstId(field.type_inst_id))); + auto type_id = + context.sem_ir().types().GetTypeIdForTypeInstId(field.type_inst_id); + subtypes.push_back(context.GetType(type_id)); + layouts.push_back( + context.sem_ir().types().GetCompleteTypeInfo(type_id).object_layout); } - return {llvm::StructType::get(context.llvm_context(), subtypes), nullptr}; + return BuildStructType(context, subtypes, layouts); } static auto BuildTypeForInst(FileContext& context, SemIR::TupleType inst) @@ -1316,11 +1432,15 @@ static auto BuildTypeForInst(FileContext& context, SemIR::TupleType inst) // type, so that may require significant special casing. auto elements = context.sem_ir().inst_blocks().Get(inst.type_elements_id); llvm::SmallVector subtypes; + llvm::SmallVector layouts; subtypes.reserve(elements.size()); + layouts.reserve(elements.size()); for (auto type_id : context.sem_ir().types().GetBlockAsTypeIds(elements)) { subtypes.push_back(context.GetType(type_id)); + layouts.push_back( + context.sem_ir().types().GetCompleteTypeInfo(type_id).object_layout); } - return {llvm::StructType::get(context.llvm_context(), subtypes), nullptr}; + return BuildStructType(context, subtypes, layouts); } static auto BuildTypeForInst(FileContext& context, SemIR::TypeType /*inst*/) @@ -1338,12 +1458,6 @@ static auto BuildTypeForInst(FileContext& context, SemIR::VtableType /*inst*/) return {llvm::Type::getVoidTy(context.llvm_context()), nullptr}; } -static auto BuildTypeForInst(FileContext& context, - SemIR::SpecificFunctionType /*inst*/) - -> FileContext::LoweredTypes { - return {llvm::PointerType::get(context.llvm_context(), 0), nullptr}; -} - template requires(InstT::Kind.template IsAnyOf< SemIR::AssociatedEntityType, SemIR::AutoType, SemIR::BoundMethodType, @@ -1353,8 +1467,8 @@ template SemIR::FunctionTypeWithSelfType, SemIR::GenericClassType, SemIR::GenericInterfaceType, SemIR::GenericNamedConstraintType, SemIR::InstType, SemIR::IntLiteralType, SemIR::NamespaceType, - SemIR::RequireSpecificDefinitionType, SemIR::UnboundElementType, - SemIR::WhereExpr, SemIR::WitnessType>()) + SemIR::RequireSpecificDefinitionType, SemIR::SpecificFunctionType, + SemIR::UnboundElementType, SemIR::WhereExpr, SemIR::WitnessType>()) static auto BuildTypeForInst(FileContext& context, InstT /*inst*/) -> FileContext::LoweredTypes { // Return an empty struct as a placeholder. @@ -1366,13 +1480,42 @@ static auto BuildTypeForInst(FileContext& context, InstT /*inst*/) auto FileContext::BuildType(SemIR::InstId inst_id) -> LoweredTypes { // Use overload resolution to select the implementation, producing compile // errors when BuildTypeForInst isn't defined for a given instruction. + LoweredTypes result; CARBON_KIND_SWITCH(sem_ir_->insts().Get(inst_id)) { -#define CARBON_SEM_IR_INST_KIND(Name) \ - case CARBON_KIND(SemIR::Name inst): { \ - return BuildTypeForInst(*this, inst); \ +#define CARBON_SEM_IR_INST_KIND(Name) \ + case CARBON_KIND(SemIR::Name inst): { \ + result = BuildTypeForInst(*this, inst); \ + break; \ } #include "toolchain/sem_ir/inst_kind.def" } + + // In debug builds, check that the type we built has the expected size. + CARBON_DCHECK([&] { + if (!result.llvm_ir_type) { + return true; + } + const auto& layout = llvm_module().getDataLayout(); + auto expected_layout = + sem_ir() + .types() + .GetCompleteTypeInfo( + sem_ir().types().GetTypeIdForTypeInstId(inst_id)) + .object_layout; + CARBON_CHECK(expected_layout.has_value()); + auto size = + SemIR::ObjectSize::Bits(layout.getTypeSizeInBits(result.llvm_ir_type)); + // Round up to byte granularity for this check, since LLVM doesn't support + // non-byte-sized packed structs. + CARBON_CHECK( + size.bytes() == expected_layout.size.bytes(), + "Lowered type {0} for {1} has unexpected size {2}, expected {3}", + *result.llvm_ir_type, sem_ir().insts().Get(inst_id), size, + expected_layout.size); + return true; + }()); + + return result; } auto FileContext::BuildGlobalVariableDecl(SemIR::VarStorage var_storage) diff --git a/toolchain/lower/function_context.cpp b/toolchain/lower/function_context.cpp index 5469b914b027..3cb023fd3f4d 100644 --- a/toolchain/lower/function_context.cpp +++ b/toolchain/lower/function_context.cpp @@ -276,6 +276,7 @@ auto FunctionContext::InitializeStorage(TypeInFile type, SemIR::InstId dest_id, if (sem_ir().constant_values().Get(source_id).is_constant()) { // When initializing from a constant, emission of the source doesn't // initialize the destination. Copy the constant value instead. + // TODO: If the type is small, emit a store rather than a memcpy. CopyValue(type, source_id, dest_id); } break; diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index 3041f6b6bc26..a6b6810109d2 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -269,7 +269,7 @@ static auto StoreArrayAsStdInitializerList(FunctionContext& context, context.GetTypeIdOfInst(array_inst_id); auto array_type = array_type_file->types().GetAs( array_type_file->types().GetObjectRepr(array_type_id)); - auto array_bound = array_type_file->GetArrayBoundValue(array_type.bound_id); + auto array_bound = array_type_file->GetZExtIntValue(array_type.bound_id); CARBON_CHECK(array_bound, "Array type with non-constant bound"); // Store the array pointer in the first element of the initializer list. diff --git a/toolchain/lower/testdata/array/iterate.carbon b/toolchain/lower/testdata/array/iterate.carbon index 5de42bb033dd..257ad89a5c2b 100644 --- a/toolchain/lower/testdata/array/iterate.carbon +++ b/toolchain/lower/testdata/array/iterate.carbon @@ -32,7 +32,7 @@ fn F() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc16_43.3.temp = alloca [6 x i32], align 4, !dbg !7 // CHECK:STDOUT: %var = alloca i32, align 4, !dbg !8 -// CHECK:STDOUT: %.loc17_19.1.temp = alloca { i32, i1 }, align 8, !dbg !8 +// CHECK:STDOUT: %.loc17_19.1.temp = alloca <{ i32, i1 }>, align 8, !dbg !8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc16_43.3.temp), !dbg !7 // CHECK:STDOUT: %.loc16_43.4.array.index = getelementptr inbounds [6 x i32], ptr %.loc16_43.3.temp, i32 0, i64 0, !dbg !7 // CHECK:STDOUT: %.loc16_43.7.array.index = getelementptr inbounds [6 x i32], ptr %.loc16_43.3.temp, i32 0, i64 1, !dbg !7 @@ -108,7 +108,7 @@ fn F() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNext.7711e6f0e1563534:Iterate.Core.355bb079bb00aa16"(ptr sret({ i32, i1 }) %return, ptr %self, ptr %cursor) #0 !dbg !49 { +// CHECK:STDOUT: define linkonce_odr void @"_CNext.7711e6f0e1563534:Iterate.Core.355bb079bb00aa16"(ptr sret(<{ i32, i1 }>) %return, ptr %self, ptr %cursor) #0 !dbg !49 { // CHECK:STDOUT: %1 = load i32, ptr %cursor, align 4, !dbg !55 // CHECK:STDOUT: %2 = icmp slt i32 %1, 6, !dbg !55 // CHECK:STDOUT: br i1 %2, label %3, label %7, !dbg !56 @@ -146,20 +146,20 @@ fn F() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.b5e6acce74484d77(ptr sret({ i32, i1 }) %return, i32 %value) #0 !dbg !83 { +// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.b5e6acce74484d77(ptr sret(<{ i32, i1 }>) %return, i32 %value) #0 !dbg !83 { // CHECK:STDOUT: call void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr %return, i32 %value), !dbg !88 // CHECK:STDOUT: ret void, !dbg !89 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.b5e6acce74484d77(ptr sret({ i32, i1 }) %return) #0 !dbg !90 { +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.b5e6acce74484d77(ptr sret(<{ i32, i1 }>) %return) #0 !dbg !90 { // CHECK:STDOUT: call void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr %return), !dbg !93 // CHECK:STDOUT: ret void, !dbg !94 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr i1 @"_CHas.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr %value) #0 !dbg !95 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %value, i32 0, i32 1, !dbg !98 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %value, i32 0, i32 1, !dbg !98 // CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !98 // CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !98 // CHECK:STDOUT: ret i1 %2, !dbg !99 @@ -167,7 +167,7 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr i32 @"_CGet.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr %value) #0 !dbg !100 { -// CHECK:STDOUT: %value1 = getelementptr inbounds nuw { i32, i1 }, ptr %value, i32 0, i32 0, !dbg !103 +// CHECK:STDOUT: %value1 = getelementptr inbounds nuw <{ i32, i1 }>, ptr %value, i32 0, i32 0, !dbg !103 // CHECK:STDOUT: %1 = load i32, ptr %value1, align 4, !dbg !103 // CHECK:STDOUT: ret i32 %1, !dbg !104 // CHECK:STDOUT: } @@ -182,17 +182,17 @@ fn F() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !113 { -// CHECK:STDOUT: %value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 0, !dbg !116 +// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !113 { +// CHECK:STDOUT: %value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 0, !dbg !116 // CHECK:STDOUT: store i32 %self, ptr %value, align 4, !dbg !116 -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !117 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !117 // CHECK:STDOUT: store i8 1, ptr %has_value, align 1, !dbg !117 // CHECK:STDOUT: ret void, !dbg !118 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr sret({ i32, i1 }) %return) #0 !dbg !119 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !120 +// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr sret(<{ i32, i1 }>) %return) #0 !dbg !119 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !120 // CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !120 // CHECK:STDOUT: ret void, !dbg !121 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/array/layout.carbon b/toolchain/lower/testdata/array/layout.carbon new file mode 100644 index 000000000000..9d385f8ce732 --- /dev/null +++ b/toolchain/lower/testdata/array/layout.carbon @@ -0,0 +1,147 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/array/layout.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/array/layout.carbon + +// --- element_tail_padding.carbon +library "[[@TEST_NAME]]"; + +fn Check(p: i32*); + +fn Test() { + // We insert tail padding after each element (including the last) to keep the + // array elements properly aligned and to ensure that a past-the-end address + // points within the overall object. + var v: array({.a: ()*, .b: i32}, 3); + Check(&v[0].b); + Check(&v[1].b); + Check(&v[2].b); +} + +// --- constant_with_padding.carbon +library "[[@TEST_NAME]]"; + +var c: array(Core.Int(257), 5) = (1, 2, 3, 4, 5); + +// CHECK:STDOUT: ; ModuleID = 'element_tail_padding.carbon' +// CHECK:STDOUT: source_filename = "element_tail_padding.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CCheck.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CTest.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %v.var = alloca [3 x <{ <{ ptr, i32 }>, [4 x i8] }>], align 8, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %v.var), !dbg !7 +// CHECK:STDOUT: %.loc10_13.array.index = getelementptr inbounds [3 x <{ <{ ptr, i32 }>, [4 x i8] }>], ptr %v.var, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: %.loc10_14.b = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %.loc10_13.array.index, i32 0, i32 1, !dbg !8 +// CHECK:STDOUT: call void @_CCheck.Main(ptr %.loc10_14.b), !dbg !9 +// CHECK:STDOUT: %.loc11_13.array.index = getelementptr inbounds [3 x <{ <{ ptr, i32 }>, [4 x i8] }>], ptr %v.var, i32 0, i32 1, !dbg !10 +// CHECK:STDOUT: %.loc11_14.b = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %.loc11_13.array.index, i32 0, i32 1, !dbg !10 +// CHECK:STDOUT: call void @_CCheck.Main(ptr %.loc11_14.b), !dbg !11 +// CHECK:STDOUT: %.loc12_13.array.index = getelementptr inbounds [3 x <{ <{ ptr, i32 }>, [4 x i8] }>], ptr %v.var, i32 0, i32 2, !dbg !12 +// CHECK:STDOUT: %.loc12_14.b = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %.loc12_13.array.index, i32 0, i32 1, !dbg !12 +// CHECK:STDOUT: call void @_CCheck.Main(ptr %.loc12_14.b), !dbg !13 +// CHECK:STDOUT: call void @"_COp.876098026a9fba08:core.Destroy.Core"(ptr %v.var), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !14 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %self) #0 !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.5e9a928496027250:core.Destroy.Core"(ptr %self) #0 !dbg !22 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !28 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.876098026a9fba08:core.Destroy.Core"(ptr %self) #0 !dbg !29 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "element_tail_padding.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "Test", linkageName: "_CTest.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 9, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 10, column: 10, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 10, column: 3, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 11, column: 10, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 11, column: 3, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 12, column: 10, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 12, column: 3, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "Op", linkageName: "_COp.7e389eab4a7e5487:core.Destroy.Core", scope: null, file: !3, line: 9, type: !16, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !19) +// CHECK:STDOUT: !16 = !DISubroutineType(types: !17) +// CHECK:STDOUT: !17 = !{null, !18} +// CHECK:STDOUT: !18 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !19 = !{!20} +// CHECK:STDOUT: !20 = !DILocalVariable(arg: 1, scope: !15, type: !18) +// CHECK:STDOUT: !21 = !DILocation(line: 9, column: 3, scope: !15) +// CHECK:STDOUT: !22 = distinct !DISubprogram(name: "Op", linkageName: "_COp.5e9a928496027250:core.Destroy.Core", scope: null, file: !3, line: 9, type: !23, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !26) +// CHECK:STDOUT: !23 = !DISubroutineType(types: !24) +// CHECK:STDOUT: !24 = !{null, !25} +// CHECK:STDOUT: !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !26 = !{!27} +// CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !22, type: !25) +// CHECK:STDOUT: !28 = !DILocation(line: 9, column: 3, scope: !22) +// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "Op", linkageName: "_COp.876098026a9fba08:core.Destroy.Core", scope: null, file: !3, line: 9, type: !23, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !30) +// CHECK:STDOUT: !30 = !{!31} +// CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !29, type: !25) +// CHECK:STDOUT: !32 = !DILocation(line: 9, column: 3, scope: !29) +// CHECK:STDOUT: ; ModuleID = 'constant_with_padding.carbon' +// CHECK:STDOUT: source_filename = "constant_with_padding.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: @_Cc.Main = global [5 x <{ i257, [28 x i8] }>] zeroinitializer +// CHECK:STDOUT: @array.loc3_1 = internal constant [5 x <{ i257, [28 x i8] }>] [<{ i257, [28 x i8] }> <{ i257 1, [28 x i8] poison }>, <{ i257, [28 x i8] }> <{ i257 2, [28 x i8] poison }>, <{ i257, [28 x i8] }> <{ i257 3, [28 x i8] poison }>, <{ i257, [28 x i8] }> <{ i257 4, [28 x i8] poison }>, <{ i257, [28 x i8] }> <{ i257 5, [28 x i8] poison }>] +// CHECK:STDOUT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @_C__global_init.Main, ptr null }] +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define internal void @_C__global_init.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 @_Cc.Main, ptr align 1 @array.loc3_1, i64 320, i1 false), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.memcpy.p0.p0.i64(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i64, i1 immarg) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "constant_with_padding.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "__global_init", linkageName: "_C__global_init.Main", scope: null, file: !3, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 3, column: 1, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 0, scope: !4) diff --git a/toolchain/lower/testdata/class/field.carbon b/toolchain/lower/testdata/class/field.carbon index 423334e852ee..8c0136d64dfd 100644 --- a/toolchain/lower/testdata/class/field.carbon +++ b/toolchain/lower/testdata/class/field.carbon @@ -250,12 +250,12 @@ fn Run() { // CHECK:STDOUT: define void @main() #0 !dbg !4 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %o.var = alloca { i32 }, align 8, !dbg !7 -// CHECK:STDOUT: %_.var = alloca { ptr, { i32 } }, align 8, !dbg !8 +// CHECK:STDOUT: %_.var = alloca <{ ptr, { i32 } }>, align 8, !dbg !8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %o.var), !dbg !7 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !8 -// CHECK:STDOUT: %.loc17_40.2.a = getelementptr inbounds nuw { ptr, { i32 } }, ptr %_.var, i32 0, i32 0, !dbg !9 +// CHECK:STDOUT: %.loc17_40.2.a = getelementptr inbounds nuw <{ ptr, { i32 } }>, ptr %_.var, i32 0, i32 0, !dbg !9 // CHECK:STDOUT: store ptr %o.var, ptr %.loc17_40.2.a, align 8, !dbg !9 -// CHECK:STDOUT: %.loc17_40.4.b = getelementptr inbounds nuw { ptr, { i32 } }, ptr %_.var, i32 0, i32 1, !dbg !9 +// CHECK:STDOUT: %.loc17_40.4.b = getelementptr inbounds nuw <{ ptr, { i32 } }>, ptr %_.var, i32 0, i32 1, !dbg !9 // CHECK:STDOUT: %.loc17_39.3.v = getelementptr inbounds nuw { i32 }, ptr %.loc17_40.4.b, i32 0, i32 0, !dbg !10 // CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %.loc17_40.4.b, ptr align 4 @Other.val.loc17_40.5, i64 4, i1 false), !dbg !9 // CHECK:STDOUT: call void @"_COp.6354d5454c7bf77d:core.Destroy.Core"(ptr %_.var), !dbg !8 diff --git a/toolchain/lower/testdata/class/virtual.carbon b/toolchain/lower/testdata/class/virtual.carbon index 95541235d16c..01d4cc720951 100644 --- a/toolchain/lower/testdata/class/virtual.carbon +++ b/toolchain/lower/testdata/class/virtual.carbon @@ -474,7 +474,7 @@ fn Make() { // CHECK:STDOUT: source_filename = "member_init.carbon" // CHECK:STDOUT: // CHECK:STDOUT: @"_CBase.MemberInit.$vtable" = unnamed_addr constant [1 x i32] [i32 trunc (i64 sub (i64 ptrtoint (ptr @_CFn.Base.MemberInit to i64), i64 ptrtoint (ptr @"_CBase.MemberInit.$vtable" to i64)) to i32)] -// CHECK:STDOUT: @Base.val.loc13_3 = internal constant { ptr, i32 } { ptr @"_CBase.MemberInit.$vtable", i32 3 } +// CHECK:STDOUT: @Base.val.loc13_3 = internal constant <{ ptr, i32 }> <{ ptr @"_CBase.MemberInit.$vtable", i32 3 }> // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CFn.Base.MemberInit(ptr %self) #0 !dbg !4 { @@ -486,22 +486,22 @@ fn Make() { // CHECK:STDOUT: define void @_CFn.MemberInit() #0 !dbg !11 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %i.var = alloca i32, align 4, !dbg !14 -// CHECK:STDOUT: %v.var = alloca { ptr, i32 }, align 8, !dbg !15 -// CHECK:STDOUT: %_.var = alloca { ptr, i32 }, align 8, !dbg !16 +// CHECK:STDOUT: %v.var = alloca <{ ptr, i32 }>, align 8, !dbg !15 +// CHECK:STDOUT: %_.var = alloca <{ ptr, i32 }>, align 8, !dbg !16 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %i.var), !dbg !14 // CHECK:STDOUT: store i32 3, ptr %i.var, align 4, !dbg !14 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %v.var), !dbg !15 -// CHECK:STDOUT: %.loc11_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 0, !dbg !17 +// CHECK:STDOUT: %.loc11_24.2.vptr = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %v.var, i32 0, i32 0, !dbg !17 // CHECK:STDOUT: %.loc11_23 = load i32, ptr %i.var, align 4, !dbg !18 -// CHECK:STDOUT: %.loc11_24.4.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !17 +// CHECK:STDOUT: %.loc11_24.4.m = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %v.var, i32 0, i32 1, !dbg !17 // CHECK:STDOUT: store i32 %.loc11_23, ptr %.loc11_24.4.m, align 4, !dbg !17 // CHECK:STDOUT: store ptr @"_CBase.MemberInit.$vtable", ptr %.loc11_24.2.vptr, align 8, !dbg !17 -// CHECK:STDOUT: %.loc12_4.m = getelementptr inbounds nuw { ptr, i32 }, ptr %v.var, i32 0, i32 1, !dbg !19 +// CHECK:STDOUT: %.loc12_4.m = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %v.var, i32 0, i32 1, !dbg !19 // CHECK:STDOUT: store i32 5, ptr %.loc12_4.m, align 4, !dbg !19 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !16 -// CHECK:STDOUT: %.loc13_24.2.vptr = getelementptr inbounds nuw { ptr, i32 }, ptr %_.var, i32 0, i32 0, !dbg !20 -// CHECK:STDOUT: %.loc13_24.5.m = getelementptr inbounds nuw { ptr, i32 }, ptr %_.var, i32 0, i32 1, !dbg !20 -// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %_.var, ptr align 8 @Base.val.loc13_3, i64 16, i1 false), !dbg !16 +// CHECK:STDOUT: %.loc13_24.2.vptr = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %_.var, i32 0, i32 0, !dbg !20 +// CHECK:STDOUT: %.loc13_24.5.m = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %_.var, i32 0, i32 1, !dbg !20 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %_.var, ptr align 1 @Base.val.loc13_3, i64 12, i1 false), !dbg !16 // CHECK:STDOUT: call void @"_COp.dbf7676be30a7aa8:core.Destroy.Core"(ptr %_.var), !dbg !16 // CHECK:STDOUT: call void @"_COp.dbf7676be30a7aa8:core.Destroy.Core"(ptr %v.var), !dbg !15 // CHECK:STDOUT: call void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %i.var), !dbg !14 diff --git a/toolchain/lower/testdata/for/bindings.carbon b/toolchain/lower/testdata/for/bindings.carbon index 10141c4b2680..b9b61e6dd1f7 100644 --- a/toolchain/lower/testdata/for/bindings.carbon +++ b/toolchain/lower/testdata/for/bindings.carbon @@ -43,7 +43,7 @@ fn For() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %r.var = alloca {}, align 8, !dbg !7 // CHECK:STDOUT: %var = alloca {}, align 8, !dbg !8 -// CHECK:STDOUT: %.loc29_33.1.temp = alloca { { i32, i32 }, i1 }, align 8, !dbg !8 +// CHECK:STDOUT: %.loc29_33.1.temp = alloca <{ { i32, i32 }, i1 }>, align 8, !dbg !8 // CHECK:STDOUT: %n.var = alloca i32, align 4, !dbg !9 // CHECK:STDOUT: %.loc29_33.8.temp = alloca { i32, i32 }, align 8, !dbg !8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %r.var), !dbg !7 @@ -133,7 +133,7 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNext.EmptyRange.1ccc388edee5a786.Main:Iterate.Core.8d745d19e7d6c4bd"(ptr sret({ { i32, i32 }, i1 }) %return, ptr %self, ptr %_) #0 !dbg !51 { +// CHECK:STDOUT: define linkonce_odr void @"_CNext.EmptyRange.1ccc388edee5a786.Main:Iterate.Core.8d745d19e7d6c4bd"(ptr sret(<{ { i32, i32 }, i1 }>) %return, ptr %self, ptr %_) #0 !dbg !51 { // CHECK:STDOUT: entry: // CHECK:STDOUT: call void @_CNone.Optional.Core.6b64456bb8003a40(ptr %return), !dbg !57 // CHECK:STDOUT: ret void, !dbg !58 @@ -152,14 +152,14 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.6b64456bb8003a40(ptr sret({ { i32, i32 }, i1 }) %return) #0 !dbg !72 { +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.6b64456bb8003a40(ptr sret(<{ { i32, i32 }, i1 }>) %return) #0 !dbg !72 { // CHECK:STDOUT: call void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.8d745d19e7d6c4bd"(ptr %return), !dbg !75 // CHECK:STDOUT: ret void, !dbg !76 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr i1 @"_CHas.3e8267224c5dc9c2:OptionalStorage.Core.8d745d19e7d6c4bd"(ptr %value) #0 !dbg !77 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %value, i32 0, i32 1, !dbg !80 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ { i32, i32 }, i1 }>, ptr %value, i32 0, i32 1, !dbg !80 // CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !80 // CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !80 // CHECK:STDOUT: ret i1 %2, !dbg !81 @@ -167,14 +167,14 @@ fn For() { // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr void @"_CGet.3e8267224c5dc9c2:OptionalStorage.Core.8d745d19e7d6c4bd"(ptr sret({ i32, i32 }) %return, ptr %value) #0 !dbg !82 { -// CHECK:STDOUT: %value1 = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %value, i32 0, i32 0, !dbg !85 +// CHECK:STDOUT: %value1 = getelementptr inbounds nuw <{ { i32, i32 }, i1 }>, ptr %value, i32 0, i32 0, !dbg !85 // CHECK:STDOUT: call void @"_COp.e4daa70d026fdea2:Copy.Core.3e36085cb869f630"(ptr %return, ptr %value1), !dbg !85 // CHECK:STDOUT: ret void, !dbg !86 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.8d745d19e7d6c4bd"(ptr sret({ { i32, i32 }, i1 }) %return) #0 !dbg !87 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { { i32, i32 }, i1 }, ptr %return, i32 0, i32 1, !dbg !88 +// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.8d745d19e7d6c4bd"(ptr sret(<{ { i32, i32 }, i1 }>) %return) #0 !dbg !87 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ { i32, i32 }, i1 }>, ptr %return, i32 0, i32 1, !dbg !88 // CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !88 // CHECK:STDOUT: ret void, !dbg !89 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/for/break_continue.carbon b/toolchain/lower/testdata/for/break_continue.carbon index 8c16f03fd956..0c7557b61299 100644 --- a/toolchain/lower/testdata/for/break_continue.carbon +++ b/toolchain/lower/testdata/for/break_continue.carbon @@ -38,7 +38,7 @@ fn For() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc20_32.1.temp = alloca { i32, i32 }, align 8, !dbg !7 // CHECK:STDOUT: %var = alloca i32, align 4, !dbg !8 -// CHECK:STDOUT: %.loc20_33.1.temp = alloca { i32, i1 }, align 8, !dbg !8 +// CHECK:STDOUT: %.loc20_33.1.temp = alloca <{ i32, i1 }>, align 8, !dbg !8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc20_32.1.temp), !dbg !7 // CHECK:STDOUT: call void @_CRange.Core(ptr %.loc20_32.1.temp, i32 100), !dbg !7 // CHECK:STDOUT: %IntRange.as.Iterate.impl.NewCursor.call = call i32 @"_CNewCursor.IntRange.9452f4c51951679b.Core:Iterate.Core.be1e879c1ad406d8"(ptr %.loc20_32.1.temp), !dbg !8 @@ -133,7 +133,7 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNext.IntRange.9452f4c51951679b.Core:Iterate.Core.be1e879c1ad406d8"(ptr sret({ i32, i1 }) %return, ptr %self, ptr %cursor) #0 !dbg !60 { +// CHECK:STDOUT: define linkonce_odr void @"_CNext.IntRange.9452f4c51951679b.Core:Iterate.Core.be1e879c1ad406d8"(ptr sret(<{ i32, i1 }>) %return, ptr %self, ptr %cursor) #0 !dbg !60 { // CHECK:STDOUT: %1 = alloca i32, align 4, !dbg !66 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %1), !dbg !66 // CHECK:STDOUT: %2 = load i32, ptr %cursor, align 4, !dbg !67 @@ -178,20 +178,20 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.a0986cb4211ec3fb(ptr sret({ i32, i1 }) %return, i32 %value) #0 !dbg !96 { +// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.a0986cb4211ec3fb(ptr sret(<{ i32, i1 }>) %return, i32 %value) #0 !dbg !96 { // CHECK:STDOUT: call void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr %return, i32 %value), !dbg !101 // CHECK:STDOUT: ret void, !dbg !102 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.a0986cb4211ec3fb(ptr sret({ i32, i1 }) %return) #0 !dbg !103 { +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.a0986cb4211ec3fb(ptr sret(<{ i32, i1 }>) %return) #0 !dbg !103 { // CHECK:STDOUT: call void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr %return), !dbg !106 // CHECK:STDOUT: ret void, !dbg !107 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr i1 @"_CHas.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr %value) #0 !dbg !108 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %value, i32 0, i32 1, !dbg !111 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %value, i32 0, i32 1, !dbg !111 // CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !111 // CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !111 // CHECK:STDOUT: ret i1 %2, !dbg !112 @@ -199,7 +199,7 @@ fn For() { // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr i32 @"_CGet.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr %value) #0 !dbg !113 { -// CHECK:STDOUT: %value1 = getelementptr inbounds nuw { i32, i1 }, ptr %value, i32 0, i32 0, !dbg !116 +// CHECK:STDOUT: %value1 = getelementptr inbounds nuw <{ i32, i1 }>, ptr %value, i32 0, i32 0, !dbg !116 // CHECK:STDOUT: %1 = load i32, ptr %value1, align 4, !dbg !116 // CHECK:STDOUT: ret i32 %1, !dbg !117 // CHECK:STDOUT: } @@ -214,17 +214,17 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !126 { -// CHECK:STDOUT: %value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 0, !dbg !129 +// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !126 { +// CHECK:STDOUT: %value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 0, !dbg !129 // CHECK:STDOUT: store i32 %self, ptr %value, align 4, !dbg !129 -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !130 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !130 // CHECK:STDOUT: store i8 1, ptr %has_value, align 1, !dbg !130 // CHECK:STDOUT: ret void, !dbg !131 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr sret({ i32, i1 }) %return) #0 !dbg !132 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !133 +// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr sret(<{ i32, i1 }>) %return) #0 !dbg !132 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !133 // CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !133 // CHECK:STDOUT: ret void, !dbg !134 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/for/for.carbon b/toolchain/lower/testdata/for/for.carbon index 60e7a1a1253d..dddd3a6d37a0 100644 --- a/toolchain/lower/testdata/for/for.carbon +++ b/toolchain/lower/testdata/for/for.carbon @@ -38,7 +38,7 @@ fn For() { // CHECK:STDOUT: entry: // CHECK:STDOUT: %.loc21_32.1.temp = alloca { i32, i32 }, align 8, !dbg !7 // CHECK:STDOUT: %var = alloca i32, align 4, !dbg !8 -// CHECK:STDOUT: %.loc21_33.1.temp = alloca { i32, i1 }, align 8, !dbg !8 +// CHECK:STDOUT: %.loc21_33.1.temp = alloca <{ i32, i1 }>, align 8, !dbg !8 // CHECK:STDOUT: call void @_CF.Main(), !dbg !9 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc21_32.1.temp), !dbg !7 // CHECK:STDOUT: call void @_CRange.Core(ptr %.loc21_32.1.temp, i32 100), !dbg !7 @@ -121,7 +121,7 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNext.IntRange.9452f4c51951679b.Core:Iterate.Core.be1e879c1ad406d8"(ptr sret({ i32, i1 }) %return, ptr %self, ptr %cursor) #0 !dbg !56 { +// CHECK:STDOUT: define linkonce_odr void @"_CNext.IntRange.9452f4c51951679b.Core:Iterate.Core.be1e879c1ad406d8"(ptr sret(<{ i32, i1 }>) %return, ptr %self, ptr %cursor) #0 !dbg !56 { // CHECK:STDOUT: %1 = alloca i32, align 4, !dbg !62 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %1), !dbg !62 // CHECK:STDOUT: %2 = load i32, ptr %cursor, align 4, !dbg !63 @@ -166,20 +166,20 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.a0986cb4211ec3fb(ptr sret({ i32, i1 }) %return, i32 %value) #0 !dbg !92 { +// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.a0986cb4211ec3fb(ptr sret(<{ i32, i1 }>) %return, i32 %value) #0 !dbg !92 { // CHECK:STDOUT: call void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr %return, i32 %value), !dbg !97 // CHECK:STDOUT: ret void, !dbg !98 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.a0986cb4211ec3fb(ptr sret({ i32, i1 }) %return) #0 !dbg !99 { +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.a0986cb4211ec3fb(ptr sret(<{ i32, i1 }>) %return) #0 !dbg !99 { // CHECK:STDOUT: call void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr %return), !dbg !102 // CHECK:STDOUT: ret void, !dbg !103 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr i1 @"_CHas.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr %value) #0 !dbg !104 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %value, i32 0, i32 1, !dbg !107 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %value, i32 0, i32 1, !dbg !107 // CHECK:STDOUT: %1 = load i8, ptr %has_value, align 1, !dbg !107 // CHECK:STDOUT: %2 = trunc i8 %1 to i1, !dbg !107 // CHECK:STDOUT: ret i1 %2, !dbg !108 @@ -187,7 +187,7 @@ fn For() { // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define linkonce_odr i32 @"_CGet.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr %value) #0 !dbg !109 { -// CHECK:STDOUT: %value1 = getelementptr inbounds nuw { i32, i1 }, ptr %value, i32 0, i32 0, !dbg !112 +// CHECK:STDOUT: %value1 = getelementptr inbounds nuw <{ i32, i1 }>, ptr %value, i32 0, i32 0, !dbg !112 // CHECK:STDOUT: %1 = load i32, ptr %value1, align 4, !dbg !112 // CHECK:STDOUT: ret i32 %1, !dbg !113 // CHECK:STDOUT: } @@ -202,17 +202,17 @@ fn For() { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !122 { -// CHECK:STDOUT: %value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 0, !dbg !125 +// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !122 { +// CHECK:STDOUT: %value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 0, !dbg !125 // CHECK:STDOUT: store i32 %self, ptr %value, align 4, !dbg !125 -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !126 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !126 // CHECK:STDOUT: store i8 1, ptr %has_value, align 1, !dbg !126 // CHECK:STDOUT: ret void, !dbg !127 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr sret({ i32, i1 }) %return) #0 !dbg !128 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !129 +// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.0b1ea4541e2057b5"(ptr sret(<{ i32, i1 }>) %return) #0 !dbg !128 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !129 // CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !129 // CHECK:STDOUT: ret void, !dbg !130 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/primitives/optional.carbon b/toolchain/lower/testdata/primitives/optional.carbon index 3eeab2b608e5..04c6eee125bf 100644 --- a/toolchain/lower/testdata/primitives/optional.carbon +++ b/toolchain/lower/testdata/primitives/optional.carbon @@ -41,7 +41,7 @@ fn AddOrRemoveConst(a: i32, b: const i32) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define void @_CConvert.Main(ptr sret({ i32, i1 }) %return, ptr %o) #0 !dbg !11 { +// CHECK:STDOUT: define void @_CConvert.Main(ptr sret(<{ i32, i1 }>) %return, ptr %o) #0 !dbg !11 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %Optional.HasValue.call = call i1 @_CHasValue.Optional.Core.03e3348579103d79(ptr %o), !dbg !17 // CHECK:STDOUT: br i1 %Optional.HasValue.call, label %if.then, label %if.else, !dbg !18 @@ -60,14 +60,14 @@ fn AddOrRemoveConst(a: i32, b: const i32) { // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define void @_CAddOrRemoveConst.Main(i32 %a, i32 %b) #0 !dbg !24 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %_.var.loc24 = alloca { i32, i1 }, align 8, !dbg !30 -// CHECK:STDOUT: %_.var.loc25 = alloca { i32, i1 }, align 8, !dbg !31 -// CHECK:STDOUT: %_.var.loc26 = alloca { i32, i1 }, align 8, !dbg !32 -// CHECK:STDOUT: %_.var.loc27 = alloca { i32, i1 }, align 8, !dbg !33 -// CHECK:STDOUT: %_.var.loc28 = alloca { i32, i1 }, align 8, !dbg !34 -// CHECK:STDOUT: %_.var.loc29 = alloca { i32, i1 }, align 8, !dbg !35 -// CHECK:STDOUT: %_.var.loc30 = alloca { i32, i1 }, align 8, !dbg !36 -// CHECK:STDOUT: %_.var.loc31 = alloca { i32, i1 }, align 8, !dbg !37 +// CHECK:STDOUT: %_.var.loc24 = alloca <{ i32, i1 }>, align 8, !dbg !30 +// CHECK:STDOUT: %_.var.loc25 = alloca <{ i32, i1 }>, align 8, !dbg !31 +// CHECK:STDOUT: %_.var.loc26 = alloca <{ i32, i1 }>, align 8, !dbg !32 +// CHECK:STDOUT: %_.var.loc27 = alloca <{ i32, i1 }>, align 8, !dbg !33 +// CHECK:STDOUT: %_.var.loc28 = alloca <{ i32, i1 }>, align 8, !dbg !34 +// CHECK:STDOUT: %_.var.loc29 = alloca <{ i32, i1 }>, align 8, !dbg !35 +// CHECK:STDOUT: %_.var.loc30 = alloca <{ i32, i1 }>, align 8, !dbg !36 +// CHECK:STDOUT: %_.var.loc31 = alloca <{ i32, i1 }>, align 8, !dbg !37 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc24), !dbg !30 // CHECK:STDOUT: call void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.6b1f19368d09e579"(ptr %_.var.loc24, i32 %a), !dbg !30 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var.loc25), !dbg !31 @@ -174,13 +174,13 @@ fn AddOrRemoveConst(a: i32, b: const i32) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.6b1f19368d09e579"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !96 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.6b1f19368d09e579"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !96 { // CHECK:STDOUT: call void @"_CConvert.90961d7b1ce4f089:OptionalAs.0e326e799dad0c64.Core.30ced37eb1e63547"(ptr %return, i32 %self), !dbg !101 // CHECK:STDOUT: ret void, !dbg !102 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.30ced37eb1e63547(ptr sret({ i32, i1 }) %return) #0 !dbg !103 { +// CHECK:STDOUT: define linkonce_odr void @_CNone.Optional.Core.30ced37eb1e63547(ptr sret(<{ i32, i1 }>) %return) #0 !dbg !103 { // CHECK:STDOUT: call void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr %return), !dbg !106 // CHECK:STDOUT: ret void, !dbg !107 // CHECK:STDOUT: } @@ -189,43 +189,43 @@ fn AddOrRemoveConst(a: i32, b: const i32) { // CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.a44fce96e16342e7:ImplicitAs.ad22d1bbc0605210.Core.77b263a5fd240ddf"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !108 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.a44fce96e16342e7:ImplicitAs.ad22d1bbc0605210.Core.77b263a5fd240ddf"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !108 { // CHECK:STDOUT: call void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.6b1f19368d09e579"(ptr %return, i32 %self), !dbg !112 // CHECK:STDOUT: ret void, !dbg !113 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.787d589b54a95071"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !114 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.787d589b54a95071"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !114 { // CHECK:STDOUT: call void @"_CConvert.3667eb502d1ddfa9:OptionalAs.c7a50af9bdd61b43.Core.5d35ad54738af8e4"(ptr %return, i32 %self), !dbg !117 // CHECK:STDOUT: ret void, !dbg !118 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.a44fce96e16342e7:ImplicitAs.ad22d1bbc0605210.Core.25fb32d66f981ccd"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !119 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.a44fce96e16342e7:ImplicitAs.ad22d1bbc0605210.Core.25fb32d66f981ccd"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !119 { // CHECK:STDOUT: call void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.787d589b54a95071"(ptr %return, i32 %self), !dbg !122 // CHECK:STDOUT: ret void, !dbg !123 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.17f42c13d842b71d:ImplicitAs.eb057aa32837c84e.Core.77b263a5fd240ddf"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !124 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.17f42c13d842b71d:ImplicitAs.eb057aa32837c84e.Core.77b263a5fd240ddf"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !124 { // CHECK:STDOUT: call void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.6b1f19368d09e579"(ptr %return, i32 %self), !dbg !127 // CHECK:STDOUT: ret void, !dbg !128 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.17f42c13d842b71d:ImplicitAs.eb057aa32837c84e.Core.693532dcb04bf6dc"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !129 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.17f42c13d842b71d:ImplicitAs.eb057aa32837c84e.Core.693532dcb04bf6dc"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !129 { // CHECK:STDOUT: call void @"_CConvert.a44fce96e16342e7:ImplicitAs.ad22d1bbc0605210.Core.77b263a5fd240ddf"(ptr %return, i32 %self), !dbg !132 // CHECK:STDOUT: ret void, !dbg !133 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.17f42c13d842b71d:ImplicitAs.eb057aa32837c84e.Core.25fb32d66f981ccd"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !134 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.17f42c13d842b71d:ImplicitAs.eb057aa32837c84e.Core.25fb32d66f981ccd"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !134 { // CHECK:STDOUT: call void @"_CConvert.e5cf8fcbb4feaae2:ImplicitAs.0f95c9e18c91e00a.Core.787d589b54a95071"(ptr %return, i32 %self), !dbg !137 // CHECK:STDOUT: ret void, !dbg !138 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.17f42c13d842b71d:ImplicitAs.eb057aa32837c84e.Core.1c991c9fdfba671f"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !139 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.17f42c13d842b71d:ImplicitAs.eb057aa32837c84e.Core.1c991c9fdfba671f"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !139 { // CHECK:STDOUT: call void @"_CConvert.a44fce96e16342e7:ImplicitAs.ad22d1bbc0605210.Core.25fb32d66f981ccd"(ptr %return, i32 %self), !dbg !142 // CHECK:STDOUT: ret void, !dbg !143 // CHECK:STDOUT: } @@ -243,20 +243,20 @@ fn AddOrRemoveConst(a: i32, b: const i32) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.90961d7b1ce4f089:OptionalAs.0e326e799dad0c64.Core.30ced37eb1e63547"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !154 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.90961d7b1ce4f089:OptionalAs.0e326e799dad0c64.Core.30ced37eb1e63547"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !154 { // CHECK:STDOUT: call void @_CSome.Optional.Core.30ced37eb1e63547(ptr %return, i32 %self), !dbg !157 // CHECK:STDOUT: ret void, !dbg !158 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr sret({ i32, i1 }) %return) #0 !dbg !159 { -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !160 +// CHECK:STDOUT: define linkonce_odr void @"_CNone.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr sret(<{ i32, i1 }>) %return) #0 !dbg !159 { +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !160 // CHECK:STDOUT: store i8 0, ptr %has_value, align 1, !dbg !160 // CHECK:STDOUT: ret void, !dbg !161 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CConvert.3667eb502d1ddfa9:OptionalAs.c7a50af9bdd61b43.Core.5d35ad54738af8e4"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !162 { +// CHECK:STDOUT: define linkonce_odr void @"_CConvert.3667eb502d1ddfa9:OptionalAs.c7a50af9bdd61b43.Core.5d35ad54738af8e4"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !162 { // CHECK:STDOUT: %temp = alloca i32, align 4, !dbg !165 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %temp), !dbg !165 // CHECK:STDOUT: %1 = call i32 @"_CConvert.a44fce96e16342e7:ImplicitAs.ad22d1bbc0605210.Core.90a4f5ca3e06badd"(i32 %self), !dbg !165 @@ -268,7 +268,7 @@ fn AddOrRemoveConst(a: i32, b: const i32) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.30ced37eb1e63547(ptr sret({ i32, i1 }) %return, i32 %value) #0 !dbg !168 { +// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.30ced37eb1e63547(ptr sret(<{ i32, i1 }>) %return, i32 %value) #0 !dbg !168 { // CHECK:STDOUT: call void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr %return, i32 %value), !dbg !171 // CHECK:STDOUT: ret void, !dbg !172 // CHECK:STDOUT: } @@ -280,16 +280,16 @@ fn AddOrRemoveConst(a: i32, b: const i32) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.b0246d3dd29c9210(ptr sret({ i32, i1 }) %return, i32 %value) #0 !dbg !178 { +// CHECK:STDOUT: define linkonce_odr void @_CSome.Optional.Core.b0246d3dd29c9210(ptr sret(<{ i32, i1 }>) %return, i32 %value) #0 !dbg !178 { // CHECK:STDOUT: call void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.5325ffa2b57e13bd"(ptr %return, i32 %value), !dbg !181 // CHECK:STDOUT: ret void, !dbg !182 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !183 { -// CHECK:STDOUT: %value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 0, !dbg !186 +// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.bfb441135f07cbe1"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !183 { +// CHECK:STDOUT: %value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 0, !dbg !186 // CHECK:STDOUT: store i32 %self, ptr %value, align 4, !dbg !186 -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !187 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !187 // CHECK:STDOUT: store i8 1, ptr %has_value, align 1, !dbg !187 // CHECK:STDOUT: ret void, !dbg !188 // CHECK:STDOUT: } @@ -301,11 +301,11 @@ fn AddOrRemoveConst(a: i32, b: const i32) { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.5325ffa2b57e13bd"(ptr sret({ i32, i1 }) %return, i32 %self) #0 !dbg !197 { -// CHECK:STDOUT: %value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 0, !dbg !200 +// CHECK:STDOUT: define linkonce_odr void @"_CSome.3e8267224c5dc9c2:OptionalStorage.Core.5325ffa2b57e13bd"(ptr sret(<{ i32, i1 }>) %return, i32 %self) #0 !dbg !197 { +// CHECK:STDOUT: %value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 0, !dbg !200 // CHECK:STDOUT: %1 = call i32 @"_COp.34e7a685d3648824:Copy.Core.64ccbb8e5d9a0b8e"(i32 %self), !dbg !201 // CHECK:STDOUT: store i32 %1, ptr %value, align 4, !dbg !200 -// CHECK:STDOUT: %has_value = getelementptr inbounds nuw { i32, i1 }, ptr %return, i32 0, i32 1, !dbg !202 +// CHECK:STDOUT: %has_value = getelementptr inbounds nuw <{ i32, i1 }>, ptr %return, i32 0, i32 1, !dbg !202 // CHECK:STDOUT: store i8 1, ptr %has_value, align 1, !dbg !202 // CHECK:STDOUT: ret void, !dbg !203 // CHECK:STDOUT: } diff --git a/toolchain/lower/testdata/struct/layout.carbon b/toolchain/lower/testdata/struct/layout.carbon new file mode 100644 index 000000000000..62a7adab02ae --- /dev/null +++ b/toolchain/lower/testdata/struct/layout.carbon @@ -0,0 +1,433 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/struct/layout.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/struct/layout.carbon + +// --- no_padding.carbon +library "[[@TEST_NAME]]"; + +fn G(p: i32*); + +fn F() { + // LLVM {i32, i32, i32} matches the Carbon layout, so we use it. + var no_padding: {.x: i32, .y: i32, .z: i32}; + G(&no_padding.x); + G(&no_padding.y); + G(&no_padding.z); +} + +// --- avoid_tail_padding.carbon +library "[[@TEST_NAME]]"; + +fn G(p: i32*); + +fn F() { + // LLVM {ptr, i32} would have a size of 16, so we use a packed struct. + var avoid_tail_padding: {.x: ()*, .y: i32}; + G(&avoid_tail_padding.y); +} + +// --- use_tail_padding.carbon +library "[[@TEST_NAME]]"; + +fn G(p: i32*); + +fn F() { + // LLVM {ptr, i32} would have a size of 16, so we use a packed struct as the + // type of `.a`. `.b` goes in the tail padding. + var use_tail_padding: {.a: {.x: ()*, .y: i32}, .b: i32}; + G(&use_tail_padding.a.y); + G(&use_tail_padding.b); +} + +// --- implicit_padding.carbon +library "[[@TEST_NAME]]"; + +fn G(p: ()**); + +fn F() { + // LLVM {<{ptr, i32}>, ptr} inserts the right padding before `.b`, so we use + // it directly. + var implicit_padding: {.a: {.x: ()*, .y: i32}, .b: ()*}; + G(&implicit_padding.a.x); + G(&implicit_padding.b); +} + +// --- insert_padding.carbon +library "[[@TEST_NAME]]"; + +fn G(p: ()**); + +fn F() { + // LLVM {<{i32, i32, i32}>, ptr, i32} would have extra tail padding, so we use + // a packed struct. That requires that we explicitly add padding between `.a` + // and `.b`. + var insert_padding: {.a: {.x: i32, .y: i32, .z: i32}, .b: ()*, .c: i32}; + G(&insert_padding.b); +} + +// CHECK:STDOUT: ; ModuleID = 'no_padding.carbon' +// CHECK:STDOUT: source_filename = "no_padding.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CG.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %no_padding.var = alloca { i32, i32, i32 }, align 8, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %no_padding.var), !dbg !7 +// CHECK:STDOUT: %.loc8.x = getelementptr inbounds nuw { i32, i32, i32 }, ptr %no_padding.var, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc8.x), !dbg !9 +// CHECK:STDOUT: %.loc9.y = getelementptr inbounds nuw { i32, i32, i32 }, ptr %no_padding.var, i32 0, i32 1, !dbg !10 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc9.y), !dbg !11 +// CHECK:STDOUT: %.loc10.z = getelementptr inbounds nuw { i32, i32, i32 }, ptr %no_padding.var, i32 0, i32 2, !dbg !12 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc10.z), !dbg !13 +// CHECK:STDOUT: call void @"_COp.3124a28397dbe9e9:core.Destroy.Core"(ptr %no_padding.var), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !14 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %self) #0 !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.3124a28397dbe9e9:core.Destroy.Core"(ptr %self) #0 !dbg !22 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !28 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "no_padding.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 8, column: 6, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 8, column: 3, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 9, column: 6, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 9, column: 3, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 10, column: 6, scope: !4) +// CHECK:STDOUT: !13 = !DILocation(line: 10, column: 3, scope: !4) +// CHECK:STDOUT: !14 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "Op", linkageName: "_COp.7e389eab4a7e5487:core.Destroy.Core", scope: null, file: !3, line: 7, type: !16, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !19) +// CHECK:STDOUT: !16 = !DISubroutineType(types: !17) +// CHECK:STDOUT: !17 = !{null, !18} +// CHECK:STDOUT: !18 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !19 = !{!20} +// CHECK:STDOUT: !20 = !DILocalVariable(arg: 1, scope: !15, type: !18) +// CHECK:STDOUT: !21 = !DILocation(line: 7, column: 3, scope: !15) +// CHECK:STDOUT: !22 = distinct !DISubprogram(name: "Op", linkageName: "_COp.3124a28397dbe9e9:core.Destroy.Core", scope: null, file: !3, line: 7, type: !23, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !26) +// CHECK:STDOUT: !23 = !DISubroutineType(types: !24) +// CHECK:STDOUT: !24 = !{null, !25} +// CHECK:STDOUT: !25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !26 = !{!27} +// CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !22, type: !25) +// CHECK:STDOUT: !28 = !DILocation(line: 7, column: 3, scope: !22) +// CHECK:STDOUT: ; ModuleID = 'avoid_tail_padding.carbon' +// CHECK:STDOUT: source_filename = "avoid_tail_padding.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CG.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %avoid_tail_padding.var = alloca <{ ptr, i32 }>, align 8, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %avoid_tail_padding.var), !dbg !7 +// CHECK:STDOUT: %.loc8.y = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %avoid_tail_padding.var, i32 0, i32 1, !dbg !8 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc8.y), !dbg !9 +// CHECK:STDOUT: call void @"_COp.0d530a9b810c77a1:core.Destroy.Core"(ptr %avoid_tail_padding.var), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !10 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %self) #0 !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.0d530a9b810c77a1:core.Destroy.Core"(ptr %self) #0 !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !24 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "avoid_tail_padding.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 8, column: 6, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 8, column: 3, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Op", linkageName: "_COp.7e389eab4a7e5487:core.Destroy.Core", scope: null, file: !3, line: 7, type: !12, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !15) +// CHECK:STDOUT: !12 = !DISubroutineType(types: !13) +// CHECK:STDOUT: !13 = !{null, !14} +// CHECK:STDOUT: !14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !15 = !{!16} +// CHECK:STDOUT: !16 = !DILocalVariable(arg: 1, scope: !11, type: !14) +// CHECK:STDOUT: !17 = !DILocation(line: 7, column: 3, scope: !11) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "Op", linkageName: "_COp.0d530a9b810c77a1:core.Destroy.Core", scope: null, file: !3, line: 7, type: !19, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !22) +// CHECK:STDOUT: !19 = !DISubroutineType(types: !20) +// CHECK:STDOUT: !20 = !{null, !21} +// CHECK:STDOUT: !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !22 = !{!23} +// CHECK:STDOUT: !23 = !DILocalVariable(arg: 1, scope: !18, type: !21) +// CHECK:STDOUT: !24 = !DILocation(line: 7, column: 3, scope: !18) +// CHECK:STDOUT: ; ModuleID = 'use_tail_padding.carbon' +// CHECK:STDOUT: source_filename = "use_tail_padding.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CG.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %use_tail_padding.var = alloca { <{ ptr, i32 }>, i32 }, align 8, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %use_tail_padding.var), !dbg !7 +// CHECK:STDOUT: %.loc9_22.a = getelementptr inbounds nuw { <{ ptr, i32 }>, i32 }, ptr %use_tail_padding.var, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: %.loc9_24.y = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %.loc9_22.a, i32 0, i32 1, !dbg !8 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc9_24.y), !dbg !9 +// CHECK:STDOUT: %.loc10.b = getelementptr inbounds nuw { <{ ptr, i32 }>, i32 }, ptr %use_tail_padding.var, i32 0, i32 1, !dbg !10 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc10.b), !dbg !11 +// CHECK:STDOUT: call void @"_COp.2106667af8a21f02:core.Destroy.Core"(ptr %use_tail_padding.var), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !12 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %self) #0 !dbg !13 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !19 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.0d530a9b810c77a1:core.Destroy.Core"(ptr %self) #0 !dbg !20 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.2106667af8a21f02:core.Destroy.Core"(ptr %self) #0 !dbg !27 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !30 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "use_tail_padding.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 8, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 9, column: 6, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 9, column: 3, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 10, column: 6, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 10, column: 3, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !13 = distinct !DISubprogram(name: "Op", linkageName: "_COp.7e389eab4a7e5487:core.Destroy.Core", scope: null, file: !3, line: 8, type: !14, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !17) +// CHECK:STDOUT: !14 = !DISubroutineType(types: !15) +// CHECK:STDOUT: !15 = !{null, !16} +// CHECK:STDOUT: !16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !17 = !{!18} +// CHECK:STDOUT: !18 = !DILocalVariable(arg: 1, scope: !13, type: !16) +// CHECK:STDOUT: !19 = !DILocation(line: 8, column: 3, scope: !13) +// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "Op", linkageName: "_COp.0d530a9b810c77a1:core.Destroy.Core", scope: null, file: !3, line: 8, type: !21, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !24) +// CHECK:STDOUT: !21 = !DISubroutineType(types: !22) +// CHECK:STDOUT: !22 = !{null, !23} +// CHECK:STDOUT: !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !24 = !{!25} +// CHECK:STDOUT: !25 = !DILocalVariable(arg: 1, scope: !20, type: !23) +// CHECK:STDOUT: !26 = !DILocation(line: 8, column: 3, scope: !20) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "Op", linkageName: "_COp.2106667af8a21f02:core.Destroy.Core", scope: null, file: !3, line: 8, type: !21, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !28) +// CHECK:STDOUT: !28 = !{!29} +// CHECK:STDOUT: !29 = !DILocalVariable(arg: 1, scope: !27, type: !23) +// CHECK:STDOUT: !30 = !DILocation(line: 8, column: 3, scope: !27) +// CHECK:STDOUT: ; ModuleID = 'implicit_padding.carbon' +// CHECK:STDOUT: source_filename = "implicit_padding.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CG.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %implicit_padding.var = alloca { <{ ptr, i32 }>, ptr }, align 8, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %implicit_padding.var), !dbg !7 +// CHECK:STDOUT: %.loc9_22.a = getelementptr inbounds nuw { <{ ptr, i32 }>, ptr }, ptr %implicit_padding.var, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: %.loc9_24.x = getelementptr inbounds nuw <{ ptr, i32 }>, ptr %.loc9_22.a, i32 0, i32 0, !dbg !8 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc9_24.x), !dbg !9 +// CHECK:STDOUT: %.loc10.b = getelementptr inbounds nuw { <{ ptr, i32 }>, ptr }, ptr %implicit_padding.var, i32 0, i32 1, !dbg !10 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc10.b), !dbg !11 +// CHECK:STDOUT: call void @"_COp.a17d504d00302116:core.Destroy.Core"(ptr %implicit_padding.var), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !12 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %self) #0 !dbg !13 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !19 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.0d530a9b810c77a1:core.Destroy.Core"(ptr %self) #0 !dbg !20 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.a17d504d00302116:core.Destroy.Core"(ptr %self) #0 !dbg !27 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !30 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "implicit_padding.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 8, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 9, column: 6, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 9, column: 3, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 10, column: 6, scope: !4) +// CHECK:STDOUT: !11 = !DILocation(line: 10, column: 3, scope: !4) +// CHECK:STDOUT: !12 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !13 = distinct !DISubprogram(name: "Op", linkageName: "_COp.7e389eab4a7e5487:core.Destroy.Core", scope: null, file: !3, line: 8, type: !14, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !17) +// CHECK:STDOUT: !14 = !DISubroutineType(types: !15) +// CHECK:STDOUT: !15 = !{null, !16} +// CHECK:STDOUT: !16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !17 = !{!18} +// CHECK:STDOUT: !18 = !DILocalVariable(arg: 1, scope: !13, type: !16) +// CHECK:STDOUT: !19 = !DILocation(line: 8, column: 3, scope: !13) +// CHECK:STDOUT: !20 = distinct !DISubprogram(name: "Op", linkageName: "_COp.0d530a9b810c77a1:core.Destroy.Core", scope: null, file: !3, line: 8, type: !21, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !24) +// CHECK:STDOUT: !21 = !DISubroutineType(types: !22) +// CHECK:STDOUT: !22 = !{null, !23} +// CHECK:STDOUT: !23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !24 = !{!25} +// CHECK:STDOUT: !25 = !DILocalVariable(arg: 1, scope: !20, type: !23) +// CHECK:STDOUT: !26 = !DILocation(line: 8, column: 3, scope: !20) +// CHECK:STDOUT: !27 = distinct !DISubprogram(name: "Op", linkageName: "_COp.a17d504d00302116:core.Destroy.Core", scope: null, file: !3, line: 8, type: !21, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !28) +// CHECK:STDOUT: !28 = !{!29} +// CHECK:STDOUT: !29 = !DILocalVariable(arg: 1, scope: !27, type: !23) +// CHECK:STDOUT: !30 = !DILocation(line: 8, column: 3, scope: !27) +// CHECK:STDOUT: ; ModuleID = 'insert_padding.carbon' +// CHECK:STDOUT: source_filename = "insert_padding.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CG.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %insert_padding.var = alloca <{ <{ { i32, i32, i32 }, [4 x i8] }>, ptr, i32 }>, align 8, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %insert_padding.var), !dbg !7 +// CHECK:STDOUT: %.loc10.b = getelementptr inbounds nuw <{ <{ { i32, i32, i32 }, [4 x i8] }>, ptr, i32 }>, ptr %insert_padding.var, i32 0, i32 1, !dbg !8 +// CHECK:STDOUT: call void @_CG.Main(ptr %.loc10.b), !dbg !9 +// CHECK:STDOUT: call void @"_COp.ad98736f0dc6e810:core.Destroy.Core"(ptr %insert_padding.var), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !10 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.7e389eab4a7e5487:core.Destroy.Core"(ptr %self) #0 !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.3124a28397dbe9e9:core.Destroy.Core"(ptr %self) #0 !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !24 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.ad98736f0dc6e810:core.Destroy.Core"(ptr %self) #0 !dbg !25 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !28 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "insert_padding.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "F", linkageName: "_CF.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 9, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 10, column: 6, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 10, column: 3, scope: !4) +// CHECK:STDOUT: !10 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: !11 = distinct !DISubprogram(name: "Op", linkageName: "_COp.7e389eab4a7e5487:core.Destroy.Core", scope: null, file: !3, line: 9, type: !12, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !15) +// CHECK:STDOUT: !12 = !DISubroutineType(types: !13) +// CHECK:STDOUT: !13 = !{null, !14} +// CHECK:STDOUT: !14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !15 = !{!16} +// CHECK:STDOUT: !16 = !DILocalVariable(arg: 1, scope: !11, type: !14) +// CHECK:STDOUT: !17 = !DILocation(line: 9, column: 3, scope: !11) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "Op", linkageName: "_COp.3124a28397dbe9e9:core.Destroy.Core", scope: null, file: !3, line: 9, type: !19, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !22) +// CHECK:STDOUT: !19 = !DISubroutineType(types: !20) +// CHECK:STDOUT: !20 = !{null, !21} +// CHECK:STDOUT: !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !22 = !{!23} +// CHECK:STDOUT: !23 = !DILocalVariable(arg: 1, scope: !18, type: !21) +// CHECK:STDOUT: !24 = !DILocation(line: 9, column: 3, scope: !18) +// CHECK:STDOUT: !25 = distinct !DISubprogram(name: "Op", linkageName: "_COp.ad98736f0dc6e810:core.Destroy.Core", scope: null, file: !3, line: 9, type: !19, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !26) +// CHECK:STDOUT: !26 = !{!27} +// CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !25, type: !21) +// CHECK:STDOUT: !28 = !DILocation(line: 9, column: 3, scope: !25) diff --git a/toolchain/lower/testdata/struct/member_access.carbon b/toolchain/lower/testdata/struct/member_access.carbon index 567dd89552ac..2562f739d0eb 100644 --- a/toolchain/lower/testdata/struct/member_access.carbon +++ b/toolchain/lower/testdata/struct/member_access.carbon @@ -20,20 +20,20 @@ fn Run() -> i32 { // CHECK:STDOUT: ; ModuleID = 'member_access.carbon' // CHECK:STDOUT: source_filename = "member_access.carbon" // CHECK:STDOUT: -// CHECK:STDOUT: @struct.454.loc14_3 = internal constant { double, i32 } { double 0.000000e+00, i32 1 } +// CHECK:STDOUT: @struct.454.loc14_3 = internal constant <{ double, i32 }> <{ double 0.000000e+00, i32 1 }> // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind // CHECK:STDOUT: define i32 @main() #0 !dbg !4 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %x.var = alloca { double, i32 }, align 8, !dbg !8 +// CHECK:STDOUT: %x.var = alloca <{ double, i32 }>, align 8, !dbg !8 // CHECK:STDOUT: %y.var = alloca i32, align 4, !dbg !9 // CHECK:STDOUT: %_.var = alloca i32, align 4, !dbg !10 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %x.var), !dbg !8 -// CHECK:STDOUT: %.loc14_48.3.a = getelementptr inbounds nuw { double, i32 }, ptr %x.var, i32 0, i32 0, !dbg !11 -// CHECK:STDOUT: %.loc14_48.6.b = getelementptr inbounds nuw { double, i32 }, ptr %x.var, i32 0, i32 1, !dbg !11 -// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x.var, ptr align 8 @struct.454.loc14_3, i64 16, i1 false), !dbg !8 +// CHECK:STDOUT: %.loc14_48.3.a = getelementptr inbounds nuw <{ double, i32 }>, ptr %x.var, i32 0, i32 0, !dbg !11 +// CHECK:STDOUT: %.loc14_48.6.b = getelementptr inbounds nuw <{ double, i32 }>, ptr %x.var, i32 0, i32 1, !dbg !11 +// CHECK:STDOUT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %x.var, ptr align 1 @struct.454.loc14_3, i64 12, i1 false), !dbg !8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %y.var), !dbg !9 -// CHECK:STDOUT: %.loc15_17.1.b = getelementptr inbounds nuw { double, i32 }, ptr %x.var, i32 0, i32 1, !dbg !12 +// CHECK:STDOUT: %.loc15_17.1.b = getelementptr inbounds nuw <{ double, i32 }>, ptr %x.var, i32 0, i32 1, !dbg !12 // CHECK:STDOUT: %.loc15_17.2 = load i32, ptr %.loc15_17.1.b, align 4, !dbg !12 // CHECK:STDOUT: store i32 %.loc15_17.2, ptr %y.var, align 4, !dbg !9 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %_.var), !dbg !10 diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index e186507ceb98..e431bb23c49d 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -70,15 +70,20 @@ File::File(const Parse::Tree* parse_tree, CheckIRId check_ir_id, expr_regions_(check_ir_id), clang_source_locs_(check_ir_id) { // `type`, `form`, and the error type are both complete & concrete types. + // TODO: This duplicates the code in `check/type_completion.cpp`. Consider + // requiring these types to be complete from Check initialization instead. types_.SetComplete( TypeType::TypeId, - {.value_repr = {.kind = ValueRepr::Copy, .type_id = TypeType::TypeId}}); + {.value_repr = {.kind = ValueRepr::Copy, .type_id = TypeType::TypeId}, + .object_layout = SemIR::ObjectLayout::Empty()}); types_.SetComplete( FormType::TypeId, - {.value_repr = {.kind = ValueRepr::Copy, .type_id = FormType::TypeId}}); + {.value_repr = {.kind = ValueRepr::Copy, .type_id = FormType::TypeId}, + .object_layout = SemIR::ObjectLayout::Empty()}); types_.SetComplete( ErrorInst::TypeId, - {.value_repr = {.kind = ValueRepr::Copy, .type_id = ErrorInst::TypeId}}); + {.value_repr = {.kind = ValueRepr::Copy, .type_id = ErrorInst::TypeId}, + .object_layout = SemIR::ObjectLayout::Empty()}); insts_.Reserve(SingletonInstKinds.size()); for (auto kind : SingletonInstKinds) { diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 003e5362b178..5f0022c6147c 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -66,7 +66,7 @@ struct ExprRegion { using ExprRegionStore = ValueStore>; using CustomLayoutStore = - BlockValueStore>; + BlockValueStore>; // The semantic IR for a single file. class File : public Printable { @@ -95,16 +95,23 @@ class File : public Printable { auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const -> void; - // Returns array bound value from the bound instruction. + // Returns the zero-extended integer value of the given instruction if it is a + // constant integer. // TODO: Move this function elsewhere. - auto GetArrayBoundValue(InstId bound_id) const -> std::optional { - if (auto bound = insts().TryGetAs( - constant_values().GetConstantInstId(bound_id))) { - return ints().Get(bound->int_id).getZExtValue(); + auto GetZExtIntValue(InstId inst_id) const -> std::optional { + if (auto val = insts().TryGetAs( + constant_values().GetConstantInstId(inst_id))) { + return ints().Get(val->int_id).getZExtValue(); } return std::nullopt; } + // Returns the layout of a pointer. + // TODO: This should depend on the target machine. + auto GetPointerLayout() const -> ObjectLayout { + return {.size = ObjectSize::Bytes(8), .alignment = ObjectSize::Bytes(8)}; + } + // Gets the pointee type of the given type, which must be a pointer type. // TODO: Move this function elsewhere. auto GetPointeeType(TypeId pointer_id) const -> TypeId { diff --git a/toolchain/sem_ir/inst_fingerprinter.cpp b/toolchain/sem_ir/inst_fingerprinter.cpp index 197ac044a0a2..4c9f604ecdd8 100644 --- a/toolchain/sem_ir/inst_fingerprinter.cpp +++ b/toolchain/sem_ir/inst_fingerprinter.cpp @@ -191,7 +191,9 @@ struct Worklist { } auto block = sem_ir->custom_layouts().Get(custom_layout_id); contents.push_back(block.size()); - contents.insert(contents.end(), block.begin(), block.end()); + for (auto size : block) { + contents.push_back(size.bits()); + } } auto Add(NameScopeId name_scope_id) -> void { diff --git a/toolchain/sem_ir/type.h b/toolchain/sem_ir/type.h index be241143a540..6aa3b20961a7 100644 --- a/toolchain/sem_ir/type.h +++ b/toolchain/sem_ir/type.h @@ -249,6 +249,15 @@ class TypeStore : public Yaml::Printable { map.Add(PrintToString(type_id), Yaml::OutputMapping([&](Yaml::OutputMapping::Map map2) { map2.Add("value_repr", Yaml::OutputScalar(info.value_repr)); + map2.Add( + "object_layout", + Yaml::OutputMapping([&](Yaml::OutputMapping::Map map3) { + map3.Add("size", + Yaml::OutputScalar(info.object_layout.size)); + map3.Add( + "alignment", + Yaml::OutputScalar(info.object_layout.alignment)); + })); if (info.abstract_class_id.has_value()) { map2.Add("abstract_class_id", Yaml::OutputScalar(info.abstract_class_id)); diff --git a/toolchain/sem_ir/type_info.cpp b/toolchain/sem_ir/type_info.cpp index 25f8e2e8dd77..fedceb28a523 100644 --- a/toolchain/sem_ir/type_info.cpp +++ b/toolchain/sem_ir/type_info.cpp @@ -37,7 +37,18 @@ auto ValueRepr::Print(llvm::raw_ostream& out) const -> void { } auto CompleteTypeInfo::Print(llvm::raw_ostream& out) const -> void { - out << "{value_rep: " << value_repr << "}"; + out << "{value_rep: " << value_repr + << ", layout: {size: " << object_layout.size + << ", align: " << object_layout.alignment << "}}"; +} + +auto ObjectSize::Print(llvm::raw_ostream& out) const -> void { + int64_t bytes = bits_ / 8; + int64_t bits = bits_ % 8; + out << bytes; + if (bits != 0) { + out << ":" << bits; + } } auto ValueRepr::ForType(const File& file, TypeId type_id) -> ValueRepr { diff --git a/toolchain/sem_ir/type_info.h b/toolchain/sem_ir/type_info.h index 9a899633337e..a77a7cef0cbb 100644 --- a/toolchain/sem_ir/type_info.h +++ b/toolchain/sem_ir/type_info.h @@ -73,6 +73,146 @@ struct ValueRepr : public Printable { TypeId type_id = TypeId::None; }; +// A size within an object representation. This stores a bit count, but provides +// convenience methods to access the size in bytes instead. +class ObjectSize : public Printable { + public: + static constexpr auto Zero() -> ObjectSize { return ObjectSize(0); } + + static constexpr auto Bits(int64_t bits) -> ObjectSize { + return ObjectSize(bits); + } + + static constexpr auto Bytes(int64_t bytes) -> ObjectSize { + return ObjectSize(bytes * 8); + } + + // Returns the size in bits. + auto bits() const -> int64_t { return bits_; } + + // Returns the minimum number of bytes that would contain this size. This is + // the size in bytes, rounded up. + auto bytes() const -> int64_t { return (bits_ + 7) / 8; } + + // Return this size rounded up to a multiple of `align`, which must be a power + // of 2. + [[nodiscard]] auto AlignedTo(ObjectSize align) const -> ObjectSize { + CARBON_CHECK(llvm::isPowerOf2_64(align.bits_), + "Non power-of-2 alignment {0}", align.bits_); + return Bits((bits_ + align.bits_ - 1) & -align.bits_); + } + + auto Print(llvm::raw_ostream& out) const -> void; + + friend constexpr auto operator<=>(ObjectSize a, ObjectSize b) { + return a.bits_ <=> b.bits_; + } + + friend constexpr auto operator==(ObjectSize a, ObjectSize b) -> bool { + return a.bits_ == b.bits_; + } + + // Computes the sum of two object sizes. This is the size of the smallest + // object that could fit both objects consecutively, without any alignment + // constraints or other padding bits. + auto operator+=(ObjectSize other) -> ObjectSize& { + bits_ += other.bits_; + return *this; + } + friend auto operator+(ObjectSize a, ObjectSize b) -> ObjectSize { + return a += b; + } + + // Scales a size by a dimensionless integer. This is the size of `n` objects + // of the given size, laid out contiguously with no padding between them. + // Equivalent to adding the size to itself `n` times. + auto operator*=(int64_t n) -> ObjectSize& { + CARBON_CHECK(n >= 0, "sizes should not be negative"); + bits_ *= n; + return *this; + } + friend auto operator*(ObjectSize a, int64_t n) -> ObjectSize { + return a *= n; + } + friend auto operator*(int64_t n, ObjectSize a) -> ObjectSize { + return a *= n; + } + + private: + explicit constexpr ObjectSize(int64_t bits) : bits_(bits) { + CARBON_CHECK(bits >= 0, "sizes should not be negative"); + } + + int64_t bits_; +}; + +// The layout of an object representation. +struct ObjectLayout { + // The size of the object representation. This may be zero for empty types. + // This is not guaranteed to be a multiple of `alignment`, so explicit tail + // padding may be required if objects using this layout are stored in an + // array. + ObjectSize size = ObjectSize::Zero(); + + // The alignment of the object representation. This will be a power of 2 for a + // valid representation, or zero if the representation is unset (such as for + // the object layout of an incomplete type). This will typically be 1 byte for + // an empty type. + ObjectSize alignment = ObjectSize::Zero(); + + // Returns the object layout to use for an empty type. + [[nodiscard]] static auto Empty() -> ObjectLayout { + return {.size = ObjectSize::Zero(), .alignment = ObjectSize::Bytes(1)}; + } + + // Returns the object layout to use for an array of `count` elements. The + // result never has tail padding, so this is *not* the same as adding the + // element to itself `count` times in general. + static auto ForArray(ObjectLayout element_layout, int64_t count) + -> ObjectLayout { + return {.size = element_layout.ArrayStride() * count, + .alignment = element_layout.alignment}; + } + + // Returns the stride of an array with this element type. + auto ArrayStride() const -> ObjectSize { return size.AlignedTo(alignment); } + + // Returns the offset that a field with the given layout would have if + // appended to this struct. + auto FieldOffset(ObjectLayout field) -> ObjectSize { + CARBON_CHECK(field.has_value()); + return size.AlignedTo(field.alignment); + } + + // Updates this aggregate layout by concatenating naturally-aligned space for + // the given field layout, which is required to be valid. + auto AppendField(ObjectLayout field) -> void { + size = FieldOffset(field) + field.size; + alignment = std::max(alignment, field.alignment); + } + + // Updates this aggregate layout by concatenating naturally-aligned space for + // the given field layout, if the field has a valid layout. If any field with + // an invalid layout is appended, the layout of the aggregate becomes invalid. + // + // An invalid layout for a field with a complete type indicates that its + // layout is dependent on a generic parameter; this causes the layout of the + // enclosing aggregate to also be dependent on that parameter. + auto TryAppendField(ObjectLayout field) -> void { + if (has_value()) { + if (field.has_value()) { + AppendField(field); + } else { + *this = ObjectLayout(); + } + } + } + + // Returns true if the layout has been set. For a complete type, this will be + // true if the layout is non-dependent. + auto has_value() const -> bool { return alignment != ObjectSize::Zero(); } +}; + // Information stored about a TypeId corresponding to a complete type. struct CompleteTypeInfo : public Printable { auto Print(llvm::raw_ostream& out) const -> void; @@ -81,6 +221,10 @@ struct CompleteTypeInfo : public Printable { // not complete. ValueRepr value_repr = ValueRepr(); + // The layout of the object representation of this type. This will be valid + // unless the type is incomplete or dependent. + ObjectLayout object_layout = ObjectLayout(); + // If this type is abstract, this is id of an abstract class it uses. ClassId abstract_class_id = ClassId::None;