thunk.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  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. return clang::GlobalDecl(decl);
  30. }
  31. // Returns the C++ thunk mangled name given the callee function.
  32. static auto GenerateThunkMangledName(
  33. clang::MangleContext& mangle_context,
  34. const clang::FunctionDecl& callee_function_decl, int num_params)
  35. -> std::string {
  36. RawStringOstream mangled_name_stream;
  37. mangle_context.mangleName(GetGlobalDecl(&callee_function_decl),
  38. mangled_name_stream);
  39. mangled_name_stream << ".carbon_thunk";
  40. if (num_params !=
  41. static_cast<int>(callee_function_decl.getNumNonObjectParams())) {
  42. mangled_name_stream << num_params;
  43. }
  44. return mangled_name_stream.TakeStr();
  45. }
  46. // Returns true if a C++ thunk is required for the given type. A C++ thunk is
  47. // required for any type except for void, pointer types and signed 32-bit and
  48. // 64-bit integers.
  49. static auto IsThunkRequiredForType(Context& context, SemIR::TypeId type_id)
  50. -> bool {
  51. if (!type_id.has_value() || type_id == SemIR::ErrorInst::TypeId) {
  52. return false;
  53. }
  54. type_id = context.types().GetUnqualifiedType(type_id);
  55. switch (context.types().GetAsInst(type_id).kind()) {
  56. case SemIR::PointerType::Kind: {
  57. return false;
  58. }
  59. case SemIR::ClassType::Kind: {
  60. if (!context.types().IsComplete(type_id)) {
  61. // Signed integers of 32 or 64 bits should be completed when imported.
  62. return true;
  63. }
  64. auto int_info = context.types().TryGetIntTypeInfo(type_id);
  65. if (!int_info || !int_info->bit_width.has_value()) {
  66. return true;
  67. }
  68. llvm::APInt bit_width = context.ints().Get(int_info->bit_width);
  69. return bit_width != 32 && bit_width != 64;
  70. }
  71. default:
  72. return true;
  73. }
  74. }
  75. auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
  76. -> bool {
  77. if (!function.clang_decl_id.has_value()) {
  78. return false;
  79. }
  80. // A thunk is required if any parameter or return type requires it. However,
  81. // we don't generate a thunk if any relevant type is erroneous.
  82. bool thunk_required = false;
  83. const auto& decl_info = context.clang_decls().Get(function.clang_decl_id);
  84. const auto* decl = cast<clang::FunctionDecl>(decl_info.key.decl);
  85. if (decl_info.key.num_params !=
  86. static_cast<int>(decl->getNumNonObjectParams())) {
  87. // We require a thunk if the number of parameters we want isn't all of them.
  88. // This happens if default arguments are in use, or (eventually) when
  89. // calling a varargs function.
  90. thunk_required = true;
  91. } else {
  92. // We require a thunk if any parameter is of reference type, even if the
  93. // corresponding SemIR function has an acceptable parameter type.
  94. // TODO: We should be able to avoid thunks for reference parameters.
  95. for (auto* param : decl->parameters()) {
  96. if (param->getType()->isReferenceType()) {
  97. thunk_required = true;
  98. break;
  99. }
  100. }
  101. }
  102. SemIR::TypeId return_type_id =
  103. function.GetDeclaredReturnType(context.sem_ir());
  104. if (return_type_id.has_value()) {
  105. if (return_type_id == SemIR::ErrorInst::TypeId) {
  106. return false;
  107. }
  108. thunk_required =
  109. thunk_required || IsThunkRequiredForType(context, return_type_id);
  110. }
  111. for (auto param_id :
  112. context.inst_blocks().GetOrEmpty(function.call_params_id)) {
  113. if (param_id == SemIR::ErrorInst::InstId) {
  114. return false;
  115. }
  116. thunk_required =
  117. thunk_required ||
  118. IsThunkRequiredForType(
  119. context, context.insts().GetAs<SemIR::AnyParam>(param_id).type_id);
  120. }
  121. return thunk_required;
  122. }
  123. // Returns whether the type is void, a pointer, or a signed int of 32 or 64
  124. // bits.
  125. static auto IsSimpleAbiType(clang::ASTContext& ast_context,
  126. clang::QualType type) -> bool {
  127. if (type->isVoidType() || type->isPointerType()) {
  128. return true;
  129. }
  130. if (const auto* builtin_type = type->getAs<clang::BuiltinType>()) {
  131. if (builtin_type->isSignedInteger()) {
  132. uint64_t type_size = ast_context.getIntWidth(type);
  133. return type_size == 32 || type_size == 64;
  134. }
  135. }
  136. return false;
  137. }
  138. namespace {
  139. // Information about the callee of a thunk.
  140. struct CalleeFunctionInfo {
  141. explicit CalleeFunctionInfo(clang::FunctionDecl* decl, int num_params)
  142. : decl(decl),
  143. num_params(num_params + decl->hasCXXExplicitFunctionObjectParameter()) {
  144. auto& ast_context = decl->getASTContext();
  145. const auto* method_decl = dyn_cast<clang::CXXMethodDecl>(decl);
  146. bool is_ctor = isa<clang::CXXConstructorDecl>(decl);
  147. has_object_parameter = method_decl && !method_decl->isStatic() && !is_ctor;
  148. if (has_object_parameter && method_decl->isImplicitObjectMemberFunction()) {
  149. implicit_this_type = method_decl->getThisType();
  150. }
  151. effective_return_type =
  152. is_ctor ? ast_context.getCanonicalTagType(method_decl->getParent())
  153. : decl->getReturnType();
  154. has_simple_return_type =
  155. IsSimpleAbiType(ast_context, effective_return_type);
  156. }
  157. // Returns whether this callee has an implicit `this` parameter.
  158. auto has_implicit_object_parameter() const -> bool {
  159. return !implicit_this_type.isNull();
  160. }
  161. // Returns whether this callee has an explicit `this` parameter.
  162. auto has_explicit_object_parameter() const -> bool {
  163. return has_object_parameter && !has_implicit_object_parameter();
  164. }
  165. // Returns the number of parameters the thunk should have.
  166. auto num_thunk_params() const -> unsigned {
  167. return has_implicit_object_parameter() + num_params +
  168. !has_simple_return_type;
  169. }
  170. // Returns the thunk parameter index corresponding to a given callee parameter
  171. // index.
  172. auto GetThunkParamIndex(unsigned callee_param_index) const -> unsigned {
  173. return has_implicit_object_parameter() + callee_param_index;
  174. }
  175. // Returns the thunk parameter index corresponding to the parameter that holds
  176. // the address of the return value.
  177. auto GetThunkReturnParamIndex() const -> unsigned {
  178. CARBON_CHECK(!has_simple_return_type);
  179. return has_implicit_object_parameter() + num_params;
  180. }
  181. // The callee function.
  182. clang::FunctionDecl* decl;
  183. // The number of explicit parameters to import. This may be less than the
  184. // number of parameters that the function has if default arguments are being
  185. // used.
  186. int num_params;
  187. // Whether the callee has an object parameter, which might be explicit or
  188. // implicit.
  189. bool has_object_parameter;
  190. // If the callee has an implicit object parameter, the corresponding `this`
  191. // type. Otherwise a null type.
  192. clang::QualType implicit_this_type;
  193. // The return type that the callee has when viewed from Carbon. This is the
  194. // C++ return type, except that constructors return the class type in Carbon
  195. // and return void in Clang's AST.
  196. clang::QualType effective_return_type;
  197. // Whether the callee has a simple return type, that we can return directly.
  198. // If not, we'll return through an out parameter instead.
  199. bool has_simple_return_type;
  200. };
  201. } // namespace
  202. // Given a pointer type, returns the corresponding _Nonnull-qualified pointer
  203. // type.
  204. static auto GetNonnullType(clang::ASTContext& ast_context,
  205. clang::QualType pointer_type) -> clang::QualType {
  206. return ast_context.getAttributedType(clang::NullabilityKind::NonNull,
  207. pointer_type, pointer_type);
  208. }
  209. // Given a type, returns the corresponding _Nonnull-qualified pointer type,
  210. // ignoring references.
  211. static auto GetNonNullablePointerType(clang::ASTContext& ast_context,
  212. clang::QualType type) {
  213. return GetNonnullType(ast_context,
  214. ast_context.getPointerType(type.getNonReferenceType()));
  215. }
  216. // Given the type of a callee parameter, returns the type to use for the
  217. // corresponding thunk parameter.
  218. static auto GetThunkParameterType(clang::ASTContext& ast_context,
  219. clang::QualType callee_type)
  220. -> clang::QualType {
  221. if (IsSimpleAbiType(ast_context, callee_type)) {
  222. return callee_type;
  223. }
  224. return GetNonNullablePointerType(ast_context, callee_type);
  225. }
  226. // Creates the thunk parameter types given the callee function.
  227. static auto BuildThunkParameterTypes(clang::ASTContext& ast_context,
  228. CalleeFunctionInfo callee_info)
  229. -> llvm::SmallVector<clang::QualType> {
  230. llvm::SmallVector<clang::QualType> thunk_param_types;
  231. thunk_param_types.reserve(callee_info.num_thunk_params());
  232. if (callee_info.has_implicit_object_parameter()) {
  233. thunk_param_types.push_back(
  234. GetNonnullType(ast_context, callee_info.implicit_this_type));
  235. }
  236. const auto* function_type =
  237. callee_info.decl->getType()->castAs<clang::FunctionProtoType>();
  238. for (int i : llvm::seq(callee_info.num_params)) {
  239. thunk_param_types.push_back(
  240. GetThunkParameterType(ast_context, function_type->getParamType(i)));
  241. }
  242. if (!callee_info.has_simple_return_type) {
  243. thunk_param_types.push_back(GetNonNullablePointerType(
  244. ast_context, callee_info.effective_return_type));
  245. }
  246. CARBON_CHECK(thunk_param_types.size() == callee_info.num_thunk_params());
  247. return thunk_param_types;
  248. }
  249. // Returns the thunk parameters using the callee function parameter identifiers.
  250. static auto BuildThunkParameters(clang::ASTContext& ast_context,
  251. CalleeFunctionInfo callee_info,
  252. clang::FunctionDecl* thunk_function_decl)
  253. -> llvm::SmallVector<clang::ParmVarDecl*> {
  254. clang::SourceLocation clang_loc = callee_info.decl->getLocation();
  255. const auto* thunk_function_proto_type =
  256. thunk_function_decl->getType()->castAs<clang::FunctionProtoType>();
  257. llvm::SmallVector<clang::ParmVarDecl*> thunk_params;
  258. unsigned num_thunk_params = thunk_function_decl->getNumParams();
  259. thunk_params.reserve(num_thunk_params);
  260. if (callee_info.has_implicit_object_parameter()) {
  261. clang::ParmVarDecl* thunk_param =
  262. clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc,
  263. clang_loc, &ast_context.Idents.get("this"),
  264. thunk_function_proto_type->getParamType(0),
  265. nullptr, clang::SC_None, nullptr);
  266. thunk_params.push_back(thunk_param);
  267. }
  268. for (int i : llvm::seq(callee_info.num_params)) {
  269. clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
  270. ast_context, thunk_function_decl, clang_loc, clang_loc,
  271. callee_info.decl->getParamDecl(i)->getIdentifier(),
  272. thunk_function_proto_type->getParamType(
  273. callee_info.GetThunkParamIndex(i)),
  274. nullptr, clang::SC_None, nullptr);
  275. thunk_params.push_back(thunk_param);
  276. }
  277. if (!callee_info.has_simple_return_type) {
  278. clang::ParmVarDecl* thunk_param =
  279. clang::ParmVarDecl::Create(ast_context, thunk_function_decl, clang_loc,
  280. clang_loc, &ast_context.Idents.get("return"),
  281. thunk_function_proto_type->getParamType(
  282. callee_info.GetThunkReturnParamIndex()),
  283. nullptr, clang::SC_None, nullptr);
  284. thunk_params.push_back(thunk_param);
  285. }
  286. CARBON_CHECK(thunk_params.size() == num_thunk_params);
  287. return thunk_params;
  288. }
  289. // Returns the thunk function declaration given the callee function and the
  290. // thunk parameter types.
  291. static auto CreateThunkFunctionDecl(
  292. Context& context, CalleeFunctionInfo callee_info,
  293. llvm::ArrayRef<clang::QualType> thunk_param_types) -> clang::FunctionDecl* {
  294. clang::ASTContext& ast_context = context.ast_context();
  295. clang::SourceLocation clang_loc = callee_info.decl->getLocation();
  296. clang::IdentifierInfo& identifier_info = ast_context.Idents.get(
  297. callee_info.decl->getNameAsString() + "__carbon_thunk");
  298. auto ext_proto_info = clang::FunctionProtoType::ExtProtoInfo();
  299. clang::QualType thunk_function_type = ast_context.getFunctionType(
  300. callee_info.has_simple_return_type ? callee_info.effective_return_type
  301. : ast_context.VoidTy,
  302. thunk_param_types, ext_proto_info);
  303. clang::DeclContext* decl_context = ast_context.getTranslationUnitDecl();
  304. // TODO: Thunks should not have external linkage, consider using `SC_Static`.
  305. clang::FunctionDecl* thunk_function_decl = clang::FunctionDecl::Create(
  306. ast_context, decl_context, clang_loc, clang_loc,
  307. clang::DeclarationName(&identifier_info), thunk_function_type,
  308. /*TInfo=*/nullptr, clang::SC_Extern);
  309. decl_context->addDecl(thunk_function_decl);
  310. thunk_function_decl->setParams(
  311. BuildThunkParameters(ast_context, callee_info, thunk_function_decl));
  312. // Set always_inline.
  313. thunk_function_decl->addAttr(
  314. clang::AlwaysInlineAttr::CreateImplicit(ast_context));
  315. // Set asm("<callee function mangled name>.carbon_thunk").
  316. thunk_function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
  317. ast_context,
  318. GenerateThunkMangledName(
  319. *context.sem_ir().clang_mangle_context(), *callee_info.decl,
  320. callee_info.num_params - callee_info.has_explicit_object_parameter()),
  321. clang_loc));
  322. // Set function declaration type source info.
  323. thunk_function_decl->setTypeSourceInfo(ast_context.getTrivialTypeSourceInfo(
  324. thunk_function_decl->getType(), clang_loc));
  325. return thunk_function_decl;
  326. }
  327. // Builds a reference to the given parameter thunk. If `type` is specified, that
  328. // is the callee parameter type that's being held by the parameter, and
  329. // conversions will be performed as necessary to recover a value of that type.
  330. static auto BuildThunkParamRef(clang::Sema& sema,
  331. clang::FunctionDecl* thunk_function_decl,
  332. unsigned thunk_index,
  333. clang::QualType type = clang::QualType())
  334. -> clang::Expr* {
  335. clang::ParmVarDecl* thunk_param =
  336. thunk_function_decl->getParamDecl(thunk_index);
  337. clang::SourceLocation clang_loc = thunk_param->getLocation();
  338. clang::Expr* call_arg = sema.BuildDeclRefExpr(
  339. thunk_param, thunk_param->getType().getNonReferenceType(),
  340. clang::VK_LValue, clang_loc);
  341. if (!type.isNull() && thunk_param->getType() != type) {
  342. clang::ExprResult deref_result =
  343. sema.BuildUnaryOp(nullptr, clang_loc, clang::UO_Deref, call_arg);
  344. CARBON_CHECK(deref_result.isUsable());
  345. // Cast to an rvalue when initializing an rvalue reference. The validity of
  346. // the initialization of the reference should be validated by the caller of
  347. // the thunk.
  348. //
  349. // TODO: Consider inserting a cast to an rvalue in more cases. Note that we
  350. // currently pass pointers to non-temporary objects as the argument when
  351. // calling a thunk, so we'll need to either change that or generate
  352. // different thunks depending on whether we're moving from each parameter.
  353. if (type->isRValueReferenceType()) {
  354. deref_result = clang::ImplicitCastExpr::Create(
  355. sema.getASTContext(), deref_result.get()->getType(), clang::CK_NoOp,
  356. deref_result.get(), nullptr, clang::ExprValueKind::VK_XValue,
  357. clang::FPOptionsOverride());
  358. }
  359. call_arg = deref_result.get();
  360. }
  361. return call_arg;
  362. }
  363. // Builds a reference to the parameter thunk parameter corresponding to the
  364. // given callee parameter index.
  365. static auto BuildParamRefForCalleeArg(clang::Sema& sema,
  366. clang::FunctionDecl* thunk_function_decl,
  367. CalleeFunctionInfo callee_info,
  368. unsigned callee_index) -> clang::Expr* {
  369. unsigned thunk_index = callee_info.GetThunkParamIndex(callee_index);
  370. return BuildThunkParamRef(
  371. sema, thunk_function_decl, thunk_index,
  372. callee_info.decl->getParamDecl(callee_index)->getType());
  373. }
  374. // Builds an argument list for the callee function by creating suitable uses of
  375. // the corresponding thunk parameters.
  376. static auto BuildCalleeArgs(clang::Sema& sema,
  377. clang::FunctionDecl* thunk_function_decl,
  378. CalleeFunctionInfo callee_info)
  379. -> llvm::SmallVector<clang::Expr*> {
  380. llvm::SmallVector<clang::Expr*> call_args;
  381. // The object parameter is always passed as `self`, not in the callee argument
  382. // list, so the first argument corresponds to the second parameter if there is
  383. // an explicit object parameter and the first parameter otherwise.
  384. int first_param = callee_info.has_explicit_object_parameter();
  385. call_args.reserve(callee_info.num_params - first_param);
  386. for (unsigned callee_index : llvm::seq(first_param, callee_info.num_params)) {
  387. call_args.push_back(BuildParamRefForCalleeArg(sema, thunk_function_decl,
  388. callee_info, callee_index));
  389. }
  390. return call_args;
  391. }
  392. // Builds the thunk function body which calls the callee function using the call
  393. // args and returns the callee function return value. Returns nullptr on
  394. // failure.
  395. static auto BuildThunkBody(clang::Sema& sema,
  396. clang::FunctionDecl* thunk_function_decl,
  397. CalleeFunctionInfo callee_info)
  398. -> clang::StmtResult {
  399. // TODO: Consider building a CompoundStmt holding our created statement to
  400. // make our result more closely resemble a real C++ function.
  401. clang::SourceLocation clang_loc = callee_info.decl->getLocation();
  402. // If the callee has an object parameter, build a member access expression as
  403. // the callee. Otherwise, build a regular reference to the function.
  404. clang::ExprResult callee;
  405. if (callee_info.has_object_parameter) {
  406. auto* object_param_ref =
  407. BuildThunkParamRef(sema, thunk_function_decl, 0,
  408. callee_info.has_explicit_object_parameter()
  409. ? callee_info.decl->getParamDecl(0)->getType()
  410. : clang::QualType());
  411. bool is_arrow = callee_info.has_implicit_object_parameter();
  412. auto object =
  413. sema.PerformMemberExprBaseConversion(object_param_ref, is_arrow);
  414. if (object.isInvalid()) {
  415. return clang::StmtError();
  416. }
  417. callee = sema.BuildMemberExpr(
  418. object.get(), is_arrow, clang_loc, clang::NestedNameSpecifierLoc(),
  419. clang::SourceLocation(), callee_info.decl,
  420. clang::DeclAccessPair::make(callee_info.decl, clang::AS_public),
  421. /*HadMultipleCandidates=*/false, clang::DeclarationNameInfo(),
  422. sema.getASTContext().BoundMemberTy, clang::VK_PRValue,
  423. clang::OK_Ordinary);
  424. } else if (!isa<clang::CXXConstructorDecl>(callee_info.decl)) {
  425. callee =
  426. sema.BuildDeclRefExpr(callee_info.decl, callee_info.decl->getType(),
  427. clang::VK_PRValue, clang_loc);
  428. }
  429. if (callee.isInvalid()) {
  430. return clang::StmtError();
  431. }
  432. // Build the argument list.
  433. llvm::SmallVector<clang::Expr*> call_args =
  434. BuildCalleeArgs(sema, thunk_function_decl, callee_info);
  435. clang::ExprResult call;
  436. if (auto info = clang::getConstructorInfo(callee_info.decl);
  437. info.Constructor) {
  438. // In C++, there are no direct calls to constructors, only initialization,
  439. // so we need to type-check and build the call ourselves.
  440. auto type = sema.Context.getCanonicalTagType(
  441. cast<clang::CXXRecordDecl>(callee_info.decl->getParent()));
  442. llvm::SmallVector<clang::Expr*> converted_args;
  443. converted_args.reserve(call_args.size());
  444. if (sema.CompleteConstructorCall(info.Constructor, type, call_args,
  445. clang_loc, converted_args)) {
  446. return clang::StmtError();
  447. }
  448. call = sema.BuildCXXConstructExpr(
  449. clang_loc, type, callee_info.decl, info.Constructor, converted_args,
  450. false, false, false, false, clang::CXXConstructionKind::Complete,
  451. clang_loc);
  452. } else {
  453. call = sema.BuildCallExpr(nullptr, callee.get(), clang_loc, call_args,
  454. clang_loc);
  455. }
  456. if (!call.isUsable()) {
  457. return clang::StmtError();
  458. }
  459. if (callee_info.has_simple_return_type) {
  460. return sema.BuildReturnStmt(clang_loc, call.get());
  461. }
  462. auto* return_object_addr = BuildThunkParamRef(
  463. sema, thunk_function_decl, callee_info.GetThunkReturnParamIndex());
  464. auto return_type = callee_info.effective_return_type.getNonReferenceType();
  465. auto* return_type_info =
  466. sema.Context.getTrivialTypeSourceInfo(return_type, clang_loc);
  467. auto placement_new = sema.BuildCXXNew(
  468. clang_loc, /*UseGlobal=*/true, clang_loc, {return_object_addr}, clang_loc,
  469. /*TypeIdParens=*/clang::SourceRange(), return_type, return_type_info,
  470. /*ArraySize=*/std::nullopt, clang_loc, call.get());
  471. return sema.ActOnExprStmt(placement_new, /*DiscardedValue=*/true);
  472. }
  473. auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
  474. -> clang::FunctionDecl* {
  475. clang::FunctionDecl* callee_function_decl =
  476. context.clang_decls()
  477. .Get(callee_function.clang_decl_id)
  478. .key.decl->getAsFunction();
  479. CARBON_CHECK(callee_function_decl);
  480. CalleeFunctionInfo callee_info(
  481. callee_function_decl,
  482. context.inst_blocks().Get(callee_function.param_patterns_id).size());
  483. // Build the thunk function declaration.
  484. auto thunk_param_types =
  485. BuildThunkParameterTypes(context.ast_context(), callee_info);
  486. clang::FunctionDecl* thunk_function_decl =
  487. CreateThunkFunctionDecl(context, callee_info, thunk_param_types);
  488. // Build the thunk function body.
  489. clang::Sema& sema = context.clang_sema();
  490. clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
  491. sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
  492. clang::StmtResult body =
  493. BuildThunkBody(sema, thunk_function_decl, callee_info);
  494. sema.ActOnFinishFunctionBody(thunk_function_decl, body.get());
  495. if (body.isInvalid()) {
  496. return nullptr;
  497. }
  498. context.clang_sema().getASTConsumer().HandleTopLevelDecl(
  499. clang::DeclGroupRef(thunk_function_decl));
  500. return thunk_function_decl;
  501. }
  502. auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id,
  503. SemIR::FunctionId callee_function_id,
  504. llvm::ArrayRef<SemIR::InstId> callee_arg_ids,
  505. SemIR::InstId thunk_callee_id) -> SemIR::InstId {
  506. auto& callee_function = context.functions().Get(callee_function_id);
  507. auto callee_function_params =
  508. context.inst_blocks().Get(callee_function.call_params_id);
  509. auto thunk_callee = GetCalleeAsFunction(context.sem_ir(), thunk_callee_id);
  510. auto& thunk_function = context.functions().Get(thunk_callee.function_id);
  511. auto thunk_function_params =
  512. context.inst_blocks().Get(thunk_function.call_params_id);
  513. // Whether we need to pass a return address to the thunk as a final argument.
  514. bool thunk_takes_return_address =
  515. callee_function.return_slot_pattern_id.has_value() &&
  516. !thunk_function.return_slot_pattern_id.has_value();
  517. // The number of arguments we should be acquiring in order to call the thunk.
  518. // This includes the return address parameter, if any.
  519. unsigned num_thunk_args =
  520. context.inst_blocks().Get(thunk_function.param_patterns_id).size();
  521. // The corresponding number of arguments that would be provided in a syntactic
  522. // call to the callee. This excludes the return slot.
  523. unsigned num_callee_args = num_thunk_args - thunk_takes_return_address;
  524. // Grab the return slot argument, if we were given one.
  525. auto return_slot_id = SemIR::InstId::None;
  526. if (callee_arg_ids.size() == num_callee_args + 1) {
  527. return_slot_id = callee_arg_ids.consume_back();
  528. }
  529. // If there's a return slot pattern, drop the corresponding parameter.
  530. // TODO: The parameter should probably only be created if the return pattern
  531. // actually needs a return address to be passed in.
  532. if (thunk_function.return_slot_pattern_id.has_value()) {
  533. thunk_function_params.consume_back();
  534. }
  535. if (callee_function.return_slot_pattern_id.has_value()) {
  536. callee_function_params.consume_back();
  537. }
  538. // We assume that the call parameters exactly match the parameter patterns for
  539. // both the thunk and the callee. This is currently guaranteed because we only
  540. // create trivial *ParamPatterns when importing a C++ function.
  541. CARBON_CHECK(num_callee_args == callee_function_params.size(), "{0} != {1}",
  542. num_callee_args, callee_function_params.size());
  543. CARBON_CHECK(num_callee_args == callee_arg_ids.size());
  544. CARBON_CHECK(num_thunk_args == thunk_function_params.size());
  545. // Build the thunk arguments by converting the callee arguments as needed.
  546. llvm::SmallVector<SemIR::InstId> thunk_arg_ids;
  547. thunk_arg_ids.reserve(num_thunk_args);
  548. for (auto [callee_param_inst_id, thunk_param_inst_id, callee_arg_id] :
  549. llvm::zip(callee_function_params, thunk_function_params,
  550. callee_arg_ids)) {
  551. SemIR::TypeId callee_param_type_id =
  552. context.insts().GetAs<SemIR::AnyParam>(callee_param_inst_id).type_id;
  553. SemIR::TypeId thunk_param_type_id =
  554. context.insts().GetAs<SemIR::AnyParam>(thunk_param_inst_id).type_id;
  555. SemIR::InstId arg_id = callee_arg_id;
  556. if (callee_param_type_id != thunk_param_type_id) {
  557. arg_id = Convert(context, loc_id, arg_id,
  558. {.kind = ConversionTarget::CppThunkRef,
  559. .type_id = callee_param_type_id});
  560. arg_id = AddInst<SemIR::AddrOf>(
  561. context, loc_id,
  562. {.type_id = GetPointerType(
  563. context, context.types().GetInstId(callee_param_type_id)),
  564. .lvalue_id = arg_id});
  565. arg_id =
  566. ConvertToValueOfType(context, loc_id, arg_id, thunk_param_type_id);
  567. }
  568. thunk_arg_ids.push_back(arg_id);
  569. }
  570. // Add an argument to hold the result of the call, if necessary.
  571. auto return_type_id = callee_function.GetDeclaredReturnType(context.sem_ir());
  572. if (thunk_takes_return_address) {
  573. // Create a temporary if the caller didn't provide a return slot.
  574. if (!return_slot_id.has_value()) {
  575. return_slot_id = AddInst<SemIR::TemporaryStorage>(
  576. context, loc_id, {.type_id = return_type_id});
  577. }
  578. auto arg_id = AddInst<SemIR::AddrOf>(
  579. context, loc_id,
  580. {.type_id = GetPointerType(
  581. context, context.types().GetInstId(
  582. context.insts().Get(return_slot_id).type_id())),
  583. .lvalue_id = return_slot_id});
  584. thunk_arg_ids.push_back(arg_id);
  585. }
  586. auto result_id = PerformCall(context, loc_id, thunk_callee_id, thunk_arg_ids);
  587. // Produce the result of the call, taking the value from the return storage.
  588. if (thunk_takes_return_address) {
  589. result_id = AddInst<SemIR::InPlaceInit>(context, loc_id,
  590. {.type_id = return_type_id,
  591. .src_id = result_id,
  592. .dest_id = return_slot_id});
  593. }
  594. return result_id;
  595. }
  596. } // namespace Carbon::Check