cpp_thunk.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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/Sema.h"
  8. #include "toolchain/check/call.h"
  9. #include "toolchain/check/context.h"
  10. #include "toolchain/check/control_flow.h"
  11. #include "toolchain/check/convert.h"
  12. #include "toolchain/check/literal.h"
  13. #include "toolchain/check/type.h"
  14. #include "toolchain/check/type_completion.h"
  15. #include "toolchain/sem_ir/ids.h"
  16. #include "toolchain/sem_ir/typed_insts.h"
  17. namespace Carbon::Check {
  18. // Returns the C++ thunk mangled name given the callee function.
  19. static auto GenerateThunkMangledName(
  20. clang::MangleContext& mangle_context,
  21. const clang::FunctionDecl& callee_function_decl) -> std::string {
  22. RawStringOstream mangled_name_stream;
  23. mangle_context.mangleName(clang::GlobalDecl(&callee_function_decl),
  24. mangled_name_stream);
  25. mangled_name_stream << ".carbon_thunk";
  26. return mangled_name_stream.TakeStr();
  27. }
  28. // Returns true if a C++ thunk is required for the given type. A C++ thunk is
  29. // required for any type except for void, pointer types and signed 32-bit and
  30. // 64-bit integers.
  31. static auto IsThunkRequiredForType(Context& context, SemIR::TypeId type_id)
  32. -> bool {
  33. if (!type_id.has_value() || type_id == SemIR::ErrorInst::TypeId) {
  34. return false;
  35. }
  36. type_id = context.types().GetUnqualifiedType(type_id);
  37. switch (context.types().GetAsInst(type_id).kind()) {
  38. case SemIR::PointerType::Kind: {
  39. return false;
  40. }
  41. case SemIR::ClassType::Kind: {
  42. if (!context.types().IsComplete(type_id)) {
  43. // Signed integers of 32 or 64 bits should be completed when imported.
  44. return true;
  45. }
  46. auto int_info = context.types().TryGetIntTypeInfo(type_id);
  47. if (!int_info || !int_info->bit_width.has_value()) {
  48. return true;
  49. }
  50. llvm::APInt bit_width = context.ints().Get(int_info->bit_width);
  51. return bit_width != 32 && bit_width != 64;
  52. }
  53. default:
  54. return true;
  55. }
  56. }
  57. auto IsCppThunkRequired(Context& context, const SemIR::Function& function)
  58. -> bool {
  59. if (!function.clang_decl_id.has_value()) {
  60. return false;
  61. }
  62. if (function.self_param_id.has_value()) {
  63. // TODO: Support member methods.
  64. return false;
  65. }
  66. SemIR::TypeId return_type_id =
  67. function.GetDeclaredReturnType(context.sem_ir());
  68. if (return_type_id.has_value()) {
  69. // TODO: Support non-void return values.
  70. return false;
  71. }
  72. bool thunk_required_for_param = false;
  73. for (auto param_id :
  74. context.inst_blocks().GetOrEmpty(function.call_params_id)) {
  75. if (param_id == SemIR::ErrorInst::InstId) {
  76. return false;
  77. }
  78. if (!thunk_required_for_param &&
  79. IsThunkRequiredForType(
  80. context,
  81. context.insts().GetAs<SemIR::AnyParam>(param_id).type_id)) {
  82. thunk_required_for_param = true;
  83. }
  84. }
  85. return thunk_required_for_param;
  86. }
  87. // Returns whether the type is a pointer or a signed int of 32 or 64 bits.
  88. static auto IsSimpleAbiType(clang::ASTContext& ast_context,
  89. clang::QualType type) -> bool {
  90. if (type->isPointerType()) {
  91. return true;
  92. }
  93. if (const auto* builtin_type = type->getAs<clang::BuiltinType>()) {
  94. if (builtin_type->isSignedInteger()) {
  95. uint64_t type_size = ast_context.getIntWidth(type);
  96. return type_size == 32 || type_size == 64;
  97. }
  98. }
  99. return false;
  100. }
  101. // Creates the thunk parameter types given the callee function.
  102. static auto BuildThunkParameterTypes(
  103. clang::ASTContext& ast_context,
  104. const clang::FunctionDecl& callee_function_decl)
  105. -> llvm::SmallVector<clang::QualType> {
  106. llvm::SmallVector<clang::QualType> thunk_param_types;
  107. thunk_param_types.reserve(callee_function_decl.getNumParams());
  108. for (const clang::ParmVarDecl* callee_param :
  109. callee_function_decl.parameters()) {
  110. clang::QualType param_type = callee_param->getType();
  111. bool is_simple_abi_type = IsSimpleAbiType(ast_context, param_type);
  112. if (!is_simple_abi_type) {
  113. clang::QualType pointer_type = ast_context.getPointerType(param_type);
  114. param_type = ast_context.getAttributedType(
  115. clang::NullabilityKind::NonNull, pointer_type, pointer_type);
  116. }
  117. thunk_param_types.push_back(param_type);
  118. }
  119. return thunk_param_types;
  120. }
  121. // Returns the thunk parameters using the callee function parameter identifiers.
  122. static auto BuildThunkParameters(
  123. clang::ASTContext& ast_context,
  124. const clang::FunctionDecl& callee_function_decl,
  125. clang::FunctionDecl* thunk_function_decl)
  126. -> llvm::SmallVector<clang::ParmVarDecl*> {
  127. clang::SourceLocation clang_loc = callee_function_decl.getLocation();
  128. unsigned num_params = thunk_function_decl->getNumParams();
  129. CARBON_CHECK(callee_function_decl.getNumParams() == num_params);
  130. const auto* thunk_function_proto_type =
  131. thunk_function_decl->getFunctionType()->getAs<clang::FunctionProtoType>();
  132. llvm::SmallVector<clang::ParmVarDecl*> thunk_params;
  133. thunk_params.reserve(num_params);
  134. for (unsigned i = 0; i < num_params; ++i) {
  135. clang::ParmVarDecl* thunk_param = clang::ParmVarDecl::Create(
  136. ast_context, thunk_function_decl, clang_loc, clang_loc,
  137. callee_function_decl.getParamDecl(i)->getIdentifier(),
  138. thunk_function_proto_type->getParamType(i), nullptr, clang::SC_None,
  139. nullptr);
  140. thunk_params.push_back(thunk_param);
  141. }
  142. return thunk_params;
  143. }
  144. // Returns the thunk function declaration given the callee function and the
  145. // thunk parameter types.
  146. static auto CreateThunkFunctionDecl(
  147. Context& context, const clang::FunctionDecl& callee_function_decl,
  148. llvm::ArrayRef<clang::QualType> thunk_param_types) -> clang::FunctionDecl* {
  149. clang::ASTContext& ast_context = context.ast_context();
  150. clang::SourceLocation clang_loc = callee_function_decl.getLocation();
  151. clang::IdentifierInfo& identifier_info = ast_context.Idents.get(
  152. callee_function_decl.getNameAsString() + "__carbon_thunk");
  153. const auto* callee_function_type = callee_function_decl.getFunctionType()
  154. ->castAs<clang::FunctionProtoType>();
  155. // TODO: Check whether we need to modify `ExtParameterInfo` in `ExtProtoInfo`.
  156. clang::QualType thunk_function_type = ast_context.getFunctionType(
  157. callee_function_decl.getReturnType(), thunk_param_types,
  158. callee_function_type->getExtProtoInfo());
  159. clang::DeclContext* decl_context = ast_context.getTranslationUnitDecl();
  160. // TODO: Thunks should not have external linkage, consider using `SC_Static`.
  161. clang::FunctionDecl* thunk_function_decl = clang::FunctionDecl::Create(
  162. ast_context, decl_context, clang_loc, clang_loc,
  163. clang::DeclarationName(&identifier_info), thunk_function_type,
  164. /*TInfo=*/nullptr, clang::SC_Extern);
  165. decl_context->addDecl(thunk_function_decl);
  166. thunk_function_decl->setParams(BuildThunkParameters(
  167. ast_context, callee_function_decl, thunk_function_decl));
  168. // Set always_inline.
  169. thunk_function_decl->addAttr(
  170. clang::AlwaysInlineAttr::CreateImplicit(ast_context));
  171. // Set asm("<callee function mangled name>.carbon_thunk").
  172. thunk_function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
  173. ast_context,
  174. GenerateThunkMangledName(*context.sem_ir().clang_mangle_context(),
  175. callee_function_decl),
  176. clang_loc));
  177. return thunk_function_decl;
  178. }
  179. // Takes the thunk function parameters and for each one creates an arg for the
  180. // callee function which is the thunk parameter or its address.
  181. static auto BuildCalleeArgs(clang::Sema& sema,
  182. clang::FunctionDecl* thunk_function_decl,
  183. const clang::FunctionDecl& callee_function_decl)
  184. -> llvm::SmallVector<clang::Expr*> {
  185. llvm::SmallVector<clang::Expr*> call_args;
  186. size_t num_params = thunk_function_decl->getNumParams();
  187. CARBON_CHECK(callee_function_decl.getNumParams() == num_params);
  188. call_args.reserve(num_params);
  189. for (unsigned i = 0; i < num_params; ++i) {
  190. clang::ParmVarDecl* thunk_param = thunk_function_decl->getParamDecl(i);
  191. clang::SourceLocation clang_loc = thunk_param->getLocation();
  192. clang::Expr* call_arg = sema.BuildDeclRefExpr(
  193. thunk_param, thunk_param->getType(), clang::VK_LValue, clang_loc);
  194. if (thunk_param->getType() !=
  195. callee_function_decl.getParamDecl(i)->getType()) {
  196. // TODO: Consider inserting a cast to an rvalue. Note that we currently
  197. // pass pointers to non-temporary objects as the argument when calling a
  198. // thunk, so we'll need to either change that or generate different thunks
  199. // depending on whether we're moving from each parameter.
  200. clang::ExprResult deref_result =
  201. sema.BuildUnaryOp(nullptr, clang_loc, clang::UO_Deref, call_arg);
  202. CARBON_CHECK(deref_result.isUsable());
  203. call_arg = deref_result.get();
  204. }
  205. call_args.push_back(call_arg);
  206. }
  207. return call_args;
  208. }
  209. // Builds the thunk function body which calls the callee function using the call
  210. // args and returns the callee function return value. Returns nullptr on
  211. // failure.
  212. static auto BuildThunkBody(clang::Sema& sema,
  213. clang::FunctionDecl* callee_function_decl,
  214. llvm::MutableArrayRef<clang::Expr*> call_args)
  215. -> clang::Stmt* {
  216. clang::SourceLocation clang_loc = callee_function_decl->getLocation();
  217. clang::DeclRefExpr* callee_function_ref = sema.BuildDeclRefExpr(
  218. callee_function_decl, callee_function_decl->getType(), clang::VK_PRValue,
  219. clang_loc);
  220. clang::ExprResult call_result = sema.BuildCallExpr(
  221. nullptr, callee_function_ref, clang_loc, call_args, clang_loc);
  222. if (!call_result.isUsable()) {
  223. return nullptr;
  224. }
  225. clang::Expr* call = call_result.get();
  226. clang::StmtResult return_result = sema.BuildReturnStmt(clang_loc, call);
  227. CARBON_CHECK(return_result.isUsable());
  228. return return_result.get();
  229. }
  230. auto BuildCppThunk(Context& context, const SemIR::Function& callee_function)
  231. -> clang::FunctionDecl* {
  232. clang::FunctionDecl* callee_function_decl =
  233. context.sem_ir()
  234. .clang_decls()
  235. .Get(callee_function.clang_decl_id)
  236. .decl->getAsFunction();
  237. CARBON_CHECK(callee_function_decl);
  238. // Build the thunk function declaration.
  239. auto thunk_param_types =
  240. BuildThunkParameterTypes(context.ast_context(), *callee_function_decl);
  241. clang::FunctionDecl* thunk_function_decl = CreateThunkFunctionDecl(
  242. context, *callee_function_decl, thunk_param_types);
  243. // Build the thunk function body.
  244. clang::Sema& sema = context.sem_ir().clang_ast_unit()->getSema();
  245. clang::Sema::ContextRAII context_raii(sema, thunk_function_decl);
  246. sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl);
  247. llvm::SmallVector<clang::Expr*> call_args =
  248. BuildCalleeArgs(sema, thunk_function_decl, *callee_function_decl);
  249. clang::Stmt* body = BuildThunkBody(sema, callee_function_decl, call_args);
  250. sema.ActOnFinishFunctionBody(thunk_function_decl, body);
  251. if (!body) {
  252. return nullptr;
  253. }
  254. return thunk_function_decl;
  255. }
  256. auto PerformCppThunkCall(Context& context, SemIR::LocId loc_id,
  257. SemIR::FunctionId callee_function_id,
  258. llvm::ArrayRef<SemIR::InstId> callee_arg_ids,
  259. SemIR::InstId thunk_callee_id) -> SemIR::InstId {
  260. llvm::ArrayRef<SemIR::InstId> callee_function_params =
  261. context.inst_blocks().GetOrEmpty(
  262. context.functions().Get(callee_function_id).call_params_id);
  263. llvm::ArrayRef<SemIR::InstId> thunk_function_params =
  264. context.inst_blocks().GetOrEmpty(
  265. context.functions()
  266. .Get(GetCalleeFunction(context.sem_ir(), thunk_callee_id)
  267. .function_id)
  268. .call_params_id);
  269. size_t num_params = callee_function_params.size();
  270. CARBON_CHECK(thunk_function_params.size() == num_params);
  271. CARBON_CHECK(callee_arg_ids.size() == num_params);
  272. llvm::SmallVector<SemIR::InstId> thunk_arg_ids;
  273. thunk_arg_ids.reserve(num_params);
  274. for (auto [callee_param_inst_id, thunk_param_inst_id, callee_arg_id] :
  275. llvm::zip(callee_function_params, thunk_function_params,
  276. callee_arg_ids)) {
  277. SemIR::TypeId callee_param_type_id =
  278. context.insts().GetAs<SemIR::AnyParam>(callee_param_inst_id).type_id;
  279. SemIR::TypeId thunk_param_type_id =
  280. context.insts().GetAs<SemIR::AnyParam>(thunk_param_inst_id).type_id;
  281. SemIR::InstId arg_id = callee_arg_id;
  282. if (callee_param_type_id != thunk_param_type_id) {
  283. CARBON_CHECK(thunk_param_type_id ==
  284. GetPointerType(context, context.types().GetInstId(
  285. callee_param_type_id)));
  286. arg_id = Convert(context, loc_id, arg_id,
  287. {.kind = ConversionTarget::CppThunkRef,
  288. .type_id = callee_param_type_id});
  289. arg_id = AddInst<SemIR::AddrOf>(
  290. context, loc_id,
  291. {.type_id = thunk_param_type_id, .lvalue_id = arg_id});
  292. }
  293. thunk_arg_ids.push_back(arg_id);
  294. }
  295. return PerformCall(context, loc_id, thunk_callee_id, thunk_arg_ids);
  296. }
  297. } // namespace Carbon::Check