thunk.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "toolchain/check/cpp/thunk.h"
  5. #include "clang/AST/ASTConsumer.h"
  6. #include "clang/AST/GlobalDecl.h"
  7. #include "clang/AST/Mangle.h"
  8. #include "clang/Sema/Lookup.h"
  9. #include "clang/Sema/Overload.h"
  10. #include "clang/Sema/Sema.h"
  11. #include "toolchain/check/call.h"
  12. #include "toolchain/check/context.h"
  13. #include "toolchain/check/control_flow.h"
  14. #include "toolchain/check/convert.h"
  15. #include "toolchain/check/literal.h"
  16. #include "toolchain/check/type.h"
  17. #include "toolchain/check/type_completion.h"
  18. #include "toolchain/sem_ir/function.h"
  19. #include "toolchain/sem_ir/ids.h"
  20. #include "toolchain/sem_ir/typed_insts.h"
  21. namespace Carbon::Check {
  22. // Returns the GlobalDecl to use to represent the given function declaration.
  23. // TODO: Refactor with `Lower::CreateGlobalDecl`.
  24. static auto GetGlobalDecl(const clang::FunctionDecl* decl)
  25. -> clang::GlobalDecl {
  26. if (const auto* ctor = dyn_cast<clang::CXXConstructorDecl>(decl)) {
  27. return clang::GlobalDecl(ctor, clang::CXXCtorType::Ctor_Complete);
  28. }
  29. if (const auto* dtor = dyn_cast<clang::CXXDestructorDecl>(decl)) {
  30. return clang::GlobalDecl(dtor, clang::CXXDtorType::Dtor_Complete);
  31. }
  32. return clang::GlobalDecl(decl);
  33. }
  34. // Returns the C++ thunk mangled name given the callee function.
  35. static auto GenerateThunkMangledName(
  36. clang::MangleContext& mangle_context,
  37. const clang::FunctionDecl& callee_function_decl,
  38. SemIR::ClangDeclKey::Signature::Kind signature_kind, int num_params)
  39. -> std::string {
  40. RawStringOstream mangled_name_stream;
  41. mangle_context.mangleName(GetGlobalDecl(&callee_function_decl),
  42. mangled_name_stream);
  43. switch (signature_kind) {
  44. case SemIR::ClangDeclKey::Signature::Normal:
  45. mangled_name_stream << ".carbon_thunk";
  46. break;
  47. case SemIR::ClangDeclKey::Signature::TuplePattern:
  48. mangled_name_stream << ".carbon_thunk_tuple";
  49. break;
  50. }
  51. if (num_params !=
  52. static_cast<int>(callee_function_decl.getNumNonObjectParams())) {
  53. mangled_name_stream << num_params;
  54. }
  55. return mangled_name_stream.TakeStr();
  56. }
  57. // Returns whether the Carbon lowering for a parameter or return of this type is
  58. // known to match the C++ lowering.
  59. static auto IsSimpleAbiType(clang::ASTContext& ast_context,
  60. clang::QualType type, bool for_parameter) -> bool {
  61. if (type->isVoidType() || type->isPointerType()) {
  62. return true;
  63. }
  64. if (type->isReferenceType()) {
  65. if (for_parameter) {
  66. // A reference parameter has a simple ABI if it's a non-const lvalue
  67. // reference. Otherwise, we map it to pass-by-value, and it's only simple
  68. // if the type uses a pointer value representation.
  69. //
  70. // TODO: Check whether the pointee type maps to a Carbon type that uses a
  71. // pointer value representation, and treat it as simple if so.
  72. return type->isLValueReferenceType() &&
  73. !type->getPointeeType().isConstQualified();
  74. }
  75. // A reference return type is always mapped to a Carbon pointer, which uses
  76. // the same ABI rule as a C++ reference.
  77. return true;
  78. }
  79. if (const auto* enum_decl = type->getAsEnumDecl()) {
  80. // An enum type has a simple ABI if its underlying type does.
  81. type = enum_decl->getIntegerType();
  82. if (type.isNull()) {
  83. return false;
  84. }
  85. }
  86. if (const auto* builtin_type = type->getAs<clang::BuiltinType>()) {
  87. if (builtin_type->isIntegerType()) {
  88. uint64_t type_size = ast_context.getIntWidth(type);
  89. return type_size == 32 || type_size == 64;
  90. }
  91. }
  92. return false;
  93. }
  94. namespace {
  95. // Information about the callee of a thunk.
  96. struct CalleeFunctionInfo {
  97. explicit CalleeFunctionInfo(clang::FunctionDecl* decl,
  98. SemIR::ClangDeclKey::Signature signature)
  99. : decl(decl),
  100. signature_kind(signature.kind),
  101. num_params(signature.num_params +
  102. decl->hasCXXExplicitFunctionObjectParameter()) {
  103. auto& ast_context = decl->getASTContext();
  104. const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
  105. bool is_ctor = isa<clang::CXXConstructorDecl>(decl);
  106. has_object_parameter = method_decl && !method_decl->isStatic() && !is_ctor;
  107. if (has_object_parameter && method_decl->isImplicitObjectMemberFunction()) {
  108. implicit_object_parameter_type =
  109. method_decl->getFunctionObjectParameterReferenceType();
  110. }
  111. effective_return_type =
  112. is_ctor ? ast_context.getCanonicalTagType(method_decl->getParent())
  113. : decl->getReturnType();
  114. has_simple_return_type = IsSimpleAbiType(ast_context, effective_return_type,
  115. /*for_parameter=*/false);
  116. }
  117. // Returns whether this callee has an implicit `this` parameter.
  118. auto has_implicit_object_parameter() const -> bool {
  119. return !implicit_object_parameter_type.isNull();
  120. }
  121. // Returns whether this callee has an explicit `this` parameter.
  122. auto has_explicit_object_parameter() const -> bool {
  123. return has_object_parameter && !has_implicit_object_parameter();
  124. }
  125. // Returns the number of parameters the thunk should have.
  126. auto num_thunk_params() const -> unsigned {
  127. return has_implicit_object_parameter() + num_params +
  128. !has_simple_return_type;
  129. }
  130. // Returns the thunk parameter index corresponding to a given callee parameter
  131. // index.
  132. auto GetThunkParamIndex(unsigned callee_param_index) const -> unsigned {
  133. return has_implicit_object_parameter() + callee_param_index;
  134. }
  135. // Returns the thunk parameter index corresponding to the parameter that holds
  136. // the address of the return value.
  137. auto GetThunkReturnParamIndex() const -> unsigned {
  138. CARBON_CHECK(!has_simple_return_type);
  139. return has_implicit_object_parameter() + num_params;
  140. }
  141. // The callee function.
  142. clang::FunctionDecl* decl;
  143. // The kind of function signature being imported.
  144. SemIR::ClangDeclKey::Signature::Kind signature_kind;
  145. // The number of explicit parameters to import. This may be less than the
  146. // number of parameters that the function has if default arguments are being
  147. // used.
  148. int num_params;
  149. // Whether the callee has an object parameter, which might be explicit or
  150. // implicit.
  151. bool has_object_parameter;
  152. // If the callee has an implicit object parameter, the type of that parameter,
  153. // which will always be a reference type. Otherwise a null type.
  154. clang::QualType implicit_object_parameter_type;
  155. // The return type that the callee has when viewed from Carbon. This is the
  156. // C++ return type, except that constructors return the class type in Carbon
  157. // and return void in Clang's AST.
  158. clang::QualType effective_return_type;
  159. // Whether the callee has a simple return type, that we can return directly.
  160. // If not, we'll return through an out parameter instead.
  161. bool has_simple_return_type;
  162. };
  163. } // namespace
  164. auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
  165. -> bool {
  166. if (!function.clang_decl_id.has_value()) {
  167. return false;
  168. }
  169. const auto& decl_info = context.clang_decls().Get(function.clang_decl_id);
  170. auto* decl = cast<clang::FunctionDecl>(decl_info.key.decl);
  171. if (decl_info.key.signature.kind != SemIR::ClangDeclKey::Signature::Normal ||
  172. decl_info.key.signature.num_params !=
  173. static_cast<int>(decl->getNumNonObjectParams())) {
  174. // We require a thunk if the number of parameters we want isn't all of them.
  175. // This happens if default arguments are in use, or (eventually) when
  176. // calling a varargs function.
  177. return true;
  178. }
  179. CalleeFunctionInfo callee_info(decl, decl_info.key.signature);
  180. if (!callee_info.has_simple_return_type) {
  181. return true;
  182. }
  183. auto& ast_context = context.ast_context();
  184. if (callee_info.has_implicit_object_parameter() &&
  185. !IsSimpleAbiType(ast_context, callee_info.implicit_object_parameter_type,
  186. /*for_parameter=*/true)) {
  187. return true;
  188. }
  189. const auto* function_type =
  190. decl->getType()->castAs<clang::FunctionProtoType>();
  191. for (int i : llvm::seq(decl->getNumParams())) {
  192. if (!IsSimpleAbiType(ast_context, function_type->getParamType(i),
  193. /*for_parameter=*/true)) {
  194. return true;
  195. }
  196. }
  197. return false;
  198. }
  199. // Given a pointer type, returns the corresponding _Nonnull-qualified pointer
  200. // type.
  201. static auto GetNonnullType(clang::ASTContext& ast_context,
  202. clang::QualType pointer_type) -> clang::QualType {
  203. return ast_context.getAttributedType(clang::NullabilityKind::NonNull,
  204. pointer_type, pointer_type);
  205. }
  206. // Given a type, returns the corresponding _Nonnull-qualified pointer type,
  207. // ignoring references.
  208. static auto GetNonNullablePointerType(clang::ASTContext& ast_context,
  209. clang::QualType type) {
  210. return GetNonnullType(ast_context,
  211. ast_context.getPointerType(type.getNonReferenceType()));
  212. }
  213. // Given the type of a callee parameter, returns the type to use for the
  214. // corresponding thunk parameter.
  215. static auto GetThunkParameterType(clang::ASTContext& ast_context,
  216. clang::QualType callee_type)
  217. -> clang::QualType {
  218. if (IsSimpleAbiType(ast_context, callee_type, /*for_parameter=*/true)) {
  219. return callee_type;
  220. }
  221. return GetNonNullablePointerType(ast_context, callee_type);
  222. }
  223. // Creates the thunk parameter types given the callee function.
  224. static auto BuildThunkParameterTypes(clang::ASTContext& ast_context,
  225. CalleeFunctionInfo callee_info)
  226. -> llvm::SmallVector<clang::QualType> {
  227. llvm::SmallVector<clang::QualType> thunk_param_types;
  228. thunk_param_types.reserve(callee_info.num_thunk_params());
  229. if (callee_info.has_implicit_object_parameter()) {
  230. thunk_param_types.push_back(callee_info.implicit_object_parameter_type);
  231. }
  232. const auto* function_type =
  233. callee_info.decl->getType()->castAs<clang::FunctionProtoType>();
  234. for (int i : llvm::seq(callee_info.num_params)) {
  235. thunk_param_types.push_back(
  236. GetThunkParameterType(ast_context, function_type->getParamType(i)));
  237. }
  238. if (!callee_info.has_simple_return_type) {
  239. thunk_param_types.push_back(GetNonNullablePointerType(
  240. ast_context, callee_info.effective_return_type));
  241. }
  242. CARBON_CHECK(thunk_param_types.size() == callee_info.num_thunk_params());
  243. return thunk_param_types;
  244. }
  245. // Returns the thunk parameters using the callee function parameter identifiers.
  246. static auto BuildThunkParameters(clang::ASTContext& ast_context,
  247. CalleeFunctionInfo callee_info,
  248. clang::FunctionDecl* thunk_function_decl)
  249. -> llvm::SmallVector<clang::ParmVarDecl*> {
  250. clang::SourceLocation clang_loc = callee_info.decl->getLocation();
  251. const auto* thunk_function_proto_type =
  252. thunk_function_decl->getType()->castAs<clang::FunctionProtoType>();
  253. llvm::SmallVector<clang::ParmVarDecl*> thunk_params;
  254. unsigned num_thunk_params = thunk_function_decl->getNumParams();
  255. thunk_params.reserve(num_thunk_params);
  256. if (callee_info.has_implicit_object_parameter()) {
  257. clang::ParmVarDecl* thunk_param =
  258. clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc,
  259. clang_loc, &ast_context.Idents.get("this"),
  260. thunk_function_proto_type->getParamType(0),
  261. nullptr, clang::SC_None, nullptr);
  262. thunk_params.push_back(thunk_param);
  263. }
  264. for (int i : llvm::seq(callee_info.num_params)) {
  265. clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
  266. ast_context, thunk_function_decl, clang_loc, clang_loc,
  267. callee_info.decl->getParamDecl(i)->getIdentifier(),
  268. thunk_function_proto_type->getParamType(
  269. callee_info.GetThunkParamIndex(i)),
  270. nullptr, clang::SC_None, nullptr);
  271. thunk_params.push_back(thunk_param);
  272. }
  273. if (!callee_info.has_simple_return_type) {
  274. clang::ParmVarDecl* thunk_param =
  275. clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc,
  276. clang_loc, &ast_context.Idents.get("return"),
  277. thunk_function_proto_type->getParamType(
  278. callee_info.GetThunkReturnParamIndex()),
  279. nullptr, clang::SC_None, nullptr);
  280. thunk_params.push_back(thunk_param);
  281. }
  282. CARBON_CHECK(thunk_params.size() == num_thunk_params);
  283. return thunk_params;
  284. }
  285. // Computes a name to use for a thunk, based on the name of the thunk's target.
  286. // The actual name used isn't critical, since it doesn't show up much except in
  287. // AST dumps and SemIR output, but we try to produce a valid C++ identifier.
  288. static auto GetDeclNameForThunk(clang::ASTContext& ast_context,
  289. clang::DeclarationName name)
  290. -> clang::DeclarationName {
  291. llvm::SmallString<64> thunk_name;
  292. switch (name.getNameKind()) {
  293. case clang::DeclarationName::NameKind::Identifier: {
  294. thunk_name = name.getAsIdentifierInfo()->getName();
  295. break;
  296. }
  297. case clang::DeclarationName::NameKind::CXXOperatorName: {
  298. thunk_name = "operator_";
  299. switch (name.getCXXOverloadedOperator()) {
  300. case clang::OO_None:
  301. case clang::NUM_OVERLOADED_OPERATORS:
  302. break;
  303. #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \
  304. case clang::OO_##Name: \
  305. thunk_name += #Name; \
  306. break;
  307. #include "clang/Basic/OperatorKinds.def"
  308. }
  309. break;
  310. }
  311. default: {
  312. break;
  313. }
  314. }
  315. if (auto type = name.getCXXNameType(); !type.isNull()) {
  316. if (auto* class_decl = type->getAsCXXRecordDecl()) {
  317. thunk_name += class_decl->getName();
  318. }
  319. }
  320. thunk_name += "__carbon_thunk";
  321. return &ast_context.Idents.get(thunk_name);
  322. }
  323. // Returns the thunk function declaration given the callee function and the
  324. // thunk parameter types.
  325. static auto CreateThunkFunctionDecl(
  326. Context& context, CalleeFunctionInfo callee_info,
  327. llvm::ArrayRef<clang::QualType> thunk_param_types) -> clang::FunctionDecl* {
  328. clang::ASTContext& ast_context = context.ast_context();
  329. clang::SourceLocation clang_loc = callee_info.decl->getLocation();
  330. clang::DeclarationName name =
  331. GetDeclNameForThunk(ast_context, callee_info.decl->getDeclName());
  332. auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo();
  333. clang::QualType thunk_function_type = ast_context.getFunctionType(
  334. callee_info.has_simple_return_type ? callee_info.effective_return_type
  335. : ast_context.VoidTy,
  336. thunk_param_types, ext_proto_info);
  337. clang::DeclContext* decl_context = ast_context.getTranslationUnitDecl();
  338. // TODO: Thunks should not have external linkage, consider using `SC_Static`.
  339. clang::FunctionDecl* thunk_function_decl =
  340. clang::FunctionDecl::Create(ast_context, decl_context, clang_loc,
  341. clang_loc, name, thunk_function_type,
  342. /*TInfo=*/nullptr, clang::SC_Extern);
  343. decl_context->addDecl(thunk_function_decl);
  344. thunk_function_decl->setParams(
  345. BuildThunkParameters(ast_context, callee_info, thunk_function_decl));
  346. // Set always_inline.
  347. thunk_function_decl->addAttr(
  348. clang::AlwaysInlineAttr::CreateImplicit(ast_context));
  349. // Set asm("<callee function mangled name>.carbon_thunk").
  350. thunk_function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
  351. ast_context,
  352. GenerateThunkMangledName(
  353. context.cpp_context()->clang_mangle_context(), *callee_info.decl,
  354. callee_info.signature_kind,
  355. callee_info.num_params - callee_info.has_explicit_object_parameter()),
  356. clang_loc));
  357. // Set function declaration type source info.
  358. thunk_function_decl->setTypeSourceInfo(ast_context.getTrivialTypeSourceInfo(
  359. thunk_function_decl->getType(), clang_loc));
  360. return thunk_function_decl;
  361. }
  362. // Builds a reference to the given parameter thunk. If `type` is specified, that
  363. // is the callee parameter type that's being held by the parameter, and
  364. // conversions will be performed as necessary to recover a value of that type.
  365. static auto BuildThunkParamRef(clang::Sema& sema,
  366. clang::FunctionDecl* thunk_function_decl,
  367. unsigned thunk_index,
  368. clang::QualType type = clang::QualType())
  369. -> clang::Expr* {
  370. clang::ParmVarDecl* thunk_param =
  371. thunk_function_decl->getParamDecl(thunk_index);
  372. clang::SourceLocation clang_loc = thunk_param->getLocation();
  373. clang::Expr* call_arg = sema.BuildDeclRefExpr(
  374. thunk_param, thunk_param->getType().getNonReferenceType(),
  375. clang::VK_LValue, clang_loc);
  376. if (!type.isNull() && thunk_param->getType() != type) {
  377. clang::ExprResult deref_result =
  378. sema.BuildUnaryOp(nullptr, clang_loc, clang::UO_Deref, call_arg);
  379. CARBON_CHECK(deref_result.isUsable());
  380. call_arg = deref_result.get();
  381. }
  382. // Cast to an rvalue when initializing an rvalue reference. The validity of
  383. // the initialization of the reference should be validated by the caller of
  384. // the thunk.
  385. //
  386. // TODO: Consider inserting a cast to an rvalue in more cases. Note that we
  387. // currently pass pointers to non-temporary objects as the argument when
  388. // calling a thunk, so we'll need to either change that or generate
  389. // different thunks depending on whether we're moving from each parameter.
  390. if (!type.isNull() && type->isRValueReferenceType()) {
  391. call_arg = clang::ImplicitCastExpr::Create(
  392. sema.getASTContext(), call_arg->getType(), clang::CK_NoOp, call_arg,
  393. nullptr, clang::ExprValueKind::VK_XValue, clang::FPOptionsOverride());
  394. }
  395. return call_arg;
  396. }
  397. // Builds a reference to the parameter thunk parameter corresponding to the
  398. // given callee parameter index.
  399. static auto BuildParamRefForCalleeArg(clang::Sema& sema,
  400. clang::FunctionDecl* thunk_function_decl,
  401. CalleeFunctionInfo callee_info,
  402. unsigned callee_index) -> clang::Expr* {
  403. unsigned thunk_index = callee_info.GetThunkParamIndex(callee_index);
  404. return BuildThunkParamRef(
  405. sema, thunk_function_decl, thunk_index,
  406. callee_info.decl->getParamDecl(callee_index)->getType());
  407. }
  408. // Builds an argument list for the callee function by creating suitable uses of
  409. // the corresponding thunk parameters.
  410. static auto BuildCalleeArgs(clang::Sema& sema,
  411. clang::FunctionDecl* thunk_function_decl,
  412. CalleeFunctionInfo callee_info)
  413. -> llvm::SmallVector<clang::Expr*> {
  414. llvm::SmallVector<clang::Expr*> call_args;
  415. // The object parameter is always passed as `self`, not in the callee argument
  416. // list, so the first argument corresponds to the second parameter if there is
  417. // an explicit object parameter and the first parameter otherwise.
  418. int first_param = callee_info.has_explicit_object_parameter();
  419. call_args.reserve(callee_info.num_params - first_param);
  420. for (unsigned callee_index : llvm::seq(first_param, callee_info.num_params)) {
  421. call_args.push_back(BuildParamRefForCalleeArg(sema, thunk_function_decl,
  422. callee_info, callee_index));
  423. }
  424. return call_args;
  425. }
  426. // Builds the thunk function body which calls the callee function using the call
  427. // args and returns the callee function return value. Returns nullptr on
  428. // failure.
  429. static auto BuildThunkBody(clang::Sema& sema,
  430. clang::FunctionDecl* thunk_function_decl,
  431. CalleeFunctionInfo callee_info)
  432. -> clang::StmtResult {
  433. // TODO: Consider building a CompoundStmt holding our created statement to
  434. // make our result more closely resemble a real C++ function.
  435. clang::SourceLocation clang_loc = callee_info.decl->getLocation();
  436. // If the callee has an object parameter, build a member access expression as
  437. // the callee. Otherwise, build a regular reference to the function.
  438. clang::ExprResult callee;
  439. if (callee_info.has_object_parameter) {
  440. auto* object_param_ref =
  441. BuildThunkParamRef(sema, thunk_function_decl, 0,
  442. callee_info.has_explicit_object_parameter()
  443. ? callee_info.decl->getParamDecl(0)->getType()
  444. : clang::QualType());
  445. constexpr bool IsArrow = false;
  446. auto object =
  447. sema.PerformMemberExprBaseConversion(object_param_ref, IsArrow);
  448. if (object.isInvalid()) {
  449. return clang::StmtError();
  450. }
  451. callee = sema.BuildMemberExpr(
  452. object.get(), IsArrow, clang_loc, clang::NestedNameSpecifierLoc(),
  453. clang::SourceLocation(), callee_info.decl,
  454. clang::DeclAccessPair::make(callee_info.decl, clang::AS_public),
  455. /*HadMultipleCandidates=*/false, clang::DeclarationNameInfo(),
  456. sema.getASTContext().BoundMemberTy, clang::VK_PRValue,
  457. clang::OK_Ordinary);
  458. } else if (!isa<clang::CXXConstructorDecl>(callee_info.decl)) {
  459. callee =
  460. sema.BuildDeclRefExpr(callee_info.decl, callee_info.decl->getType(),
  461. clang::VK_PRValue, clang_loc);
  462. }
  463. if (callee.isInvalid()) {
  464. return clang::StmtError();
  465. }
  466. // Build the argument list.
  467. llvm::SmallVector<clang::Expr*> call_args =
  468. BuildCalleeArgs(sema, thunk_function_decl, callee_info);
  469. clang::ExprResult call;
  470. if (auto info = clang::getConstructorInfo(callee_info.decl);
  471. info.Constructor) {
  472. // In C++, there are no direct calls to constructors, only initialization,
  473. // so we need to type-check and build the call ourselves.
  474. auto type = sema.Context.getCanonicalTagType(
  475. cast<clang::CXXRecordDecl>(callee_info.decl->getParent()));
  476. llvm::SmallVector<clang::Expr*> converted_args;
  477. converted_args.reserve(call_args.size());
  478. if (sema.CompleteConstructorCall(info.Constructor, type, call_args,
  479. clang_loc, converted_args)) {
  480. return clang::StmtError();
  481. }
  482. call = sema.BuildCXXConstructExpr(
  483. clang_loc, type, callee_info.decl, info.Constructor, converted_args,
  484. false, false, false, false, clang::CXXConstructionKind::Complete,
  485. clang_loc);
  486. } else {
  487. call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc, call_args,
  488. clang_loc);
  489. }
  490. if (!call.isUsable()) {
  491. return clang::StmtError();
  492. }
  493. if (callee_info.has_simple_return_type) {
  494. return sema.BuildReturnStmt(clang_loc, call.get());
  495. }
  496. auto* return_object_addr = BuildThunkParamRef(
  497. sema, thunk_function_decl, callee_info.GetThunkReturnParamIndex());
  498. auto return_type = callee_info.effective_return_type.getNonReferenceType();
  499. auto* return_type_info =
  500. sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc);
  501. auto placement_new = sema.BuildCXXNew(
  502. clang_loc, /*UseGlobal=*/true, clang_loc, {return_object_addr}, clang_loc,
  503. /*TypeIdParens=*/clang::SourceRange(), return_type, return_type_info,
  504. /*ArraySize=*/std::nullopt, clang_loc, call.get());
  505. return sema.ActOnExprStmt(placement_new, /*DiscardedValue=*/true);
  506. }
  507. auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
  508. -> clang::FunctionDecl* {
  509. auto clang_decl_key =
  510. context.clang_decls().Get(callee_function.clang_decl_id).key;
  511. clang::FunctionDecl* callee_function_decl =
  512. clang_decl_key.decl->getAsFunction();
  513. CARBON_CHECK(callee_function_decl);
  514. // TODO: The signature kind doesn't affect the thunk that we build, so we
  515. // shouldn't consider it here. However, to do that, we would need to cache the
  516. // thunks we build so that we don't build the same thunk multiple times if
  517. // it's used with multiple different signature kinds.
  518. CalleeFunctionInfo callee_info(callee_function_decl,
  519. clang_decl_key.signature);
  520. // Build the thunk function declaration.
  521. auto thunk_param_types =
  522. BuildThunkParameterTypes(context.ast_context(), callee_info);
  523. clang::FunctionDecl* thunk_function_decl =
  524. CreateThunkFunctionDecl(context, callee_info, thunk_param_types);
  525. // Build the thunk function body.
  526. clang::Sema& sema = context.clang_sema();
  527. clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
  528. sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
  529. clang::StmtResult body =
  530. BuildThunkBody(sema, thunk_function_decl, callee_info);
  531. sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
  532. if (body.isInvalid()) {
  533. return nullptr;
  534. }
  535. context.clang_sema().getASTConsumer().HandleTopLevelDecl(
  536. clang::DeclGroupRef(thunk_function_decl));
  537. return thunk_function_decl;
  538. }
  539. auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id,
  540. SemIR::FunctionId callee_function_id,
  541. llvm::ArrayRef<SemIR::InstId> callee_arg_ids,
  542. SemIR::InstId thunk_callee_id) -> SemIR::InstId {
  543. auto& callee_function = context.functions().Get(callee_function_id);
  544. auto callee_function_params =
  545. context.inst_blocks().Get(callee_function.call_params_id);
  546. auto callee_return_patterns =
  547. context.inst_blocks().GetOrEmpty(callee_function.return_patterns_id);
  548. auto thunk_callee = GetCalleeAsFunction(context.sem_ir(), thunk_callee_id);
  549. auto& thunk_function = context.functions().Get(thunk_callee.function_id);
  550. auto thunk_function_params =
  551. context.inst_blocks().Get(thunk_function.call_params_id);
  552. auto thunk_return_patterns =
  553. context.inst_blocks().GetOrEmpty(thunk_function.return_patterns_id);
  554. CARBON_CHECK(
  555. callee_return_patterns.size() <= 1 && thunk_return_patterns.size() <= 1,
  556. "TODO: generalize this logic to support multiple return patterns.");
  557. // Whether we need to pass a return address to the thunk as a final argument.
  558. bool thunk_takes_return_address =
  559. !callee_return_patterns.empty() && thunk_return_patterns.empty();
  560. // The number of arguments we should be acquiring in order to call the thunk.
  561. // This includes the return address parameters, if any.
  562. unsigned num_thunk_args =
  563. context.inst_blocks().Get(thunk_function.param_patterns_id).size();
  564. // The corresponding number of arguments that would be provided in a syntactic
  565. // call to the callee. This excludes the return slot.
  566. unsigned num_callee_args = num_thunk_args - thunk_takes_return_address;
  567. // Grab the return slot argument, if we were given one.
  568. auto return_slot_id = SemIR::InstId::None;
  569. if (callee_arg_ids.size() == num_callee_args + 1) {
  570. return_slot_id = callee_arg_ids.consume_back();
  571. }
  572. // If there are return slot patterns, drop the corresponding parameters.
  573. // TODO: The parameter should probably only be created if the return pattern
  574. // actually needs a return address to be passed in.
  575. thunk_function_params =
  576. thunk_function_params.drop_back(thunk_return_patterns.size());
  577. callee_function_params =
  578. callee_function_params.drop_back(callee_return_patterns.size());
  579. // We assume that the call parameters exactly match the parameter patterns for
  580. // both the thunk and the callee. This is guaranteed even when we generate a
  581. // tuple pattern wrapping the function parameters.
  582. CARBON_CHECK(num_callee_args == callee_function_params.size(), "{0} != {1}",
  583. num_callee_args, callee_function_params.size());
  584. CARBON_CHECK(num_callee_args == callee_arg_ids.size());
  585. CARBON_CHECK(num_thunk_args == thunk_function_params.size());
  586. // Build the thunk arguments by converting the callee arguments as needed.
  587. llvm::SmallVector<SemIR::InstId> thunk_arg_ids;
  588. thunk_arg_ids.reserve(num_thunk_args);
  589. for (auto [callee_param_inst_id, thunk_param_inst_id, callee_arg_id] :
  590. llvm::zip(callee_function_params, thunk_function_params,
  591. callee_arg_ids)) {
  592. SemIR::TypeId callee_param_type_id =
  593. context.insts().GetAs<SemIR::AnyParam>(callee_param_inst_id).type_id;
  594. SemIR::TypeId thunk_param_type_id =
  595. context.insts().GetAs<SemIR::AnyParam>(thunk_param_inst_id).type_id;
  596. SemIR::InstId arg_id = callee_arg_id;
  597. if (callee_param_type_id != thunk_param_type_id) {
  598. arg_id = Convert(context, loc_id, arg_id,
  599. {.kind = ConversionTarget::CppThunkRef,
  600. .type_id = callee_param_type_id});
  601. arg_id = AddInst<SemIR::AddrOf>(
  602. context, loc_id,
  603. {.type_id = GetPointerType(
  604. context, context.types().GetTypeInstId(callee_param_type_id)),
  605. .lvalue_id = arg_id});
  606. arg_id =
  607. ConvertToValueOfType(context, loc_id, arg_id, thunk_param_type_id);
  608. }
  609. thunk_arg_ids.push_back(arg_id);
  610. }
  611. // Add an argument to hold the result of the call, if necessary.
  612. auto return_type_id = callee_function.GetDeclaredReturnType(context.sem_ir());
  613. if (thunk_takes_return_address) {
  614. // Create a temporary if the caller didn't provide a return slot.
  615. if (!return_slot_id.has_value()) {
  616. return_slot_id = AddInst<SemIR::TemporaryStorage>(
  617. context, loc_id, {.type_id = return_type_id});
  618. }
  619. auto arg_id = AddInst<SemIR::AddrOf>(
  620. context, loc_id,
  621. {.type_id = GetPointerType(
  622. context, context.types().GetTypeInstId(
  623. context.insts().Get(return_slot_id).type_id())),
  624. .lvalue_id = return_slot_id});
  625. thunk_arg_ids.push_back(arg_id);
  626. } else if (return_slot_id.has_value()) {
  627. thunk_arg_ids.push_back(return_slot_id);
  628. }
  629. // Compute the return type of the call to the thunk.
  630. auto thunk_return_type_id =
  631. thunk_function.GetDeclaredReturnType(context.sem_ir());
  632. if (!thunk_return_type_id.has_value()) {
  633. CARBON_CHECK(thunk_takes_return_address || !return_type_id.has_value());
  634. thunk_return_type_id = GetTupleType(context, {});
  635. } else {
  636. CARBON_CHECK(thunk_return_type_id == return_type_id);
  637. }
  638. auto result_id = GetOrAddInst<SemIR::Call>(
  639. context, loc_id,
  640. {.type_id = thunk_return_type_id,
  641. .callee_id = thunk_callee_id,
  642. .args_id = context.inst_blocks().Add(thunk_arg_ids)});
  643. // Produce the result of the call, taking the value from the return storage.
  644. if (thunk_takes_return_address) {
  645. result_id = AddInst<SemIR::MarkInPlaceInit>(context, loc_id,
  646. {.type_id = return_type_id,
  647. .src_id = result_id,
  648. .dest_id = return_slot_id});
  649. }
  650. return result_id;
  651. }
  652. } // namespace Carbon::Check