cpp_thunk.cpp 24 KB

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