mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 12:21:06 +01:00
Generate and use a C++ thunk to call non simple ABI C++ functions (#5850)
When the C++ function has a parameter that is not a pointer and not a
signed integer of 32 or 64 bits, generate a thunk.
Terminology:
* Callee function: The C++ function we actually want to call.
* Thunk function: The C++ function we generated that calls the callee
function.
* A simple ABI type, for now, is one of:
* A pointer
* signed integer with 32 bits
* signed integer with 64 bits
The thunk function is marked `always_inline` and uses the `asm`
attribute to set its mangled to the callee function mangled name
suffixed with `".carbon_thunk"`.
When importing a C++ function, we decide whether calling it requires a
thunk and if so we generate it and import it as well, which is currently
a recursive call.
When calling the thunk function, we initialize a temporary storage for
each non simple ABI parameter type and take its address. This can be
optimized when the variable is already in storage.
Not supported yet:
* Functions with non void return values.
* Member methods.
Moved unsigned int param test from `arithmetic_types_direct.carbon` to
`arithmetic_types_bridged.carbon`, since only signed integers aren't
bridged using a thunk.
C++ Interop Demo:
```c++
// hello_world.h
struct S {
S() {}
S(const S&) { x = 1; }
int x;
};
void hello_world(S s);
```
```c++
// hello_world.cpp
#include "hello_world.h"
#include <cstdio>
void hello_world2(S s) { printf("hello_world2: %d\n", s.x); }
void hello_world(S s) {
printf("hello_world: %d\n", s.x);
hello_world2(s);
}
```
```carbon
// main.carbon
library "Main";
import Cpp library "hello_world.h";
fn Run() -> i32 {
var s : Cpp.S;
Cpp.hello_world(s);
return 0;
}
```
```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ ./demo
hello_world: 1
hello_world2: 1
```
Before this change (no thunk - copy constructor not called when calling
`hello_world()`):
```shell
$ ./demo
hello_world: -1219172304
hello_world2: 1
```
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include "toolchain/check/class.h"
|
||||
#include "toolchain/check/context.h"
|
||||
#include "toolchain/check/convert.h"
|
||||
#include "toolchain/check/cpp_thunk.h"
|
||||
#include "toolchain/check/diagnostic_helpers.h"
|
||||
#include "toolchain/check/eval.h"
|
||||
#include "toolchain/check/function.h"
|
||||
@@ -38,11 +39,13 @@
|
||||
#include "toolchain/check/pattern.h"
|
||||
#include "toolchain/check/pattern_match.h"
|
||||
#include "toolchain/check/type.h"
|
||||
#include "toolchain/check/type_completion.h"
|
||||
#include "toolchain/diagnostics/diagnostic.h"
|
||||
#include "toolchain/diagnostics/diagnostic_emitter.h"
|
||||
#include "toolchain/diagnostics/format_providers.h"
|
||||
#include "toolchain/parse/node_ids.h"
|
||||
#include "toolchain/sem_ir/clang_decl.h"
|
||||
#include "toolchain/sem_ir/function.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/inst.h"
|
||||
#include "toolchain/sem_ir/name_scope.h"
|
||||
@@ -932,7 +935,8 @@ static auto MakeIntType(Context& context, IntId size_id, bool is_signed)
|
||||
|
||||
// Maps a C++ builtin type to a Carbon type.
|
||||
// TODO: Support more builtin types.
|
||||
static auto MapBuiltinType(Context& context, clang::QualType qual_type,
|
||||
static auto MapBuiltinType(Context& context, SemIR::LocId loc_id,
|
||||
clang::QualType qual_type,
|
||||
const clang::BuiltinType& type) -> TypeExpr {
|
||||
clang::ASTContext& ast_context = context.ast_context();
|
||||
if (type.isBooleanType()) {
|
||||
@@ -942,11 +946,22 @@ static auto MapBuiltinType(Context& context, clang::QualType qual_type,
|
||||
context, SemIR::BoolType::TypeInstId)));
|
||||
}
|
||||
if (type.isInteger()) {
|
||||
auto width = ast_context.getIntWidth(qual_type);
|
||||
unsigned width = context.ast_context().getIntWidth(qual_type);
|
||||
bool is_signed = type.isSignedInteger();
|
||||
auto int_n_type = ast_context.getIntTypeForBitwidth(width, is_signed);
|
||||
if (ast_context.hasSameType(qual_type, int_n_type)) {
|
||||
return MakeIntType(context, context.ints().Add(width), is_signed);
|
||||
auto int_n_type =
|
||||
context.ast_context().getIntTypeForBitwidth(width, is_signed);
|
||||
if (context.ast_context().hasSameType(qual_type, int_n_type)) {
|
||||
TypeExpr type_expr =
|
||||
MakeIntType(context, context.ints().Add(width), is_signed);
|
||||
// Try to make sure signed integer of 32 or 64 bits are complete so we can
|
||||
// check against them when deciding whether we need to generate a thunk.
|
||||
if (is_signed && (width == 32 || width == 64)) {
|
||||
SemIR::TypeId type_id = type_expr.type_id;
|
||||
if (!context.types().IsComplete(type_id)) {
|
||||
TryToCompleteType(context, type_id, loc_id);
|
||||
}
|
||||
}
|
||||
return type_expr;
|
||||
}
|
||||
// TODO: Handle integer types that map to named aliases.
|
||||
} else if (type.isDoubleType()) {
|
||||
@@ -986,10 +1001,10 @@ static auto MapRecordType(Context& context, const clang::RecordType& type)
|
||||
// Maps a C++ type that is not a wrapper type such as a pointer to a Carbon
|
||||
// type.
|
||||
// TODO: Support more types.
|
||||
static auto MapNonWrapperType(Context& context, clang::QualType type)
|
||||
-> TypeExpr {
|
||||
static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id,
|
||||
clang::QualType type) -> TypeExpr {
|
||||
if (const auto* builtin_type = type->getAs<clang::BuiltinType>()) {
|
||||
return MapBuiltinType(context, type, *builtin_type);
|
||||
return MapBuiltinType(context, loc_id, type, *builtin_type);
|
||||
}
|
||||
|
||||
if (const auto* record_type = type->getAs<clang::RecordType>()) {
|
||||
@@ -1066,7 +1081,7 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
|
||||
wrapper_types.push_back(orig_type);
|
||||
}
|
||||
|
||||
auto mapped = MapNonWrapperType(context, type);
|
||||
auto mapped = MapNonWrapperType(context, loc_id, type);
|
||||
|
||||
for (auto wrapper : llvm::reverse(wrapper_types)) {
|
||||
if (!mapped.inst_id.has_value() ||
|
||||
@@ -1300,6 +1315,64 @@ static auto CreateFunctionParamsInsts(Context& context, SemIR::LocId loc_id,
|
||||
.call_params_id = call_params_id}};
|
||||
}
|
||||
|
||||
// Creates a `FunctionDecl` and a `Function` without C++ thunk information.
|
||||
// Returns std::nullopt on failure. The given Clang declaration is assumed to:
|
||||
// * Have not been imported before.
|
||||
// * Be of supported type (ignoring parameters).
|
||||
static auto ImportFunction(Context& context, SemIR::LocId loc_id,
|
||||
clang::FunctionDecl* clang_decl)
|
||||
-> std::optional<SemIR::FunctionId> {
|
||||
context.scope_stack().PushForDeclName();
|
||||
context.inst_block_stack().Push();
|
||||
context.pattern_block_stack().Push();
|
||||
|
||||
auto function_params_insts =
|
||||
CreateFunctionParamsInsts(context, loc_id, clang_decl);
|
||||
|
||||
auto pattern_block_id = context.pattern_block_stack().Pop();
|
||||
auto decl_block_id = context.inst_block_stack().Pop();
|
||||
context.scope_stack().Pop();
|
||||
|
||||
if (!function_params_insts.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto function_decl = SemIR::FunctionDecl{
|
||||
SemIR::TypeId::None, SemIR::FunctionId::None, decl_block_id};
|
||||
auto decl_id =
|
||||
AddPlaceholderInstInNoBlock(context, Parse::NodeId::None, function_decl);
|
||||
context.imports().push_back(decl_id);
|
||||
|
||||
auto function_info = SemIR::Function{
|
||||
{.name_id = AddIdentifierName(context, clang_decl->getName()),
|
||||
.parent_scope_id = GetParentNameScopeId(context, clang_decl),
|
||||
.generic_id = SemIR::GenericId::None,
|
||||
.first_param_node_id = Parse::NodeId::None,
|
||||
.last_param_node_id = Parse::NodeId::None,
|
||||
.pattern_block_id = pattern_block_id,
|
||||
.implicit_param_patterns_id =
|
||||
function_params_insts->implicit_param_patterns_id,
|
||||
.param_patterns_id = function_params_insts->param_patterns_id,
|
||||
.is_extern = false,
|
||||
.extern_library_id = SemIR::LibraryNameId::None,
|
||||
.non_owning_decl_id = SemIR::InstId::None,
|
||||
.first_owning_decl_id = decl_id,
|
||||
.definition_id = SemIR::InstId::None},
|
||||
{.call_params_id = function_params_insts->call_params_id,
|
||||
.return_slot_pattern_id = function_params_insts->return_slot_pattern_id,
|
||||
.virtual_modifier = SemIR::FunctionFields::VirtualModifier::None,
|
||||
.self_param_id = FindSelfPattern(
|
||||
context, function_params_insts->implicit_param_patterns_id),
|
||||
.clang_decl_id = context.sem_ir().clang_decls().Add(
|
||||
{.decl = clang_decl, .inst_id = decl_id})}};
|
||||
|
||||
function_decl.function_id = context.functions().Add(function_info);
|
||||
function_decl.type_id = GetFunctionType(context, function_decl.function_id,
|
||||
SemIR::SpecificId::None);
|
||||
ReplaceInstBeforeConstantUse(context, decl_id, function_decl);
|
||||
return function_decl.function_id;
|
||||
}
|
||||
|
||||
// Imports a function declaration from Clang to Carbon. If successful, returns
|
||||
// the new Carbon function declaration `InstId`. If the declaration was already
|
||||
// imported, returns the mapped instruction.
|
||||
@@ -1334,59 +1407,29 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
}
|
||||
|
||||
context.scope_stack().PushForDeclName();
|
||||
context.inst_block_stack().Push();
|
||||
context.pattern_block_stack().Push();
|
||||
CARBON_CHECK(clang_decl->getFunctionType()->isFunctionProtoType(),
|
||||
"Not Prototype function (non-C++ code)");
|
||||
|
||||
auto function_params_insts =
|
||||
CreateFunctionParamsInsts(context, loc_id, clang_decl);
|
||||
|
||||
auto pattern_block_id = context.pattern_block_stack().Pop();
|
||||
auto decl_block_id = context.inst_block_stack().Pop();
|
||||
context.scope_stack().Pop();
|
||||
|
||||
if (!function_params_insts.has_value()) {
|
||||
auto function_id = ImportFunction(context, loc_id, clang_decl);
|
||||
if (!function_id) {
|
||||
MarkFailedDecl(context, clang_decl);
|
||||
return SemIR::ErrorInst::InstId;
|
||||
}
|
||||
|
||||
auto function_decl = SemIR::FunctionDecl{
|
||||
SemIR::TypeId::None, SemIR::FunctionId::None, decl_block_id};
|
||||
auto decl_id =
|
||||
AddPlaceholderInstInNoBlock(context, Parse::NodeId::None, function_decl);
|
||||
context.imports().push_back(decl_id);
|
||||
SemIR::Function& function_info = context.functions().Get(*function_id);
|
||||
if (IsCppThunkRequired(context, function_info)) {
|
||||
clang::FunctionDecl* thunk_clang_decl =
|
||||
BuildCppThunk(context, function_info);
|
||||
if (thunk_clang_decl) {
|
||||
SemIR::FunctionId thunk_function_id =
|
||||
*ImportFunction(context, loc_id, thunk_clang_decl);
|
||||
SemIR::InstId thunk_function_decl_id =
|
||||
context.functions().Get(thunk_function_id).first_owning_decl_id;
|
||||
function_info.SetHasCppThunk(thunk_function_decl_id);
|
||||
}
|
||||
}
|
||||
|
||||
auto function_info = SemIR::Function{
|
||||
{.name_id = AddIdentifierName(context, clang_decl->getName()),
|
||||
.parent_scope_id = GetParentNameScopeId(context, clang_decl),
|
||||
.generic_id = SemIR::GenericId::None,
|
||||
.first_param_node_id = Parse::NodeId::None,
|
||||
.last_param_node_id = Parse::NodeId::None,
|
||||
.pattern_block_id = pattern_block_id,
|
||||
.implicit_param_patterns_id =
|
||||
function_params_insts->implicit_param_patterns_id,
|
||||
.param_patterns_id = function_params_insts->param_patterns_id,
|
||||
.is_extern = false,
|
||||
.extern_library_id = SemIR::LibraryNameId::None,
|
||||
.non_owning_decl_id = SemIR::InstId::None,
|
||||
.first_owning_decl_id = decl_id,
|
||||
.definition_id = SemIR::InstId::None},
|
||||
{.call_params_id = function_params_insts->call_params_id,
|
||||
.return_slot_pattern_id = function_params_insts->return_slot_pattern_id,
|
||||
.virtual_modifier = SemIR::FunctionFields::VirtualModifier::None,
|
||||
.self_param_id = FindSelfPattern(
|
||||
context, function_params_insts->implicit_param_patterns_id),
|
||||
.clang_decl_id = context.sem_ir().clang_decls().Add(
|
||||
{.decl = clang_decl, .inst_id = decl_id})}};
|
||||
|
||||
function_decl.function_id = context.functions().Add(function_info);
|
||||
|
||||
function_decl.type_id = GetFunctionType(context, function_decl.function_id,
|
||||
SemIR::SpecificId::None);
|
||||
|
||||
ReplaceInstBeforeConstantUse(context, decl_id, function_decl);
|
||||
|
||||
return decl_id;
|
||||
return function_info.first_owning_decl_id;
|
||||
}
|
||||
|
||||
using DeclSet = llvm::SetVector<clang::Decl*>;
|
||||
|
||||
Reference in New Issue
Block a user