diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index b2fde61fc2f5..b9d16635c9b3 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -337,6 +337,14 @@ class CarbonExternalASTSource : public clang::ExternalASTSource { auto CompleteType(clang::TagDecl* tag_decl) -> void override; + auto layoutRecordType( + const clang::RecordDecl* record_decl, uint64_t& size, uint64_t& alignment, + llvm::DenseMap& field_offsets, + llvm::DenseMap& + base_offsets, + llvm::DenseMap& + vbase_offsets) -> bool override; + private: // Builds the top-level C++ namespace `Carbon` and adds it to the translation // unit. @@ -533,6 +541,54 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName( } } +// If this declaration declares a class type that is "owned" by Carbon, and not +// imported from C++, returns the corresponding type ID and `ClassType`. +// Otherwise returns `nullopt`. +static auto GetAsCarbonOwnedClass(Context& context, + const clang::TagDecl* tag_decl) + -> std::optional> { + // Quickly check whether we could possibly own this class. + // TODO: Once we multiplex with the ASTReader, handle + // ASTReader::completeVisibleDeclsMap setting this to `false`. + if (!tag_decl->hasExternalVisibleStorage()) { + return std::nullopt; + } + + auto key = SemIR::ClangDeclKey::ForNonFunctionDecl( + const_cast(tag_decl->getFirstDecl())); + auto clang_decl_id = context.clang_decls().Lookup(key); + if (!clang_decl_id.has_value()) { + return std::nullopt; + } + + auto inst_id = context.clang_decls().Get(clang_decl_id).inst_id; + auto const_id = context.constant_values().Get(inst_id); + if (!const_id.has_value()) { + return std::nullopt; + } + + auto class_type = + context.constant_values().TryGetInstAs(const_id); + if (!class_type) { + return std::nullopt; + } + + // Determine whether this class was imported from C++. + // TODO: This currently can't happen, because only Carbon classes have + // external lexical storage, but will happen once we support importing C++ + // classes from AST files. Add a test once that is supported. + // TODO: Consider setting `extern_library_id` on classes imported from C++ to + // indicate the current file does not own them. + const auto& class_info = context.classes().Get(class_type->class_id); + if (class_info.parent_scope_id.has_value() && + context.name_scopes().Get(class_info.parent_scope_id).is_cpp_scope()) { + return std::nullopt; + } + + auto class_type_id = context.types().GetTypeIdForTypeConstantId(const_id); + return std::make_pair(class_type_id, *class_type); +} + auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { auto* class_decl = dyn_cast(tag_decl); if (!class_decl) { @@ -541,39 +597,26 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { return; } - auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(tag_decl->getFirstDecl()); - auto clang_decl_id = context_->clang_decls().Lookup(key); - if (!clang_decl_id.has_value()) { + auto carbon_class_info = GetAsCarbonOwnedClass(*context_, tag_decl); + if (!carbon_class_info) { return; } + auto& [class_type_id, class_type] = *carbon_class_info; - auto inst_id = context_->clang_decls().Get(clang_decl_id).inst_id; - auto const_id = context_->constant_values().Get(inst_id); - if (!const_id.has_value()) { - return; - } - - auto class_type = - context_->constant_values().TryGetInstAs(const_id); - if (!class_type) { - return; - } - - auto class_type_id = context_->types().GetTypeIdForTypeConstantId(const_id); auto context_fn = [](DiagnosticContextBuilder& /*builder*/) -> void {}; if (!RequireCompleteType(*context_, class_type_id, GetCurrentCppLocId(), context_fn)) { return; } - const auto& class_info = context_->classes().Get(class_type->class_id); + const auto& class_info = context_->classes().Get(class_type.class_id); class_decl->startDefinition(); CARBON_CHECK(class_decl->hasDefinition()); // If the Carbon class has a base class that we can map into C++, add that as // a C++ base class. auto base_type_id = - class_info.GetBaseType(context_->sem_ir(), class_type->specific_id); + class_info.GetBaseType(context_->sem_ir(), class_type.specific_id); if (base_type_id.has_value()) { auto base_loc = GetCppLocation(*context_, SemIR::LocId(class_info.base_id)); if (auto base_type = MapToCppType(*context_, base_type_id); @@ -598,6 +641,57 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { class_decl->completeDefinition(); } +auto CarbonExternalASTSource::layoutRecordType( + const clang::RecordDecl* record_decl, uint64_t& size, uint64_t& alignment, + llvm::DenseMap& field_offsets, + llvm::DenseMap& base_offsets, + llvm::DenseMap& + vbase_offsets) -> bool { + auto carbon_class_info = GetAsCarbonOwnedClass(*context_, record_decl); + if (!carbon_class_info) { + return false; + } + auto& [class_type_id, class_type] = *carbon_class_info; + + // Clang should not have asked for the layout of an incomplete type, but check + // now to be sure, and to generate a specific definition if needed. + // TODO: Add a test for layout of a specific class once they're supported in + // general. + CompleteTypeOrCheckFail(*context_, class_type_id); + + // Set the overall size and alignment. We round up the size to an integer + // number of bytes in order to avoid surprising Clang too much. + auto layout = context_->sem_ir() + .types() + .GetCompleteTypeInfo(class_type_id) + .object_layout; + size = layout.size.bytes() * 8; + alignment = layout.alignment.bits(); + + // TODO: Add field offsets once we import fields. + static_cast(field_offsets); + + // Add offset for base class, if any. + if (const auto* class_decl = dyn_cast(record_decl); + class_decl && !class_decl->bases().empty()) { + CARBON_CHECK(class_decl->getNumBases() == 1, + "Carbon class with multiple bases"); + const auto& base = *class_decl->bases_begin(); + // TODO: If this class introduced a vptr, the base will be at an offset of + // `sizeof(void*)`, not 0. + base_offsets.insert( + {base.getType()->getAsCXXRecordDecl()->getCanonicalDecl(), + clang::CharUnits::Zero()}); + + // TODO: Support deriving from a C++ class with virtual bases. + CARBON_CHECK(class_decl->getNumVBases() == 0, + "Carbon class with multiple bases"); + static_cast(vbase_offsets); + } + + return true; +} + // Parses a sequence of top-level declarations and forms a corresponding // representation in the Clang AST. Unlike clang::ParseAST, does not finish the // translation unit when EOF is reached. diff --git a/toolchain/check/testdata/interop/cpp/class/export/class.carbon b/toolchain/check/testdata/interop/cpp/class/export/class.carbon index 0b97f6ec21c0..5119f61e61e5 100644 --- a/toolchain/check/testdata/interop/cpp/class/export/class.carbon +++ b/toolchain/check/testdata/interop/cpp/class/export/class.carbon @@ -97,24 +97,3 @@ inline Cpp ''' // CHECK:STDERR: Carbon::T t; '''; - -// --- fail_todo_fields.carbon -library "[[@TEST_NAME]]"; - -import Cpp; - -class A { - var a: i32; - var b: i32; -} - -inline Cpp ''' -// CHECK:STDERR: fail_todo_fields.carbon:[[@LINE+7]]:15: error: static assertion failed due to requirement 'sizeof(Carbon::A) == 2 * sizeof(int)' [CppInteropParseError] -// CHECK:STDERR: 18 | static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); -// CHECK:STDERR: | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_fields.carbon:[[@LINE+4]]:33: note: expression evaluates to '1 == 8' [CppInteropParseNote] -// CHECK:STDERR: 18 | static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); -// CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); -'''; diff --git a/toolchain/check/testdata/interop/cpp/class/export/layout.carbon b/toolchain/check/testdata/interop/cpp/class/export/layout.carbon new file mode 100644 index 000000000000..4a3cd66c3168 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/class/export/layout.carbon @@ -0,0 +1,49 @@ +// 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/check/testdata/interop/cpp/class/export/layout.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/class/export/layout.carbon + +// --- simple.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class C { + var a: i32; + var b: i32; + var c: i32; +} + +inline Cpp '''c++ +static_assert(sizeof(Carbon::C) == 12); +static_assert(alignof(Carbon::C) == 4); +'''; + +// --- packed.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class C { + var a: ()*; + var b: i32; +} + +class D { + var c: C; + var d: i32; +} + +inline Cpp '''c++ +static_assert(sizeof(Carbon::C) == 12); +static_assert(alignof(Carbon::C) == 8); +static_assert(sizeof(Carbon::D) == 16); +static_assert(alignof(Carbon::D) == 8); +'''; diff --git a/toolchain/check/testdata/interop/cpp/reverse/class.carbon b/toolchain/check/testdata/interop/cpp/reverse/class.carbon deleted file mode 100644 index 0a6b0bd4fd8c..000000000000 --- a/toolchain/check/testdata/interop/cpp/reverse/class.carbon +++ /dev/null @@ -1,123 +0,0 @@ -// 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/check/testdata/interop/cpp/reverse/class.carbon -// TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/reverse/class.carbon - -// --- static_members.carbon -library "[[@TEST_NAME]]"; - -import Cpp; - -class A { - fn F() {} -} - -inline Cpp ''' -void G() { - Carbon::A::F(); -} -'''; - -// --- nested.carbon -library "[[@TEST_NAME]]"; - -import Cpp; - -class A { - class B { - class C { - } - } -} - -inline Cpp ''' -void G() { - Carbon::A::B::C *p = nullptr; -} -'''; - -// --- fail_incomplete_concrete.carbon -library "[[@TEST_NAME]]"; - -import Cpp; - -// CHECK:STDERR: fail_incomplete_concrete.carbon:[[@LINE+4]]:1: error: class was forward declared here [ClassForwardDeclaredHere] -// CHECK:STDERR: class A; -// CHECK:STDERR: ^~~~~~~~ -// CHECK:STDERR: -class A; - -inline Cpp ''' -// CHECK:STDERR: fail_incomplete_concrete.carbon:[[@LINE+4]]:11: error: variable has incomplete type 'Carbon::A' [CppInteropParseError] -// CHECK:STDERR: 16 | Carbon::A a; -// CHECK:STDERR: | ^ -// CHECK:STDERR: -Carbon::A a; -'''; - -// --- fail_todo_monomorphization_failure.carbon -library "[[@TEST_NAME]]"; - -import Cpp; - -class B(N:! i32) { - var a: array(i8, N); -} - -// TODO: Once we allow this, we should produce an "invalid array bound" error -// during monomorphization triggered by the C++ code. -// CHECK:STDERR: fail_todo_monomorphization_failure.carbon:[[@LINE+4]]:11: error: alias initializer must be a name reference [AliasRequiresNameRef] -// CHECK:STDERR: alias T = B(-1); -// CHECK:STDERR: ^~~~~ -// CHECK:STDERR: -alias T = B(-1); - -inline Cpp ''' -// CHECK:STDERR: fail_todo_monomorphization_failure.carbon:[[@LINE+4]]:9: error: no type named 'T' in namespace 'Carbon' [CppInteropParseError] -// CHECK:STDERR: 22 | Carbon::T t; -// CHECK:STDERR: | ~~~~~~~~^ -// CHECK:STDERR: -Carbon::T t; -'''; - -// --- fail_todo_fields.carbon -library "[[@TEST_NAME]]"; - -import Cpp; - -class A { - var a: i32; - var b: i32; -} - -inline Cpp ''' -// CHECK:STDERR: fail_todo_fields.carbon:[[@LINE+7]]:15: error: static assertion failed due to requirement 'sizeof(Carbon::A) == 2 * sizeof(int)' [CppInteropParseError] -// CHECK:STDERR: 18 | static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); -// CHECK:STDERR: | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_fields.carbon:[[@LINE+4]]:33: note: expression evaluates to '1 == 8' [CppInteropParseNote] -// CHECK:STDERR: 18 | static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); -// CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -static_assert(sizeof(Carbon::A) == 2 * sizeof(int)); -'''; - -// --- alias_identity.carbon -library "[[@TEST_NAME]]"; - -import Cpp; - -class A {} -alias B = A; - -inline Cpp ''' -// OK, Carbon::A and Carbon::B are the same type. -Carbon::A *pa; -Carbon::B *pb = pa; -'''; diff --git a/toolchain/lower/testdata/interop/cpp/reverse/class.carbon b/toolchain/lower/testdata/interop/cpp/reverse/class.carbon index 9eddb7c5414a..89b7b31a5af3 100644 --- a/toolchain/lower/testdata/interop/cpp/reverse/class.carbon +++ b/toolchain/lower/testdata/interop/cpp/reverse/class.carbon @@ -11,7 +11,6 @@ // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/reverse/class.carbon // --- pointer.carbon - library "[[@TEST_NAME]]"; import Cpp; @@ -28,6 +27,49 @@ void call() { pass(nullptr); } fn DoIt() { Cpp.call(); } +// --- layout.carbon + +library "[[@TEST_NAME]]"; + +import Cpp; + +base class A { + var x: ()*; + var y: i32; +} + +class B { + var a: A; + var b: i32; +} + +class C { + extend base: A; + var c: i32; +} + +fn CallA(a: A*); +fn CallB(b: B*); +fn CallC(b: C*); + +inline Cpp ''' +void f() { + Carbon::A a; + Carbon::CallA(&a); +} + +void g() { + Carbon::B b; + Carbon::CallB(&b); +} + +void h() { + Carbon::C c; + Carbon::CallC(&c); + Carbon::CallA(&c); +} +'''; + // CHECK:STDOUT: ; ModuleID = 'pointer.carbon' // CHECK:STDOUT: source_filename = "pointer.carbon" // CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" @@ -77,8 +119,153 @@ fn DoIt() { Cpp.call(); } // CHECK:STDOUT: !11 = !{!12, !12, i64 0} // CHECK:STDOUT: !12 = !{!"p1 _ZTSN6Carbon1AE", !13, i64 0} // CHECK:STDOUT: !13 = !{!"any pointer", !9, i64 0} -// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "DoIt", linkageName: "_CDoIt.Main", scope: null, file: !6, line: 16, type: !15, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "DoIt", linkageName: "_CDoIt.Main", scope: null, file: !6, line: 15, type: !15, spFlags: DISPFlagDefinition, unit: !5) // CHECK:STDOUT: !15 = !DISubroutineType(types: !16) // CHECK:STDOUT: !16 = !{null} -// CHECK:STDOUT: !17 = !DILocation(line: 16, column: 13, scope: !14) -// CHECK:STDOUT: !18 = !DILocation(line: 16, column: 1, scope: !14) +// CHECK:STDOUT: !17 = !DILocation(line: 15, column: 13, scope: !14) +// CHECK:STDOUT: !18 = !DILocation(line: 15, column: 1, scope: !14) +// CHECK:STDOUT: ; ModuleID = 'layout.carbon' +// CHECK:STDOUT: source_filename = "layout.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: %"class.Carbon::A" = type { [12 x i8] } +// CHECK:STDOUT: %"class.Carbon::B" = type { [16 x i8] } +// CHECK:STDOUT: %"class.Carbon::C" = type { [16 x i8] } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable +// CHECK:STDOUT: define dso_local void @_Z1fv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a = alloca %"class.Carbon::A", align 8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a) #3 +// CHECK:STDOUT: call void @_ZN6CarbonL16CallA__cpp_thunkEPNS_1AE(ptr noundef %a) +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %a) #3 +// CHECK:STDOUT: ret void +// 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: ; Function Attrs: alwaysinline mustprogress nounwind uwtable +// CHECK:STDOUT: define internal void @_ZN6CarbonL16CallA__cpp_thunkEPNS_1AE(ptr noundef %0) #2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %0, ptr %.addr, align 8, !tbaa !11 +// CHECK:STDOUT: call void @_CCallA__carbon_thunk.Main(ptr noundef nonnull align 8 dereferenceable(8) %.addr) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.end.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable +// CHECK:STDOUT: define dso_local void @_Z1gv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %b = alloca %"class.Carbon::B", align 8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %b) #3 +// CHECK:STDOUT: call void @_ZN6CarbonL16CallB__cpp_thunkEPNS_1BE(ptr noundef %b) +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %b) #3 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable +// CHECK:STDOUT: define internal void @_ZN6CarbonL16CallB__cpp_thunkEPNS_1BE(ptr noundef %0) #2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %0, ptr %.addr, align 8, !tbaa !14 +// CHECK:STDOUT: call void @_CCallB__carbon_thunk.Main(ptr noundef nonnull align 8 dereferenceable(8) %.addr) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable +// CHECK:STDOUT: define dso_local void @_Z1hv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %c = alloca %"class.Carbon::C", align 8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c) #3 +// CHECK:STDOUT: call void @_ZN6CarbonL16CallC__cpp_thunkEPNS_1CE(ptr noundef %c) +// CHECK:STDOUT: call void @_ZN6CarbonL16CallA__cpp_thunkEPNS_1AE(ptr noundef %c) +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %c) #3 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable +// CHECK:STDOUT: define internal void @_ZN6CarbonL16CallC__cpp_thunkEPNS_1CE(ptr noundef %0) #2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %0, ptr %.addr, align 8, !tbaa !16 +// CHECK:STDOUT: call void @_CCallC__carbon_thunk.Main(ptr noundef nonnull align 8 dereferenceable(8) %.addr) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CCallA.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CCallB.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CCallC.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CCallA__carbon_thunk.Main(ptr %_) #3 !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc21 = load ptr, ptr %_, align 8, !dbg !24 +// CHECK:STDOUT: call void @_CCallA.Main(ptr %.loc21), !dbg !24 +// CHECK:STDOUT: ret void, !dbg !24 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CCallB__carbon_thunk.Main(ptr %_) #3 !dbg !25 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc22 = load ptr, ptr %_, align 8, !dbg !28 +// CHECK:STDOUT: call void @_CCallB.Main(ptr %.loc22), !dbg !28 +// CHECK:STDOUT: ret void, !dbg !28 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CCallC__carbon_thunk.Main(ptr %_) #3 !dbg !29 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc23 = load ptr, ptr %_, align 8, !dbg !32 +// CHECK:STDOUT: call void @_CCallC.Main(ptr %.loc23), !dbg !32 +// CHECK:STDOUT: ret void, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #2 = { alwaysinline mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #3 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = !{i32 8, !"PIC Level", i32 2} +// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "layout.carbon", directory: "") +// CHECK:STDOUT: !7 = !{!8, !8, i64 0} +// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} +// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} +// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} +// CHECK:STDOUT: !11 = !{!12, !12, i64 0} +// CHECK:STDOUT: !12 = !{!"p1 _ZTSN6Carbon1AE", !13, i64 0} +// CHECK:STDOUT: !13 = !{!"any pointer", !9, i64 0} +// CHECK:STDOUT: !14 = !{!15, !15, i64 0} +// CHECK:STDOUT: !15 = !{!"p1 _ZTSN6Carbon1BE", !13, i64 0} +// CHECK:STDOUT: !16 = !{!17, !17, i64 0} +// CHECK:STDOUT: !17 = !{!"p1 _ZTSN6Carbon1CE", !13, i64 0} +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "CallA__carbon_thunk", linkageName: "_CCallA__carbon_thunk.Main", scope: null, file: !6, line: 21, type: !19, spFlags: DISPFlagDefinition, unit: !5, 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: 21, column: 1, scope: !18) +// CHECK:STDOUT: !25 = distinct !DISubprogram(name: "CallB__carbon_thunk", linkageName: "_CCallB__carbon_thunk.Main", scope: null, file: !6, line: 22, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !26) +// CHECK:STDOUT: !26 = !{!27} +// CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !25, type: !21) +// CHECK:STDOUT: !28 = !DILocation(line: 22, column: 1, scope: !25) +// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "CallC__carbon_thunk", linkageName: "_CCallC__carbon_thunk.Main", scope: null, file: !6, line: 23, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !30) +// CHECK:STDOUT: !30 = !{!31} +// CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !29, type: !21) +// CHECK:STDOUT: !32 = !DILocation(line: 23, column: 1, scope: !29)