Просмотр исходного кода

[C++ interop] Pass top-level declarations to the code generator. (#6237)

This allows us to lower indirectly-referenced C++ functions and
variables.
Richard Smith 6 месяцев назад
Родитель
Сommit
8cf4c4d10d

+ 3 - 0
toolchain/check/cpp/thunk.cpp

@@ -4,6 +4,7 @@
 
 #include "toolchain/check/cpp/thunk.h"
 
+#include "clang/AST/ASTConsumer.h"
 #include "clang/AST/GlobalDecl.h"
 #include "clang/AST/Mangle.h"
 #include "clang/Sema/Lookup.h"
@@ -569,6 +570,8 @@ auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
     return nullptr;
   }
 
+  context.clang_sema().getASTConsumer().HandleTopLevelDecl(
+      clang::DeclGroupRef(thunk_function_decl));
   return thunk_function_decl;
 }
 

+ 20 - 9
toolchain/lower/file_context.cpp

@@ -72,6 +72,26 @@ auto FileContext::PrepareToLower() -> void {
     // const-correct.
     cpp_code_generator_->Initialize(
         const_cast<clang::ASTContext&>(clang_ast_unit()->getASTContext()));
+
+    // Work around `visitLocalTopLevelDecls` not being const. It doesn't modify
+    // the AST unit other than triggering deserialization.
+    auto* non_const_ast_unit = const_cast<clang::ASTUnit*>(clang_ast_unit());
+
+    // Emit any top-level declarations now.
+    // TODO: This may miss things that we need to emit which are handed to the
+    // ASTConsumer in other ways. Instead of doing this, we should create the
+    // CodeGenerator earlier and register it as an ASTConsumer before we parse
+    // the C++ inputs.
+    non_const_ast_unit->visitLocalTopLevelDecls(
+        cpp_code_generator_.get(),
+        [](void* codegen_ptr, const clang::Decl* decl) {
+          auto* codegen = static_cast<clang::CodeGenerator*>(codegen_ptr);
+          // CodeGenerator won't modify the declaration it's given, but we can
+          // only call it via the ASTConsumer interface which doesn't know that.
+          auto* non_const_decl = const_cast<clang::Decl*>(decl);
+          codegen->HandleTopLevelDecl(clang::DeclGroupRef(non_const_decl));
+          return true;
+        });
   }
 
   // Lower all types that were required to be complete.
@@ -396,9 +416,6 @@ auto FileContext::HandleReferencedCppFunction(clang::FunctionDecl* cpp_decl)
       cpp_code_generator_->GetAddrOfGlobal(CreateGlobalDecl(cpp_def),
                                            /*isForDefinition=*/false);
   CARBON_CHECK(function_address);
-
-  // Emit the function code.
-  cpp_code_generator_->HandleTopLevelDecl(clang::DeclGroupRef(cpp_def));
 }
 
 auto FileContext::HandleReferencedSpecificFunction(
@@ -442,12 +459,6 @@ auto FileContext::BuildFunctionDecl(SemIR::FunctionId function_id,
   // corresponding C++ function anyway.
   if (function.special_function_kind ==
       SemIR::Function::SpecialFunctionKind::HasCppThunk) {
-    // Make sure Clang emits this function.
-    // TODO: This shouldn't be necessary: Clang should emit definitions of
-    // functions that it emits calls to. But this doesn't currently work.
-    auto clang_decl_id = sem_ir().functions().Get(function_id).clang_decl_id;
-    HandleReferencedCppFunction(cast<clang::FunctionDecl>(
-        sem_ir().clang_decls().Get(clang_decl_id).key.decl));
     return nullptr;
   }
 

+ 13 - 7
toolchain/lower/testdata/interop/cpp/function_decl.carbon

@@ -95,14 +95,14 @@ fn MyF() {
 inline void foo1() {}
 inline void foo2() { foo1(); }
 
-// --- todo_import_inline_recursive_function_decl.carbon
+// --- import_inline_recursive_function_decl.carbon
 
 library "[[@TEST_NAME]]";
 
 import Cpp library "inline_recursive_function_decl.h";
 
 fn MyF() {
-  // TODO: This should generate the definition of the inline function `foo1()`.
+  // This should generate the definition of the inline function `foo1()`.
   Cpp.foo2();
 }
 
@@ -292,13 +292,15 @@ fn MyF() {
 // CHECK:STDOUT: !11 = !DILocation(line: 8, column: 3, scope: !7)
 // CHECK:STDOUT: !12 = !DILocation(line: 9, column: 3, scope: !7)
 // CHECK:STDOUT: !13 = !DILocation(line: 6, column: 1, scope: !7)
-// CHECK:STDOUT: ; ModuleID = 'todo_import_inline_recursive_function_decl.carbon'
-// CHECK:STDOUT: source_filename = "todo_import_inline_recursive_function_decl.carbon"
+// CHECK:STDOUT: ; ModuleID = 'import_inline_recursive_function_decl.carbon'
+// CHECK:STDOUT: source_filename = "import_inline_recursive_function_decl.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: $_Z4foo2v = comdat any
 // CHECK:STDOUT:
+// CHECK:STDOUT: $_Z4foo1v = comdat any
+// CHECK:STDOUT:
 // CHECK:STDOUT: define void @_CMyF.Main() !dbg !7 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   call void @_Z4foo2v(), !dbg !10
@@ -312,10 +314,14 @@ fn MyF() {
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: declare void @_Z4foo1v() #1
+// CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
+// CHECK:STDOUT: define linkonce_odr dso_local void @_Z4foo1v() #1 comdat {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   ret void
+// CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: attributes #0 = { mustprogress noinline optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
-// CHECK:STDOUT: attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #1 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}
@@ -326,7 +332,7 @@ fn MyF() {
 // CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0}
 // CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2}
 // CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
-// CHECK:STDOUT: !6 = !DIFile(filename: "todo_import_inline_recursive_function_decl.carbon", directory: "")
+// CHECK:STDOUT: !6 = !DIFile(filename: "import_inline_recursive_function_decl.carbon", directory: "")
 // CHECK:STDOUT: !7 = distinct !DISubprogram(name: "MyF", linkageName: "_CMyF.Main", scope: null, file: !6, line: 6, type: !8, spFlags: DISPFlagDefinition, unit: !5)
 // CHECK:STDOUT: !8 = !DISubroutineType(types: !9)
 // CHECK:STDOUT: !9 = !{}

+ 3 - 1
toolchain/lower/testdata/interop/cpp/globals.carbon

@@ -34,7 +34,9 @@ fn MyF() {
 // 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: @global = external global {}
+// CHECK:STDOUT: %class.C = type { i8 }
+// CHECK:STDOUT:
+// CHECK:STDOUT: @global = dso_local global %class.C zeroinitializer, align 1
 // CHECK:STDOUT:
 // CHECK:STDOUT: define void @_CMyF.Main() !dbg !7 {
 // CHECK:STDOUT: entry:

+ 15 - 15
toolchain/lower/testdata/interop/cpp/template.carbon

@@ -37,10 +37,10 @@ fn PassClass(a: Cpp.Class) -> Cpp.Class {
 // CHECK:STDOUT:
 // CHECK:STDOUT: %class.Class = type { i8 }
 // CHECK:STDOUT:
-// CHECK:STDOUT: $_Z8identityIiET_S0_ = comdat any
-// CHECK:STDOUT:
 // CHECK:STDOUT: $_Z8identityI5ClassET_S1_ = comdat any
 // CHECK:STDOUT:
+// CHECK:STDOUT: $_Z8identityIiET_S0_ = comdat any
+// CHECK:STDOUT:
 // CHECK:STDOUT: define i32 @_CPassI32.Main(i32 %a) !dbg !7 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %identity.call = call i32 @_Z8identityIiET_S0_(i32 %a), !dbg !10
@@ -53,17 +53,8 @@ fn PassClass(a: Cpp.Class) -> Cpp.Class {
 // CHECK:STDOUT:   ret void, !dbg !14
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
-// CHECK:STDOUT: define linkonce_odr dso_local i32 @_Z8identityIiET_S0_(i32 %x) #0 comdat {
-// CHECK:STDOUT: entry:
-// CHECK:STDOUT:   %x.addr = alloca i32, align 4
-// CHECK:STDOUT:   store i32 %x, ptr %x.addr, align 4
-// CHECK:STDOUT:   %0 = load i32, ptr %x.addr, align 4
-// CHECK:STDOUT:   ret i32 %0
-// CHECK:STDOUT: }
-// CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress
-// CHECK:STDOUT: define dso_local void @_Z8identityI5ClassET_S1_.carbon_thunk(ptr %x, ptr %return) #1 {
+// CHECK:STDOUT: define dso_local void @_Z8identityI5ClassET_S1_.carbon_thunk(ptr %x, ptr %return) #0 {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x.addr = alloca ptr, align 8
 // CHECK:STDOUT:   %return.addr = alloca ptr, align 8
@@ -78,14 +69,23 @@ fn PassClass(a: Cpp.Class) -> Cpp.Class {
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
-// CHECK:STDOUT: define linkonce_odr dso_local void @_Z8identityI5ClassET_S1_() #0 comdat {
+// CHECK:STDOUT: define linkonce_odr dso_local void @_Z8identityI5ClassET_S1_() #1 comdat {
 // CHECK:STDOUT: entry:
 // CHECK:STDOUT:   %x = alloca %class.Class, align 1
 // CHECK:STDOUT:   ret void
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
-// CHECK:STDOUT: attributes #0 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
-// CHECK:STDOUT: attributes #1 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: ; Function Attrs: mustprogress noinline nounwind optnone
+// CHECK:STDOUT: define linkonce_odr dso_local i32 @_Z8identityIiET_S0_(i32 %x) #1 comdat {
+// CHECK:STDOUT: entry:
+// CHECK:STDOUT:   %x.addr = alloca i32, align 4
+// CHECK:STDOUT:   store i32 %x, ptr %x.addr, align 4
+// CHECK:STDOUT:   %0 = load i32, ptr %x.addr, align 4
+// CHECK:STDOUT:   ret i32 %0
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: attributes #0 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
+// CHECK:STDOUT: attributes #1 = { mustprogress noinline nounwind optnone "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
 // CHECK:STDOUT:
 // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4}
 // CHECK:STDOUT: !llvm.dbg.cu = !{!5}