From f2d0c4d0ae8cd16e8c3c365140e24a163bf166fa Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Wed, 3 Jun 2026 16:47:54 -0400 Subject: [PATCH] Export Carbon global variables and static vars to C++ (#7298) Add `ExportVarToCpp`. This checks the `clang_decls` mapping and returns an existing decl if found. Otherwise, it creates a new `VarDecl` and adds it to the `clang_decls` mapping. When lowering, in `FileContext::BuildGlobalVariableDecl`, the `clang_decls` mapping is used to lookup an existing `llvm::GlobalVariable` for the instruction. If found, use that rather than creating a new one to avoid an unwanted second definition in the llvm IR. --- toolchain/check/cpp/export.cpp | 63 +++++++++++ toolchain/check/cpp/export.h | 7 ++ toolchain/check/cpp/generate_ast.cpp | 4 + .../interop/cpp/class/export/static.carbon | 25 +++++ .../interop/cpp/primitive_types/array.carbon | 6 +- .../interop/cpp/var/export/var.carbon | 45 ++++++++ .../cpp/var/{ => import}/constexpr.carbon | 4 +- .../cpp/var/{ => import}/global.carbon | 4 +- .../cpp/var/{ => import}/namespace.carbon | 4 +- toolchain/lower/file_context.cpp | 15 +++ toolchain/lower/testdata/class/static.carbon | 101 ++++++++++++++++++ toolchain/lower/testdata/var/export.carbon | 87 +++++++++++++++ 12 files changed, 354 insertions(+), 11 deletions(-) create mode 100644 toolchain/check/testdata/interop/cpp/class/export/static.carbon create mode 100644 toolchain/check/testdata/interop/cpp/var/export/var.carbon rename toolchain/check/testdata/interop/cpp/var/{ => import}/constexpr.carbon (88%) rename toolchain/check/testdata/interop/cpp/var/{ => import}/global.carbon (98%) rename toolchain/check/testdata/interop/cpp/var/{ => import}/namespace.carbon (93%) create mode 100644 toolchain/lower/testdata/var/export.carbon diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 4b2be4615b0d..5dee85ec99af 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -17,6 +17,7 @@ #include "toolchain/check/type.h" #include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/mangler.h" +#include "toolchain/sem_ir/pattern.h" #include "toolchain/sem_ir/typed_insts.h" #include "toolchain/sem_ir/vtable.h" @@ -937,4 +938,66 @@ auto ExportDestructorToCpp(Context& context, const SemIR::Class& class_info, return cpp_destructor_decl; } +auto ExportVarToCpp(Context& context, SemIR::LocId loc_id, + SemIR::VarStorage var_storage) -> clang::VarDecl* { + // Check if the variable was already exported and return the existing + // `VarDecl` if so. Note that the `pattern_id` is used as the key + // rather than the `InstId` for the `VarStorage`. This just makes + // lookup more convenient in places where the `VarStorage` `InstId` is + // not readily accessible. + auto clang_decl_id = context.clang_decls().Lookup(var_storage.pattern_id); + if (clang_decl_id.has_value()) { + return cast( + context.clang_decls().Get(clang_decl_id).key.decl); + } + + // Look up the entity name and check the scope. + auto entity_name_id = GetFirstBindingNameFromPatternId( + context.sem_ir(), var_storage.pattern_id); + const auto& entity_name = context.entity_names().Get(entity_name_id); + auto scope_inst = context.insts().Get( + context.name_scopes().Get(entity_name.parent_scope_id).inst_id()); + CARBON_CHECK(scope_inst.Is() || + scope_inst.Is()); + + // Map the parent scope into the C++ AST. + auto* decl_context = + ExportNameScopeToCpp(context, loc_id, entity_name.parent_scope_id); + if (!decl_context) { + return nullptr; + } + + // Map the type. + auto cpp_type = MapToCppType(context, var_storage.type_id); + if (cpp_type.isNull()) { + context.TODO(loc_id, "failed to map Carbon type to C++"); + return nullptr; + } + + // Create the `clang::VarDecl` and add it to `clang_decls()`. + auto clang_loc = GetCppLocation(context, loc_id); + auto* identifier_info = GetClangIdentifierInfo(context, entity_name.name_id); + auto* var_decl = clang::VarDecl::Create( + context.ast_context(), decl_context, + /*StartLoc=*/clang_loc, /*IdLoc=*/clang_loc, identifier_info, cpp_type, + /*TInfo=*/nullptr, clang::SC_Extern); + context.clang_decls().Add( + {.key = SemIR::ClangDeclKey::ForNonFunctionDecl(var_decl), + .inst_id = var_storage.pattern_id}); + + if (scope_inst.Is()) { + // TODO: Map Carbon access to C++ access. + var_decl->setAccess(clang::AS_public); + } + + // Set the Carbon mangled variable name. + SemIR::Mangler m(context.sem_ir(), context.total_ir_count(), + context.mangle_string_fingerprint()); + std::string mangled_name = m.MangleGlobalVariable(var_storage.pattern_id); + var_decl->addAttr( + clang::AsmLabelAttr::Create(context.ast_context(), mangled_name)); + + return var_decl; +} + } // namespace Carbon::Check diff --git a/toolchain/check/cpp/export.h b/toolchain/check/cpp/export.h index 7de607c6ec65..e36ae3146aca 100644 --- a/toolchain/check/cpp/export.h +++ b/toolchain/check/cpp/export.h @@ -64,6 +64,13 @@ auto ExportDestructorToCpp(Context& context, const SemIR::Class& class_info, clang::CXXRecordDecl* record_decl) -> clang::CXXDestructorDecl*; +// Export a Carbon variable into C++. +// +// Returns nullptr if the variable could not be exported an an error was +// diagnosed. +auto ExportVarToCpp(Context& context, SemIR::LocId loc_id, + SemIR::VarStorage var_storage) -> clang::VarDecl*; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CPP_EXPORT_H_ diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index 94026954a69d..320c67e72c3e 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -435,6 +435,10 @@ auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup) case CARBON_KIND(SemIR::FieldDecl field_decl): { return ExportFieldToCpp(*context_, target_inst_id, field_decl); } + case CARBON_KIND(SemIR::VarStorage var_storage): { + return ExportVarToCpp(*context_, SemIR::LocId(target_inst_id), + var_storage); + } default: return nullptr; } diff --git a/toolchain/check/testdata/interop/cpp/class/export/static.carbon b/toolchain/check/testdata/interop/cpp/class/export/static.carbon new file mode 100644 index 000000000000..9efa7a51e639 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/class/export/static.carbon @@ -0,0 +1,25 @@ +// 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/static.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/class/export/static.carbon + +// --- static.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +class C { + static var x: i32 = 123; +} + +inline Cpp ''' +int F() { + return Carbon::C::x; +} +'''; diff --git a/toolchain/check/testdata/interop/cpp/primitive_types/array.carbon b/toolchain/check/testdata/interop/cpp/primitive_types/array.carbon index 400de55acaef..da85204a39f7 100644 --- a/toolchain/check/testdata/interop/cpp/primitive_types/array.carbon +++ b/toolchain/check/testdata/interop/cpp/primitive_types/array.carbon @@ -30,7 +30,7 @@ fn G(x: Cpp.X, n: i32) -> i32 { return x.arr[n]; } -// --- fail_todo_export_variable.carbon +// --- export_variable.carbon library "[[@TEST_NAME]]"; import Cpp; @@ -39,10 +39,6 @@ var arr: array(i32, 5); inline Cpp ''' int F(int n) { - // CHECK:STDERR: fail_todo_export_variable.carbon:[[@LINE+4]]:18: error: no member named 'arr' in namespace 'Carbon' [CppInteropParseError] - // CHECK:STDERR: 13 | return Carbon::arr[n]; - // CHECK:STDERR: | ^~~ - // CHECK:STDERR: return Carbon::arr[n]; } '''; diff --git a/toolchain/check/testdata/interop/cpp/var/export/var.carbon b/toolchain/check/testdata/interop/cpp/var/export/var.carbon new file mode 100644 index 000000000000..8f4d64fac370 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/var/export/var.carbon @@ -0,0 +1,45 @@ +// 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/var/export/var.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/var/export/var.carbon + +// --- var.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +var x: i32 = 123; + +inline Cpp ''' +int Get() { + return Carbon::x; +} +'''; + +// --- fail_var_unsupported_type.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +// CHECK:STDERR: fail_var_unsupported_type.carbon:[[@LINE+4]]:5: error: semantics TODO: `failed to map Carbon type to C++` [SemanticsTodo] +// CHECK:STDERR: var x: () = (); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +var x: () = (); + +inline Cpp ''' +void F() { + // CHECK:STDERR: fail_var_unsupported_type.carbon:[[@LINE+4]]:11: error: no member named 'x' in namespace 'Carbon' [CppInteropParseError] + // CHECK:STDERR: 17 | Carbon::x; + // CHECK:STDERR: | ^ + // CHECK:STDERR: + Carbon::x; +} +'''; diff --git a/toolchain/check/testdata/interop/cpp/var/constexpr.carbon b/toolchain/check/testdata/interop/cpp/var/import/constexpr.carbon similarity index 88% rename from toolchain/check/testdata/interop/cpp/var/constexpr.carbon rename to toolchain/check/testdata/interop/cpp/var/import/constexpr.carbon index f313eb26f905..48d47f00086f 100644 --- a/toolchain/check/testdata/interop/cpp/var/constexpr.carbon +++ b/toolchain/check/testdata/interop/cpp/var/import/constexpr.carbon @@ -7,9 +7,9 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/var/constexpr.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/var/import/constexpr.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/var/constexpr.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/var/import/constexpr.carbon // --- bool.carbon diff --git a/toolchain/check/testdata/interop/cpp/var/global.carbon b/toolchain/check/testdata/interop/cpp/var/import/global.carbon similarity index 98% rename from toolchain/check/testdata/interop/cpp/var/global.carbon rename to toolchain/check/testdata/interop/cpp/var/import/global.carbon index 5764bf5c10ab..79528388fc69 100644 --- a/toolchain/check/testdata/interop/cpp/var/global.carbon +++ b/toolchain/check/testdata/interop/cpp/var/import/global.carbon @@ -7,9 +7,9 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/var/global.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/var/import/global.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/var/global.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/var/import/global.carbon // ============================================================================ // Global scope diff --git a/toolchain/check/testdata/interop/cpp/var/namespace.carbon b/toolchain/check/testdata/interop/cpp/var/import/namespace.carbon similarity index 93% rename from toolchain/check/testdata/interop/cpp/var/namespace.carbon rename to toolchain/check/testdata/interop/cpp/var/import/namespace.carbon index 97a0a75d1491..c0b81b6ca657 100644 --- a/toolchain/check/testdata/interop/cpp/var/namespace.carbon +++ b/toolchain/check/testdata/interop/cpp/var/import/namespace.carbon @@ -6,9 +6,9 @@ // // AUTOUPDATE // TIP: To test this file alone, run: -// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/var/namespace.carbon +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/var/import/namespace.carbon // TIP: To dump output, run: -// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/var/namespace.carbon +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/var/import/namespace.carbon // --- namespace.h diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index b30af36d07aa..05d3f257ada3 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -672,6 +672,21 @@ auto FileContext::BuildDISubprogram(const SemIR::Function& function, auto FileContext::BuildGlobalVariableDecl(SemIR::VarStorage var_storage) -> llvm::Constant* { + // When a Carbon variable is exported and used from C++, code + // generation for the C++ code may have already created an + // llvm::GlobalVariable. If so, return that global rather than + // creating a new one. + auto clang_decl_id = sem_ir().clang_decls().Lookup(var_storage.pattern_id); + if (clang_decl_id.has_value()) { + auto* decl = sem_ir().clang_decls().Get(clang_decl_id).key.decl; + auto* constant = cpp_code_generator_->GetAddrOfGlobal( + CreateGlobalDecl(cast(decl)), + /*isForDefinition=*/false); + if (constant) { + return constant; + } + } + auto var_name_id = SemIR::GetFirstBindingNameFromPatternId(sem_ir(), var_storage.pattern_id); if (auto cpp_global_var_id = diff --git a/toolchain/lower/testdata/class/static.carbon b/toolchain/lower/testdata/class/static.carbon index ec977c3f5942..f1681dfe0d2c 100644 --- a/toolchain/lower/testdata/class/static.carbon +++ b/toolchain/lower/testdata/class/static.carbon @@ -23,6 +23,24 @@ fn F() { C.static_field_with_init = 789; } +// --- static_export.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +class C { + static var x: i32 = 123; +} + +inline Cpp ''' +int F() { + return Carbon::C::x; +} +'''; + +fn Run() -> i32 { + return Cpp.F(); +} + // CHECK:STDOUT: ; ModuleID = 'static.carbon' // CHECK:STDOUT: source_filename = "static.carbon" // CHECK:STDOUT: @@ -65,3 +83,86 @@ fn F() { // CHECK:STDOUT: !11 = !DILocation(line: 4, column: 10, scope: !10) // CHECK:STDOUT: !12 = !DILocation(line: 5, column: 10, scope: !10) // CHECK:STDOUT: !13 = !DILocation(line: 0, scope: !10) +// CHECK:STDOUT: ; ModuleID = 'static_export.carbon' +// CHECK:STDOUT: source_filename = "static_export.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: @_Cx.C.Main = global i32 0, align 4 +// 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: mustprogress nounwind uwtable +// CHECK:STDOUT: define dso_local noundef i32 @_Z1Fv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %0 = load i32, ptr @_Cx.C.Main, align 4, !tbaa !7 +// CHECK:STDOUT: ret i32 %0 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.C.Main"(ptr %self) #1 !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_COp.907c496eccf2294c:core.Destroy.Core"(ptr %self), !dbg !17 +// CHECK:STDOUT: ret void, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.907c496eccf2294c:core.Destroy.Core"(ptr %self) #2 !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @main() #2 !dbg !22 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %F.call = call i32 @_Z1Fv(), !dbg !26 +// CHECK:STDOUT: ret i32 %F.call, !dbg !27 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define internal void @_C__global_init.Main() #2 !dbg !28 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: store i32 123, ptr @_Cx.C.Main, align 4, !dbg !31 +// CHECK:STDOUT: ret void, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { 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 #1 = { alwaysinline nounwind } +// CHECK:STDOUT: attributes #2 = { 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: "static_export.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 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.C.Main", scope: null, file: !6, line: 4, type: !12, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !15) +// CHECK:STDOUT: !12 = !DISubroutineType(types: !13) +// CHECK:STDOUT: !13 = !{null, !14} +// CHECK:STDOUT: !14 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !15 = !{!16} +// CHECK:STDOUT: !16 = !DILocalVariable(arg: 1, scope: !11, type: !14) +// CHECK:STDOUT: !17 = !DILocation(line: 4, column: 1, scope: !11) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "Op", linkageName: "_COp.907c496eccf2294c:core.Destroy.Core", scope: null, file: !6, line: 4, type: !12, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !19) +// CHECK:STDOUT: !19 = !{!20} +// CHECK:STDOUT: !20 = !DILocalVariable(arg: 1, scope: !18, type: !14) +// CHECK:STDOUT: !21 = !DILocation(line: 4, column: 1, scope: !18) +// CHECK:STDOUT: !22 = distinct !DISubprogram(name: "Run", linkageName: "main", scope: null, file: !6, line: 14, type: !23, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !23 = !DISubroutineType(types: !24) +// CHECK:STDOUT: !24 = !{!25} +// CHECK:STDOUT: !25 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !26 = !DILocation(line: 15, column: 10, scope: !22) +// CHECK:STDOUT: !27 = !DILocation(line: 15, column: 3, scope: !22) +// CHECK:STDOUT: !28 = distinct !DISubprogram(name: "__global_init", linkageName: "_C__global_init.Main", scope: null, file: !6, type: !29, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !29 = !DISubroutineType(types: !30) +// CHECK:STDOUT: !30 = !{null} +// CHECK:STDOUT: !31 = !DILocation(line: 5, column: 10, scope: !28) +// CHECK:STDOUT: !32 = !DILocation(line: 0, scope: !28) diff --git a/toolchain/lower/testdata/var/export.carbon b/toolchain/lower/testdata/var/export.carbon new file mode 100644 index 000000000000..f05b2b31c885 --- /dev/null +++ b/toolchain/lower/testdata/var/export.carbon @@ -0,0 +1,87 @@ +// 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/var/export.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/var/export.carbon + +// --- var_export.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +var x: i32; + +inline Cpp ''' +int Get() { + return Carbon::x; +} +'''; + +fn Run() -> i32 { + return Cpp.Get(); +} + +// CHECK:STDOUT: ; ModuleID = 'var_export.carbon' +// CHECK:STDOUT: source_filename = "var_export.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: @_Cx.Main = global i32 0, align 4 +// 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: mustprogress nounwind uwtable +// CHECK:STDOUT: define dso_local noundef i32 @_Z3Getv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %0 = load i32, ptr @_Cx.Main, align 4, !tbaa !7 +// CHECK:STDOUT: ret i32 %0 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @main() #1 !dbg !11 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %Get.call = call i32 @_Z3Getv(), !dbg !15 +// CHECK:STDOUT: ret i32 %Get.call, !dbg !16 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define internal void @_C__global_init.Main() #1 !dbg !17 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: store i32 poison, ptr @_Cx.Main, align 4, !dbg !20 +// CHECK:STDOUT: ret void, !dbg !21 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { 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 #1 = { 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: "var_export.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 = distinct !DISubprogram(name: "Run", linkageName: "main", scope: null, file: !6, line: 13, type: !12, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !12 = !DISubroutineType(types: !13) +// CHECK:STDOUT: !13 = !{!14} +// CHECK:STDOUT: !14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !15 = !DILocation(line: 14, column: 10, scope: !11) +// CHECK:STDOUT: !16 = !DILocation(line: 14, column: 3, scope: !11) +// CHECK:STDOUT: !17 = distinct !DISubprogram(name: "__global_init", linkageName: "_C__global_init.Main", scope: null, file: !6, type: !18, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !18 = !DISubroutineType(types: !19) +// CHECK:STDOUT: !19 = !{null} +// CHECK:STDOUT: !20 = !DILocation(line: 5, column: 1, scope: !17) +// CHECK:STDOUT: !21 = !DILocation(line: 0, scope: !17)