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;