| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792 |
- // 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 "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"
- #include "clang/Sema/Overload.h"
- #include "clang/Sema/Sema.h"
- #include "toolchain/check/call.h"
- #include "toolchain/check/context.h"
- #include "toolchain/check/control_flow.h"
- #include "toolchain/check/convert.h"
- #include "toolchain/check/cpp/context.h"
- #include "toolchain/check/literal.h"
- #include "toolchain/check/type.h"
- #include "toolchain/check/type_completion.h"
- #include "toolchain/sem_ir/function.h"
- #include "toolchain/sem_ir/ids.h"
- #include "toolchain/sem_ir/typed_insts.h"
- namespace Carbon::Check {
- // Generate and return a function:
- // `void* operator new(__SIZE_TYPE__, void*) noexcept`.
- static auto GeneratePlacementNewFunctionDecl(clang::ASTContext& context)
- -> clang::FunctionDecl* {
- clang::QualType size_type = context.getSizeType();
- clang::QualType void_ptr_type = context.VoidPtrTy;
- auto ext_info = clang::FunctionProtoType::ExtProtoInfo();
- ext_info.ExceptionSpec.Type = clang::EST_BasicNoexcept;
- clang::QualType function_type = context.getFunctionType(
- void_ptr_type, {size_type, void_ptr_type}, ext_info);
- clang::DeclarationName name =
- context.DeclarationNames.getCXXOperatorName(clang::OO_New);
- clang::FunctionDecl* function_decl = clang::FunctionDecl::Create(
- context, context.getTranslationUnitDecl(), clang::SourceLocation(),
- clang::SourceLocation(), name, function_type,
- /*TInfo=*/nullptr, clang::SC_None);
- clang::ParmVarDecl* size_param = clang::ParmVarDecl::Create(
- context, function_decl, clang::SourceLocation(), clang::SourceLocation(),
- nullptr, size_type, nullptr, clang::SC_None, nullptr);
- clang::ParmVarDecl* ptr_param = clang::ParmVarDecl::Create(
- context, function_decl, clang::SourceLocation(), clang::SourceLocation(),
- nullptr, void_ptr_type, nullptr, clang::SC_None, nullptr);
- function_decl->setParams({size_param, ptr_param});
- CARBON_CHECK(function_decl->isReservedGlobalPlacementOperator());
- return function_decl;
- }
- // Returns the GlobalDecl to use to represent the given function declaration.
- // TODO: Refactor with `Lower::CreateGlobalDecl`.
- static auto GetGlobalDecl(const clang::FunctionDecl* decl)
- -> clang::GlobalDecl {
- if (const auto* ctor = dyn_cast<clang::CXXConstructorDecl>(decl)) {
- return clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete);
- }
- if (const auto* dtor = dyn_cast<clang::CXXDestructorDecl>(decl)) {
- return clang::GlobalDecl(dtor, clang::CXXDtorType::Dtor_Complete);
- }
- return clang::GlobalDecl(decl);
- }
- // Returns the C++ thunk mangled name given the callee function.
- static auto GenerateThunkMangledName(
- clang::MangleContext& mangle_context,
- const clang::FunctionDecl& callee_function_decl,
- SemIR::ClangDeclKey::Signature::Kind signature_kind, int num_params)
- -> std::string {
- RawStringOstream mangled_name_stream;
- mangle_context.mangleName(GetGlobalDecl(&callee_function_decl),
- mangled_name_stream);
- switch (signature_kind) {
- case SemIR::ClangDeclKey::Signature::Normal:
- mangled_name_stream << ".carbon_thunk";
- break;
- case SemIR::ClangDeclKey::Signature::TuplePattern:
- mangled_name_stream << ".carbon_thunk_tuple";
- break;
- }
- if (num_params !=
- static_cast<int>(callee_function_decl.getNumNonObjectParams())) {
- mangled_name_stream << num_params;
- }
- return mangled_name_stream.TakeStr();
- }
- // Returns whether the Carbon lowering for a parameter or return of this type is
- // known to match the C++ lowering.
- static auto IsSimpleAbiType(clang::ASTContext& ast_context,
- clang::QualType type, bool for_parameter) -> bool {
- if (type->isVoidType() || type->isPointerType()) {
- return true;
- }
- if (type->isReferenceType()) {
- if (for_parameter) {
- // A reference parameter has a simple ABI if it's a non-const lvalue
- // reference. Otherwise, we map it to pass-by-value, and it's only simple
- // if the type uses a pointer value representation.
- //
- // TODO: Check whether the pointee type maps to a Carbon type that uses a
- // pointer value representation, and treat it as simple if so.
- return type->isLValueReferenceType() &&
- !type->getPointeeType().isConstQualified();
- }
- // A reference return type is always mapped to a Carbon pointer, which uses
- // the same ABI rule as a C++ reference.
- return true;
- }
- if (const auto* enum_decl = type->getAsEnumDecl()) {
- // An enum type has a simple ABI if its underlying type does.
- type = enum_decl->getIntegerType();
- if (type.isNull()) {
- return false;
- }
- }
- if (const auto* builtin_type = type->getAs<clang::BuiltinType>()) {
- if (builtin_type->isIntegerType()) {
- uint64_t type_size = ast_context.getIntWidth(type);
- return type_size == 32 || type_size == 64;
- }
- }
- return false;
- }
- namespace {
- // Information about the callee of a thunk.
- struct CalleeFunctionInfo {
- explicit CalleeFunctionInfo(clang::FunctionDecl* decl,
- SemIR::ClangDeclKey::Signature signature)
- : decl(decl),
- signature_kind(signature.kind),
- num_params(signature.num_params +
- decl->hasCXXExplicitFunctionObjectParameter()) {
- auto& ast_context = decl->getASTContext();
- const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
- bool is_ctor = isa<clang::CXXConstructorDecl>(decl);
- has_object_parameter = method_decl && !method_decl->isStatic() && !is_ctor;
- if (has_object_parameter && method_decl->isImplicitObjectMemberFunction()) {
- implicit_object_parameter_type =
- method_decl->getFunctionObjectParameterReferenceType();
- }
- effective_return_type =
- is_ctor ? ast_context.getCanonicalTagType(method_decl->getParent())
- : decl->getReturnType();
- has_simple_return_type = IsSimpleAbiType(ast_context, effective_return_type,
- /*for_parameter=*/false);
- }
- // Returns whether this callee has an implicit `this` parameter.
- auto has_implicit_object_parameter() const -> bool {
- return !implicit_object_parameter_type.isNull();
- }
- // Returns whether this callee has an explicit `this` parameter.
- auto has_explicit_object_parameter() const -> bool {
- return has_object_parameter && !has_implicit_object_parameter();
- }
- // Returns the number of parameters the thunk should have.
- auto num_thunk_params() const -> unsigned {
- return has_implicit_object_parameter() + num_params +
- !has_simple_return_type;
- }
- // Returns the thunk parameter index corresponding to a given callee parameter
- // index.
- auto GetThunkParamIndex(unsigned callee_param_index) const -> unsigned {
- return has_implicit_object_parameter() + callee_param_index;
- }
- // Returns the thunk parameter index corresponding to the parameter that holds
- // the address of the return value.
- auto GetThunkReturnParamIndex() const -> unsigned {
- CARBON_CHECK(!has_simple_return_type);
- return has_implicit_object_parameter() + num_params;
- }
- // The callee function.
- clang::FunctionDecl* decl;
- // The kind of function signature being imported.
- SemIR::ClangDeclKey::Signature::Kind signature_kind;
- // The number of explicit parameters to import. This may be less than the
- // number of parameters that the function has if default arguments are being
- // used.
- int num_params;
- // Whether the callee has an object parameter, which might be explicit or
- // implicit.
- bool has_object_parameter;
- // If the callee has an implicit object parameter, the type of that parameter,
- // which will always be a reference type. Otherwise a null type.
- clang::QualType implicit_object_parameter_type;
- // The return type that the callee has when viewed from Carbon. This is the
- // C++ return type, except that constructors return the class type in Carbon
- // and return void in Clang's AST.
- clang::QualType effective_return_type;
- // Whether the callee has a simple return type, that we can return directly.
- // If not, we'll return through an out parameter instead.
- bool has_simple_return_type;
- };
- } // namespace
- auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
- -> bool {
- if (!function.clang_decl_id.has_value()) {
- return false;
- }
- const auto& decl_info = context.clang_decls().Get(function.clang_decl_id);
- auto* decl = cast<clang::FunctionDecl>(decl_info.key.decl);
- if (decl_info.key.signature.kind != SemIR::ClangDeclKey::Signature::Normal ||
- decl_info.key.signature.num_params !=
- static_cast<int>(decl->getNumNonObjectParams())) {
- // We require a thunk if the number of parameters we want isn't all of them.
- // This happens if default arguments are in use, or (eventually) when
- // calling a varargs function.
- return true;
- }
- CalleeFunctionInfo callee_info(decl, decl_info.key.signature);
- if (!callee_info.has_simple_return_type) {
- return true;
- }
- auto& ast_context = context.ast_context();
- if (callee_info.has_implicit_object_parameter() &&
- !IsSimpleAbiType(ast_context, callee_info.implicit_object_parameter_type,
- /*for_parameter=*/true)) {
- return true;
- }
- const auto* function_type =
- decl->getType()->castAs<clang::FunctionProtoType>();
- for (int i : llvm::seq(decl->getNumParams())) {
- if (!IsSimpleAbiType(ast_context, function_type->getParamType(i),
- /*for_parameter=*/true)) {
- return true;
- }
- }
- return false;
- }
- // Given a pointer type, returns the corresponding _Nonnull-qualified pointer
- // type.
- static auto GetNonnullType(clang::ASTContext& ast_context,
- clang::QualType pointer_type) -> clang::QualType {
- return ast_context.getAttributedType(clang::NullabilityKind::NonNull,
- pointer_type, pointer_type);
- }
- // Given a type, returns the corresponding _Nonnull-qualified pointer type,
- // ignoring references.
- static auto GetNonNullablePointerType(clang::ASTContext& ast_context,
- clang::QualType type) {
- return GetNonnullType(ast_context,
- ast_context.getPointerType(type.getNonReferenceType()));
- }
- // Given the type of a callee parameter, returns the type to use for the
- // corresponding thunk parameter.
- static auto GetThunkParameterType(clang::ASTContext& ast_context,
- clang::QualType callee_type)
- -> clang::QualType {
- if (IsSimpleAbiType(ast_context, callee_type, /*for_parameter=*/true)) {
- return callee_type;
- }
- return GetNonNullablePointerType(ast_context, callee_type);
- }
- // Creates the thunk parameter types given the callee function.
- static auto BuildThunkParameterTypes(clang::ASTContext& ast_context,
- CalleeFunctionInfo callee_info)
- -> llvm::SmallVector<clang::QualType> {
- llvm::SmallVector<clang::QualType> thunk_param_types;
- thunk_param_types.reserve(callee_info.num_thunk_params());
- if (callee_info.has_implicit_object_parameter()) {
- thunk_param_types.push_back(callee_info.implicit_object_parameter_type);
- }
- const auto* function_type =
- callee_info.decl->getType()->castAs<clang::FunctionProtoType>();
- for (int i : llvm::seq(callee_info.num_params)) {
- thunk_param_types.push_back(
- GetThunkParameterType(ast_context, function_type->getParamType(i)));
- }
- if (!callee_info.has_simple_return_type) {
- thunk_param_types.push_back(GetNonNullablePointerType(
- ast_context, callee_info.effective_return_type));
- }
- CARBON_CHECK(thunk_param_types.size() == callee_info.num_thunk_params());
- return thunk_param_types;
- }
- // Returns the thunk parameters using the callee function parameter identifiers.
- static auto BuildThunkParameters(clang::ASTContext& ast_context,
- CalleeFunctionInfo callee_info,
- clang::FunctionDecl* thunk_function_decl)
- -> llvm::SmallVector<clang::ParmVarDecl*> {
- clang::SourceLocation clang_loc = callee_info.decl->getLocation();
- const auto* thunk_function_proto_type =
- thunk_function_decl->getType()->castAs<clang::FunctionProtoType>();
- llvm::SmallVector<clang::ParmVarDecl*> thunk_params;
- unsigned num_thunk_params = thunk_function_decl->getNumParams();
- thunk_params.reserve(num_thunk_params);
- if (callee_info.has_implicit_object_parameter()) {
- clang::ParmVarDecl* thunk_param =
- clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc,
- clang_loc, &ast_context.Idents.get("this"),
- thunk_function_proto_type->getParamType(0),
- nullptr, clang::SC_None, nullptr);
- thunk_params.push_back(thunk_param);
- }
- for (int i : llvm::seq(callee_info.num_params)) {
- clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
- ast_context, thunk_function_decl, clang_loc, clang_loc,
- callee_info.decl->getParamDecl(i)->getIdentifier(),
- thunk_function_proto_type->getParamType(
- callee_info.GetThunkParamIndex(i)),
- nullptr, clang::SC_None, nullptr);
- thunk_params.push_back(thunk_param);
- }
- if (!callee_info.has_simple_return_type) {
- clang::ParmVarDecl* thunk_param =
- clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc,
- clang_loc, &ast_context.Idents.get("return"),
- thunk_function_proto_type->getParamType(
- callee_info.GetThunkReturnParamIndex()),
- nullptr, clang::SC_None, nullptr);
- thunk_params.push_back(thunk_param);
- }
- CARBON_CHECK(thunk_params.size() == num_thunk_params);
- return thunk_params;
- }
- // Computes a name to use for a thunk, based on the name of the thunk's target.
- // The actual name used isn't critical, since it doesn't show up much except in
- // AST dumps and SemIR output, but we try to produce a valid C++ identifier.
- static auto GetDeclNameForThunk(clang::ASTContext& ast_context,
- clang::DeclarationName name)
- -> clang::DeclarationName {
- llvm::SmallString<64> thunk_name;
- switch (name.getNameKind()) {
- case clang::DeclarationName::NameKind::Identifier: {
- thunk_name = name.getAsIdentifierInfo()->getName();
- break;
- }
- case clang::DeclarationName::NameKind::CXXOperatorName: {
- thunk_name = "operator_";
- switch (name.getCXXOverloadedOperator()) {
- case clang::OO_None:
- case clang::NUM_OVERLOADED_OPERATORS:
- break;
- #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
- case clang::OO_##Name: \
- thunk_name += #Name; \
- break;
- #include "clang/Basic/OperatorKinds.def"
- }
- break;
- }
- default: {
- break;
- }
- }
- if (auto type = name.getCXXNameType(); !type.isNull()) {
- if (auto* class_decl = type->getAsCXXRecordDecl()) {
- thunk_name += class_decl->getName();
- }
- }
- thunk_name += "__carbon_thunk";
- return &ast_context.Idents.get(thunk_name);
- }
- // Returns the thunk function declaration given the callee function and the
- // thunk parameter types.
- static auto CreateThunkFunctionDecl(
- Context& context, CalleeFunctionInfo callee_info,
- llvm::ArrayRef<clang::QualType> thunk_param_types) -> clang::FunctionDecl* {
- clang::ASTContext& ast_context = context.ast_context();
- clang::SourceLocation clang_loc = callee_info.decl->getLocation();
- clang::DeclarationName name =
- GetDeclNameForThunk(ast_context, callee_info.decl->getDeclName());
- auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo();
- clang::QualType thunk_function_type = ast_context.getFunctionType(
- callee_info.has_simple_return_type ? callee_info.effective_return_type
- : ast_context.VoidTy,
- thunk_param_types, ext_proto_info);
- clang::DeclContext* decl_context = ast_context.getTranslationUnitDecl();
- clang::FunctionDecl* thunk_function_decl = clang::FunctionDecl::Create(
- ast_context, decl_context, clang_loc, clang_loc, name,
- thunk_function_type, /*TInfo=*/nullptr, clang::SC_None,
- /*UsesFPIntrin=*/false, /*isInlineSpecified=*/true);
- decl_context->addDecl(thunk_function_decl);
- thunk_function_decl->setParams(
- BuildThunkParameters(ast_context, callee_info, thunk_function_decl));
- // Force the thunk to be inlined and discarded.
- thunk_function_decl->addAttr(
- clang::AlwaysInlineAttr::CreateImplicit(ast_context));
- thunk_function_decl->addAttr(
- clang::InternalLinkageAttr::CreateImplicit(ast_context));
- // Set asm("<callee function mangled name>.carbon_thunk").
- thunk_function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
- ast_context,
- GenerateThunkMangledName(
- context.cpp_context()->clang_mangle_context(), *callee_info.decl,
- callee_info.signature_kind,
- callee_info.num_params - callee_info.has_explicit_object_parameter()),
- clang_loc));
- // Set function declaration type source info.
- thunk_function_decl->setTypeSourceInfo(ast_context.getTrivialTypeSourceInfo(
- thunk_function_decl->getType(), clang_loc));
- return thunk_function_decl;
- }
- // Builds a reference to the given parameter thunk. If `type` is specified, that
- // is the callee parameter type that's being held by the parameter, and
- // conversions will be performed as necessary to recover a value of that type.
- static auto BuildThunkParamRef(clang::Sema& sema,
- clang::FunctionDecl* thunk_function_decl,
- unsigned thunk_index,
- clang::QualType type = clang::QualType())
- -> clang::Expr* {
- clang::ParmVarDecl* thunk_param =
- thunk_function_decl->getParamDecl(thunk_index);
- clang::SourceLocation clang_loc = thunk_param->getLocation();
- clang::Expr* call_arg = sema.BuildDeclRefExpr(
- thunk_param, thunk_param->getType().getNonReferenceType(),
- clang::VK_LValue, clang_loc);
- if (!type.isNull() && thunk_param->getType() != type) {
- clang::ExprResult deref_result =
- sema.BuildUnaryOp(nullptr, clang_loc, clang::UO_Deref, call_arg);
- CARBON_CHECK(deref_result.isUsable());
- call_arg = deref_result.get();
- }
- // Cast to an rvalue when initializing an rvalue reference. The validity of
- // the initialization of the reference should be validated by the caller of
- // the thunk.
- //
- // TODO: Consider inserting a cast to an rvalue in more cases. Note that we
- // currently pass pointers to non-temporary objects as the argument when
- // calling a thunk, so we'll need to either change that or generate
- // different thunks depending on whether we're moving from each parameter.
- if (!type.isNull() && type->isRValueReferenceType()) {
- call_arg = clang::ImplicitCastExpr::Create(
- sema.getASTContext(), call_arg->getType(), clang::CK_NoOp, call_arg,
- nullptr, clang::ExprValueKind::VK_XValue, clang::FPOptionsOverride());
- }
- return call_arg;
- }
- // Builds a reference to the parameter thunk parameter corresponding to the
- // given callee parameter index.
- static auto BuildParamRefForCalleeArg(clang::Sema& sema,
- clang::FunctionDecl* thunk_function_decl,
- CalleeFunctionInfo callee_info,
- unsigned callee_index) -> clang::Expr* {
- unsigned thunk_index = callee_info.GetThunkParamIndex(callee_index);
- return BuildThunkParamRef(
- sema, thunk_function_decl, thunk_index,
- callee_info.decl->getParamDecl(callee_index)->getType());
- }
- // Builds an argument list for the callee function by creating suitable uses of
- // the corresponding thunk parameters.
- static auto BuildCalleeArgs(clang::Sema& sema,
- clang::FunctionDecl* thunk_function_decl,
- CalleeFunctionInfo callee_info)
- -> llvm::SmallVector<clang::Expr*> {
- llvm::SmallVector<clang::Expr*> call_args;
- // The object parameter is always passed as `self`, not in the callee argument
- // list, so the first argument corresponds to the second parameter if there is
- // an explicit object parameter and the first parameter otherwise.
- int first_param = callee_info.has_explicit_object_parameter();
- call_args.reserve(callee_info.num_params - first_param);
- for (unsigned callee_index : llvm::seq(first_param, callee_info.num_params)) {
- call_args.push_back(BuildParamRefForCalleeArg(sema, thunk_function_decl,
- callee_info, callee_index));
- }
- return call_args;
- }
- // Builds the thunk function body which calls the callee function using the call
- // args and returns the callee function return value. Returns nullptr on
- // failure.
- static auto BuildThunkBody(CppContext& cpp_context, clang::Sema& sema,
- clang::FunctionDecl* thunk_function_decl,
- CalleeFunctionInfo callee_info)
- -> clang::StmtResult {
- // TODO: Consider building a CompoundStmt holding our created statement to
- // make our result more closely resemble a real C++ function.
- clang::SourceLocation clang_loc = callee_info.decl->getLocation();
- // If the callee has an object parameter, build a member access expression as
- // the callee. Otherwise, build a regular reference to the function.
- clang::ExprResult callee;
- if (callee_info.has_object_parameter) {
- clang::QualType object_param_type =
- cast<clang::CXXMethodDecl>(callee_info.decl)
- ->getFunctionObjectParameterReferenceType();
- auto* object_param_ref =
- BuildThunkParamRef(sema, thunk_function_decl, 0, object_param_type);
- constexpr bool IsArrow = false;
- auto object =
- sema.PerformMemberExprBaseConversion(object_param_ref, IsArrow);
- if (object.isInvalid()) {
- return clang::StmtError();
- }
- callee = sema.BuildMemberExpr(
- object.get(), IsArrow, clang_loc, clang::NestedNameSpecifierLoc(),
- clang::SourceLocation(), callee_info.decl,
- clang::DeclAccessPair::make(callee_info.decl, clang::AS_public),
- /*HadMultipleCandidates=*/false, clang::DeclarationNameInfo(),
- sema.getASTContext().BoundMemberTy, clang::VK_PRValue,
- clang::OK_Ordinary);
- } else if (!isa<clang::CXXConstructorDecl>(callee_info.decl)) {
- callee =
- sema.BuildDeclRefExpr(callee_info.decl, callee_info.decl->getType(),
- clang::VK_PRValue, clang_loc);
- }
- if (callee.isInvalid()) {
- return clang::StmtError();
- }
- // Build the argument list.
- llvm::SmallVector<clang::Expr*> call_args =
- BuildCalleeArgs(sema, thunk_function_decl, callee_info);
- clang::ExprResult call;
- if (auto info = clang::getConstructorInfo(callee_info.decl);
- info.Constructor) {
- // In C++, there are no direct calls to constructors, only initialization,
- // so we need to type-check and build the call ourselves.
- auto type = sema.Context.getCanonicalTagType(
- cast<clang::CXXRecordDecl>(callee_info.decl->getParent()));
- llvm::SmallVector<clang::Expr*> converted_args;
- converted_args.reserve(call_args.size());
- if (sema.CompleteConstructorCall(info.Constructor, type, call_args,
- clang_loc, converted_args)) {
- return clang::StmtError();
- }
- call = sema.BuildCXXConstructExpr(
- clang_loc, type, callee_info.decl, info.Constructor, converted_args,
- false, false, false, false, clang::CXXConstructionKind::Complete,
- clang_loc);
- } else {
- call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc, call_args,
- clang_loc);
- }
- if (!call.isUsable()) {
- return clang::StmtError();
- }
- if (callee_info.has_simple_return_type) {
- return sema.BuildReturnStmt(clang_loc, call.get());
- }
- auto* return_object_addr = BuildThunkParamRef(
- sema, thunk_function_decl, callee_info.GetThunkReturnParamIndex());
- auto return_type = callee_info.effective_return_type.getNonReferenceType();
- auto* return_type_info =
- sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc);
- auto* placement_new_decl = cpp_context.placement_new_decl();
- if (!placement_new_decl) {
- placement_new_decl = GeneratePlacementNewFunctionDecl(sema.getASTContext());
- cpp_context.set_placement_new_decl(placement_new_decl);
- }
- sema.MarkFunctionReferenced(clang_loc, placement_new_decl);
- clang::ImplicitAllocationParameters params(return_type,
- clang::TypeAwareAllocationMode::No,
- clang::AlignedAllocationMode::No);
- clang::SourceRange range(clang_loc, clang_loc);
- auto* placement_new = clang::CXXNewExpr::Create(
- sema.getASTContext(), /*IsGlobalNew*/ true, placement_new_decl,
- /*OperatorDelete*/ nullptr, params, /*UsualArrayDeleteWantsSize*/ false,
- {return_object_addr},
- /*TypeIdParens=*/clang::SourceRange(), /*ArraySize=*/std::nullopt,
- clang::CXXNewInitializationStyle::Parens, call.get(),
- sema.getASTContext().getPointerType(return_type), return_type_info, range,
- range);
- return sema.ActOnExprStmt(placement_new, /*DiscardedValue=*/true);
- }
- auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
- -> clang::FunctionDecl* {
- auto clang_decl_key =
- context.clang_decls().Get(callee_function.clang_decl_id).key;
- clang::FunctionDecl* callee_function_decl =
- clang_decl_key.decl->getAsFunction();
- CARBON_CHECK(callee_function_decl);
- // TODO: The signature kind doesn't affect the thunk that we build, so we
- // shouldn't consider it here. However, to do that, we would need to cache the
- // thunks we build so that we don't build the same thunk multiple times if
- // it's used with multiple different signature kinds.
- CalleeFunctionInfo callee_info(callee_function_decl,
- clang_decl_key.signature);
- // Build the thunk function declaration.
- auto thunk_param_types =
- BuildThunkParameterTypes(context.ast_context(), callee_info);
- clang::FunctionDecl* thunk_function_decl =
- CreateThunkFunctionDecl(context, callee_info, thunk_param_types);
- // Build the thunk function body.
- clang::Sema& sema = context.clang_sema();
- clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
- sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
- clang::StmtResult body = BuildThunkBody(*context.cpp_context(), sema,
- thunk_function_decl, callee_info);
- sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
- if (body.isInvalid()) {
- return nullptr;
- }
- context.clang_sema().getASTConsumer().HandleTopLevelDecl(
- clang::DeclGroupRef(thunk_function_decl));
- return thunk_function_decl;
- }
- auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id,
- SemIR::FunctionId callee_function_id,
- llvm::ArrayRef<SemIR::InstId> callee_arg_ids,
- SemIR::InstId thunk_callee_id) -> SemIR::InstId {
- auto& callee_function = context.functions().Get(callee_function_id);
- auto callee_function_params =
- context.inst_blocks().Get(callee_function.call_params_id);
- auto num_callee_return_params =
- callee_function.call_param_ranges.return_size();
- auto thunk_callee = GetCalleeAsFunction(context.sem_ir(), thunk_callee_id);
- auto& thunk_function = context.functions().Get(thunk_callee.function_id);
- auto thunk_function_params =
- context.inst_blocks().Get(thunk_function.call_params_id);
- auto num_thunk_return_params = thunk_function.call_param_ranges.return_size();
- CARBON_CHECK(
- num_callee_return_params <= 1 && num_thunk_return_params <= 1,
- "TODO: generalize this logic to support multiple return patterns.");
- // Whether we need to pass a return address to the thunk as a final argument.
- bool thunk_takes_return_address =
- num_callee_return_params > 0 && num_thunk_return_params == 0;
- // The number of arguments we should be acquiring in order to call the thunk.
- // This includes the return address parameters, if any.
- unsigned num_thunk_args =
- context.inst_blocks().Get(thunk_function.param_patterns_id).size();
- // The corresponding number of arguments that would be provided in a syntactic
- // call to the callee. This excludes the return slot.
- unsigned num_callee_args = num_thunk_args - thunk_takes_return_address;
- // Grab the return slot argument, if we were given one.
- auto return_slot_id = SemIR::InstId::None;
- if (callee_arg_ids.size() == num_callee_args + 1) {
- return_slot_id = callee_arg_ids.consume_back();
- }
- // If there are return slot patterns, drop the corresponding parameters.
- // TODO: The parameter should probably only be created if the return pattern
- // actually needs a return address to be passed in.
- thunk_function_params =
- thunk_function_params.drop_back(num_thunk_return_params);
- callee_function_params =
- callee_function_params.drop_back(num_callee_return_params);
- // We assume that the call parameters exactly match the parameter patterns for
- // both the thunk and the callee. This is guaranteed even when we generate a
- // tuple pattern wrapping the function parameters.
- CARBON_CHECK(num_callee_args == callee_function_params.size(), "{0} != {1}",
- num_callee_args, callee_function_params.size());
- CARBON_CHECK(num_callee_args == callee_arg_ids.size());
- CARBON_CHECK(num_thunk_args == thunk_function_params.size());
- // Build the thunk arguments by converting the callee arguments as needed.
- llvm::SmallVector<SemIR::InstId> thunk_arg_ids;
- thunk_arg_ids.reserve(num_thunk_args);
- for (auto [callee_param_inst_id, thunk_param_inst_id, callee_arg_id] :
- llvm::zip(callee_function_params, thunk_function_params,
- callee_arg_ids)) {
- SemIR::TypeId callee_param_type_id =
- context.insts().GetAs<SemIR::AnyParam>(callee_param_inst_id).type_id;
- SemIR::TypeId thunk_param_type_id =
- context.insts().GetAs<SemIR::AnyParam>(thunk_param_inst_id).type_id;
- SemIR::InstId arg_id = callee_arg_id;
- if (callee_param_type_id != thunk_param_type_id) {
- arg_id = Convert(context, loc_id, arg_id,
- {.kind = ConversionTarget::CppThunkRef,
- .type_id = callee_param_type_id});
- arg_id = AddInst<SemIR::AddrOf>(
- context, loc_id,
- {.type_id = GetPointerType(
- context, context.types().GetTypeInstId(callee_param_type_id)),
- .lvalue_id = arg_id});
- arg_id =
- ConvertToValueOfType(context, loc_id, arg_id, thunk_param_type_id);
- }
- thunk_arg_ids.push_back(arg_id);
- }
- // Add an argument to hold the result of the call, if necessary.
- auto return_type_id = callee_function.GetDeclaredReturnType(context.sem_ir());
- if (thunk_takes_return_address) {
- // Create a temporary if the caller didn't provide a return slot.
- if (!return_slot_id.has_value()) {
- return_slot_id = AddInst<SemIR::TemporaryStorage>(
- context, loc_id, {.type_id = return_type_id});
- }
- auto arg_id = AddInst<SemIR::AddrOf>(
- context, loc_id,
- {.type_id = GetPointerType(
- context, context.types().GetTypeInstId(
- context.insts().Get(return_slot_id).type_id())),
- .lvalue_id = return_slot_id});
- thunk_arg_ids.push_back(arg_id);
- } else if (return_slot_id.has_value()) {
- thunk_arg_ids.push_back(return_slot_id);
- }
- // Compute the return type of the call to the thunk.
- auto thunk_return_type_id =
- thunk_function.GetDeclaredReturnType(context.sem_ir());
- if (!thunk_return_type_id.has_value()) {
- CARBON_CHECK(thunk_takes_return_address || !return_type_id.has_value());
- thunk_return_type_id = GetTupleType(context, {});
- } else {
- CARBON_CHECK(thunk_return_type_id == return_type_id);
- }
- auto result_id = GetOrAddInst<SemIR::Call>(
- context, loc_id,
- {.type_id = thunk_return_type_id,
- .callee_id = thunk_callee_id,
- .args_id = context.inst_blocks().Add(thunk_arg_ids)});
- // Produce the result of the call, taking the value from the return storage.
- if (thunk_takes_return_address) {
- result_id = AddInst<SemIR::MarkInPlaceInit>(context, loc_id,
- {.type_id = return_type_id,
- .src_id = result_id,
- .dest_id = return_slot_id});
- }
- return result_id;
- }
- } // namespace Carbon::Check
|