diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 0bacf6947d41..e6cf9d6960b0 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -4,6 +4,7 @@ #include "toolchain/check/cpp/export.h" +#include "llvm/Support/Casting.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/type_mapping.h" @@ -165,6 +166,70 @@ auto ExportClassToCpp(Context& context, SemIR::LocId loc_id, return record_decl; } +namespace { +struct FunctionInfo { + explicit FunctionInfo(Context& context, SemIR::FunctionId function_id, + const SemIR::Function& function, + clang::DeclContext* decl_context) + : function_id(function_id), + function(function), + decl_context(decl_context) { + auto function_params = + context.inst_blocks().Get(function.call_param_patterns_id); + + // Get the function's `self` parameter type, if present. + if (function.call_param_ranges.implicit_size() > 0) { + CARBON_CHECK(function.call_param_ranges.implicit_size() == 1); + + auto param_inst_id = + function_params[function.call_param_ranges.implicit_begin().index]; + auto scrutinee_type_id = ExtractScrutineeType( + context.sem_ir(), context.insts().Get(param_inst_id).type_id()); + self_type_id = scrutinee_type_id; + } + + // Get the function's explicit parameter types. + function_params = + function_params.drop_front(function.call_param_ranges.implicit_size()); + function_params = + function_params.drop_back(function.call_param_ranges.return_size()); + for (auto param_inst_id : function_params) { + auto scrutinee_type_id = ExtractScrutineeType( + context.sem_ir(), context.insts().Get(param_inst_id).type_id()); + param_type_ids.push_back(scrutinee_type_id); + } + } + + // Get the `StorageClass` to use for `CXXMethodDecl`s. + auto GetStorageClass() const -> clang::StorageClass { + if (has_self()) { + return clang::SC_None; + } else { + return clang::SC_Static; + } + } + + // Whether the function has a `self` parameter. + auto has_self() const -> bool { return self_type_id != SemIR::TypeId::None; } + + SemIR::FunctionId function_id; + const SemIR::Function& function; + + // Parent scope in the C++ AST where a C++ thunk for this function can + // be created. If the function is a method, this will be a + // `CXXRecordDecl`. + clang::DeclContext* decl_context; + + // Types of the function's explicit parameters (excludes implicit + // parameters and return parameters). + llvm::SmallVector param_type_ids; + + // Type of the function's `self` parameter, or `None` if the function + // is not a method. + SemIR::TypeId self_type_id = SemIR::TypeId::None; +}; +} // namespace + // Create a `clang::FunctionDecl` for the given Carbon function. This // can be used to call the Carbon function from C++. The Carbon // function's ABI must be compatible with C++. @@ -178,15 +243,21 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context, auto clang_loc = GetCppLocation(context, loc_id); const SemIR::Function& function = context.functions().Get(function_id); + FunctionInfo callee(context, function_id, function, nullptr); // Get parameters types. - auto carbon_function_params = - context.inst_blocks().Get(function.call_param_patterns_id); llvm::SmallVector cpp_param_types; - for (auto param_inst_id : carbon_function_params) { - auto scrutinee_type_id = ExtractScrutineeType( - context.sem_ir(), context.insts().Get(param_inst_id).type_id()); - auto cpp_type = MapToCppType(context, scrutinee_type_id); + if (callee.has_self()) { + auto cpp_type = MapToCppType(context, callee.self_type_id); + if (cpp_type.isNull()) { + context.TODO(loc_id, "failed to map Carbon self type to C++"); + return nullptr; + } + cpp_type = context.ast_context().getLValueReferenceType(cpp_type); + cpp_param_types.push_back(cpp_type); + } + for (auto param_type_id : callee.param_type_ids) { + auto cpp_type = MapToCppType(context, param_type_id); if (cpp_type.isNull()) { context.TODO(loc_id, "failed to map Carbon type to C++"); return nullptr; @@ -206,7 +277,7 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context, CARBON_CHECK(identifier_info, "function with non-identifier name {0}", function.name_id); - auto* function_decl = clang::FunctionDecl::Create( + clang::FunctionDecl* function_decl = clang::FunctionDecl::Create( context.ast_context(), context.ast_context().getTranslationUnitDecl(), /*StartLoc=*/clang_loc, /*NLoc=*/clang_loc, identifier_info, cpp_function_type, /*TInfo=*/nullptr, clang::SC_Extern); @@ -233,10 +304,9 @@ static auto BuildCppFunctionDeclForCarbonFn(Context& context, // Create the declaration of the C++ thunk. static auto BuildCppToCarbonThunkDecl( - Context& context, SemIR::LocId loc_id, clang::DeclContext* decl_context, + Context& context, SemIR::LocId loc_id, const FunctionInfo& target, clang::DeclarationName thunk_name, - llvm::ArrayRef thunk_param_types, - SemIR::TypeId return_type_id) -> clang::FunctionDecl* { + llvm::ArrayRef thunk_param_types) -> clang::FunctionDecl* { clang::ASTContext& ast_context = context.ast_context(); auto clang_loc = GetCppLocation(context, loc_id); @@ -244,6 +314,7 @@ static auto BuildCppToCarbonThunkDecl( // Get the C++ return type (this corresponds to the return type of the // target Carbon function). clang::QualType cpp_return_type = context.ast_context().VoidTy; + auto return_type_id = target.function.GetDeclaredReturnType(context.sem_ir()); if (return_type_id != SemIR::TypeId::None) { cpp_return_type = MapToCppType(context, return_type_id); if (cpp_return_type.isNull()) { @@ -267,21 +338,22 @@ static auto BuildCppToCarbonThunkDecl( auto trailing_requires_clause = clang::AssociatedConstraint(); clang::FunctionDecl* thunk_function_decl = nullptr; - if (auto* parent_class = dyn_cast(decl_context)) { - // TODO: Support non-static methods. + if (auto* parent_class = + dyn_cast(target.decl_context)) { thunk_function_decl = clang::CXXMethodDecl::Create( ast_context, parent_class, clang_loc, name_info, thunk_function_type, - tinfo, clang::SC_Static, uses_fp_intrin, inline_specified, + tinfo, target.GetStorageClass(), uses_fp_intrin, inline_specified, constexpr_kind, clang_loc, trailing_requires_clause); // TODO: Map Carbon access to C++ access. thunk_function_decl->setAccess(clang::AS_public); } else { thunk_function_decl = clang::FunctionDecl::Create( - ast_context, decl_context, clang_loc, name_info, thunk_function_type, - tinfo, clang::SC_None, uses_fp_intrin, inline_specified, + ast_context, target.decl_context, clang_loc, name_info, + thunk_function_type, tinfo, clang::SC_None, uses_fp_intrin, + inline_specified, /*hasWrittenPrototype=*/true, constexpr_kind, trailing_requires_clause); } - decl_context->addDecl(thunk_function_decl); + target.decl_context->addHiddenDecl(thunk_function_decl); llvm::SmallVector param_var_decls; for (auto [i, type] : llvm::enumerate(thunk_param_types)) { @@ -305,6 +377,7 @@ static auto BuildCppToCarbonThunkDecl( // Create the body of a C++ thunk that calls a Carbon thunk. The // arguments are passed by reference to the callee. static auto BuildCppToCarbonThunkBody(clang::Sema& sema, + const FunctionInfo& target, clang::FunctionDecl* function_decl, clang::FunctionDecl* callee_function_decl) -> clang::StmtResult { @@ -342,6 +415,20 @@ static auto BuildCppToCarbonThunkBody(clang::Sema& sema, clang_loc); llvm::SmallVector call_args; + // For methods, pass the `this` pointer as the first argument to the callee. + if (target.has_self()) { + auto* parent_class = cast(target.decl_context); + clang::QualType class_type = + sema.getASTContext().getCanonicalTagType(parent_class); + auto class_ptr_type = sema.getASTContext().getPointerType(class_type); + auto* this_expr = sema.BuildCXXThisExpr(clang_loc, class_ptr_type, + /*IsImplicit=*/true); + this_expr = clang::UnaryOperator::Create( + sema.getASTContext(), this_expr, clang::UO_Deref, class_type, + clang::ExprValueKind::VK_LValue, clang::ExprObjectKind::OK_Ordinary, + clang_loc, /*CanOverflow=*/false, clang::FPOptionsOverride()); + call_args.push_back(this_expr); + } for (auto* param : function_decl->parameters()) { clang::Expr* call_arg = sema.BuildDeclRefExpr(param, param->getType().getNonReferenceType(), @@ -376,18 +463,22 @@ static auto BuildCppToCarbonThunkBody(clang::Sema& sema, // parameter types are mapped from the parameters of the target function // with `MapToCppType`. (Note that the target function here is the // callee of the Carbon thunk.) -static auto BuildCppToCarbonThunk( - Context& context, SemIR::LocId loc_id, clang::DeclContext* decl_context, - llvm::StringRef base_name, clang::FunctionDecl* carbon_function_decl, - llvm::ArrayRef callee_param_type_ids, - SemIR::TypeId return_type_id) -> clang::FunctionDecl* { +static auto BuildCppToCarbonThunk(Context& context, SemIR::LocId loc_id, + const FunctionInfo& target, + llvm::StringRef base_name, + clang::FunctionDecl* carbon_function_decl) + -> clang::FunctionDecl* { // Create the thunk's name. llvm::SmallString<64> thunk_name = base_name; - thunk_name += "__cpp_thunk"; + // TODO: changing the thunk name for methods hits this clang assertion: + // https://github.com/llvm/llvm-project/blob/058398c4ceaf/clang/lib/AST/Expr.cpp#L1720 + if (!target.has_self()) { + thunk_name += "__cpp_thunk"; + } auto& thunk_ident = context.ast_context().Idents.get(thunk_name); llvm::SmallVector param_types; - for (auto type_id : callee_param_type_ids) { + for (auto type_id : target.param_type_ids) { auto cpp_type = MapToCppType(context, type_id); if (cpp_type.isNull()) { context.TODO(loc_id, "failed to map C++ type to Carbon"); @@ -397,14 +488,14 @@ static auto BuildCppToCarbonThunk( } auto* thunk_function_decl = BuildCppToCarbonThunkDecl( - context, loc_id, decl_context, &thunk_ident, param_types, return_type_id); + context, loc_id, target, &thunk_ident, param_types); // Build the thunk function body. clang::Sema& sema = context.clang_sema(); clang::Sema::ContextRAII context_raii(sema, thunk_function_decl); sema.ActOnStartOfFunctionDef(nullptr, thunk_function_decl); - clang::StmtResult body = BuildCppToCarbonThunkBody(sema, thunk_function_decl, - carbon_function_decl); + clang::StmtResult body = BuildCppToCarbonThunkBody( + sema, target, thunk_function_decl, carbon_function_decl); sema.ActOnFinishFunctionBody(thunk_function_decl, body.get()); CARBON_CHECK(!body.isInvalid()); @@ -415,13 +506,12 @@ static auto BuildCppToCarbonThunk( // Create a Carbon thunk that calls `callee`. The thunk's parameters are // all references to the callee parameter type. -static auto BuildCarbonToCarbonThunk( - Context& context, SemIR::LocId loc_id, SemIR::FunctionId callee_function_id, - const SemIR::Function& callee, - llvm::ArrayRef callee_param_type_ids) -> SemIR::FunctionId { +static auto BuildCarbonToCarbonThunk(Context& context, SemIR::LocId loc_id, + const FunctionInfo& target) + -> SemIR::FunctionId { // Create the thunk's name. llvm::SmallString<64> thunk_name = - context.names().GetFormatted(callee.name_id); + context.names().GetFormatted(target.function.name_id); thunk_name += "__carbon_thunk"; auto& ident = context.ast_context().Idents.get(thunk_name); auto thunk_name_id = @@ -430,24 +520,27 @@ static auto BuildCarbonToCarbonThunk( // Get the thunk's parameters. These match the callee parameters, with // the addition of an output parameter for the callee's return value // (if it has one). - llvm::SmallVector thunk_param_type_ids(callee_param_type_ids); - auto callee_return_type_id = callee.GetDeclaredReturnType(context.sem_ir()); + llvm::SmallVector thunk_param_type_ids(target.param_type_ids); + auto callee_return_type_id = + target.function.GetDeclaredReturnType(context.sem_ir()); if (callee_return_type_id != SemIR::TypeId::None) { thunk_param_type_ids.push_back(callee_return_type_id); } auto carbon_thunk_function_id = - MakeGeneratedFunctionDecl(context, loc_id, - {.parent_scope_id = callee.parent_scope_id, - .name_id = thunk_name_id, - .param_type_ids = thunk_param_type_ids, - .params_are_refs = true}) + MakeGeneratedFunctionDecl( + context, loc_id, + {.parent_scope_id = target.function.parent_scope_id, + .name_id = thunk_name_id, + .self_type_id = target.self_type_id, + .param_type_ids = thunk_param_type_ids, + .params_are_refs = true}) .second; BuildThunkDefinitionForExport( - context, carbon_thunk_function_id, callee_function_id, + context, carbon_thunk_function_id, target.function_id, context.functions().Get(carbon_thunk_function_id).first_decl_id(), - callee.first_decl_id()); + target.function.first_decl_id()); return carbon_thunk_function_id; } @@ -464,13 +557,6 @@ auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id, return nullptr; } - if (callee.call_param_ranges.implicit_size() != 0) { - context.TODO(loc_id, - "unsupported: C++ calling a Carbon function with " - "an implicit parameter"); - return nullptr; - } - // Map the parent scope into the C++ AST. auto* decl_context = ExportNameScopeToCpp(context, loc_id, callee.parent_scope_id); @@ -478,24 +564,13 @@ auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id, return nullptr; } - // Get the parameter types of the Carbon function being - // called. Exclude return params, if present. - auto callee_function_params = - context.inst_blocks().Get(callee.call_param_patterns_id); - callee_function_params = - callee_function_params.drop_back(callee.call_param_ranges.return_size()); - - llvm::SmallVector callee_param_type_ids; - for (auto callee_param_inst_id : callee_function_params) { - auto scrutinee_type_id = ExtractScrutineeType( - context.sem_ir(), context.insts().Get(callee_param_inst_id).type_id()); - callee_param_type_ids.push_back(scrutinee_type_id); - } + FunctionInfo target_function_info(context, callee_function_id, callee, + decl_context); // Create a Carbon thunk that calls the callee. The thunk's parameters // are all references so that the ABI is compatible with C++ callers. - auto carbon_thunk_function_id = BuildCarbonToCarbonThunk( - context, loc_id, callee_function_id, callee, callee_param_type_ids); + auto carbon_thunk_function_id = + BuildCarbonToCarbonThunk(context, loc_id, target_function_info); // Create a `clang::FunctionDecl` that can be used to call the Carbon thunk. auto* carbon_function_decl = BuildCppFunctionDeclForCarbonFn( @@ -505,10 +580,9 @@ auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id, } // Create a C++ thunk that calls the Carbon thunk. - return BuildCppToCarbonThunk(context, loc_id, decl_context, + return BuildCppToCarbonThunk(context, loc_id, target_function_info, context.names().GetFormatted(callee.name_id), - carbon_function_decl, callee_param_type_ids, - callee.GetDeclaredReturnType(context.sem_ir())); + carbon_function_decl); } } // namespace Carbon::Check diff --git a/toolchain/check/testdata/interop/cpp/function/export/function.carbon b/toolchain/check/testdata/interop/cpp/function/export/function.carbon index 994ac1b663a1..a10bb861fc05 100644 --- a/toolchain/check/testdata/interop/cpp/function/export/function.carbon +++ b/toolchain/check/testdata/interop/cpp/function/export/function.carbon @@ -101,22 +101,13 @@ void G() { } '''; -// --- fail_todo_method.carbon +// --- method.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_todo_method.carbon:[[@LINE+5]]:1: in import [InImport] -// CHECK:STDERR: other.carbon:11:3: error: semantics TODO: `unsupported: C++ calling a Carbon function with an implicit parameter` [SemanticsTodo] -// CHECK:STDERR: fn Method[self: Self]() { self; } -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: import Other; import Cpp inline ''' void G() { - // CHECK:STDERR: fail_todo_method.carbon:[[@LINE+4]]:22: error: no member named 'Method' in 'Carbon::Other::C' [CppInteropParseError] - // CHECK:STDERR: 16 | Carbon::Other::C().Method(); - // CHECK:STDERR: | ~~~~~~~~~~~~~~~~~~ ^ - // CHECK:STDERR: Carbon::Other::C().Method(); } '''; diff --git a/toolchain/lower/testdata/interop/cpp/reverse/method.carbon b/toolchain/lower/testdata/interop/cpp/reverse/method.carbon index 3a28ca54bf96..f3df97db3509 100644 --- a/toolchain/lower/testdata/interop/cpp/reverse/method.carbon +++ b/toolchain/lower/testdata/interop/cpp/reverse/method.carbon @@ -2,7 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon // // AUTOUPDATE // TIP: To test this file alone, run: @@ -10,6 +10,41 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/reverse/method.carbon +// --- method.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class A { + fn F[self: Self]() { self; } +} + +inline Cpp ''' +void CallF() { + Carbon::A().F(); +} +'''; + +// --- method_with_args_and_return.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class A { + fn F[self: Self](a: i32) -> i32 { + self; + return a; + } +} + +inline Cpp ''' +int CallF() { + return Carbon::A().F(123); +} +'''; + +fn CallCallF() -> i32 { return Cpp.CallF(); } + // --- static.carbon library "[[@TEST_NAME]]"; @@ -27,6 +62,190 @@ void CallF() { fn CallCallF() { Cpp.CallF(); } +// CHECK:STDOUT: ; ModuleID = 'method.carbon' +// CHECK:STDOUT: source_filename = "method.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: %"class.Carbon::A" = type {} +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable +// CHECK:STDOUT: define dso_local void @_Z5CallFv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %ref.tmp = alloca %"class.Carbon::A", align 1 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %ref.tmp) #3 +// CHECK:STDOUT: call void @_ZN6Carbon1A1FEv(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp) +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %ref.tmp) #3 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable +// CHECK:STDOUT: define internal void @_ZN6Carbon1A1FEv(ptr noundef nonnull align 1 dereferenceable(1) %this) #2 align 2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !11 +// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK:STDOUT: call void @_CF__carbon_thunk.A.Main(ptr noundef nonnull align 1 dereferenceable(1) %this1) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.end.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF.A.Main(ptr %self) #3 !dbg !14 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !20 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF__carbon_thunk.A.Main(ptr %self) #3 !dbg !21 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CF.A.Main(ptr %self), !dbg !24 +// CHECK:STDOUT: ret void, !dbg !24 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #2 = { alwaysinline mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #3 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = !{i32 8, !"PIC Level", i32 2} +// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "method.carbon", directory: "") +// CHECK:STDOUT: !7 = !{!8, !8, i64 0} +// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} +// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} +// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} +// CHECK:STDOUT: !11 = !{!12, !12, i64 0} +// CHECK:STDOUT: !12 = !{!"p1 _ZTSN6Carbon1AE", !13, i64 0} +// CHECK:STDOUT: !13 = !{!"any pointer", !9, i64 0} +// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "F", linkageName: "_CF.A.Main", scope: null, file: !6, line: 6, type: !15, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !18) +// CHECK:STDOUT: !15 = !DISubroutineType(types: !16) +// CHECK:STDOUT: !16 = !{null, !17} +// CHECK:STDOUT: !17 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !18 = !{!19} +// CHECK:STDOUT: !19 = !DILocalVariable(arg: 1, scope: !14, type: !17) +// CHECK:STDOUT: !20 = !DILocation(line: 6, column: 3, scope: !14) +// CHECK:STDOUT: !21 = distinct !DISubprogram(name: "F__carbon_thunk", linkageName: "_CF__carbon_thunk.A.Main", scope: null, file: !6, line: 6, type: !15, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !22) +// CHECK:STDOUT: !22 = !{!23} +// CHECK:STDOUT: !23 = !DILocalVariable(arg: 1, scope: !21, type: !17) +// CHECK:STDOUT: !24 = !DILocation(line: 6, column: 3, scope: !21) +// CHECK:STDOUT: ; ModuleID = 'method_with_args_and_return.carbon' +// CHECK:STDOUT: source_filename = "method_with_args_and_return.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: %"class.Carbon::A" = type {} +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable +// CHECK:STDOUT: define dso_local noundef i32 @_Z5CallFv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %ref.tmp = alloca %"class.Carbon::A", align 1 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %ref.tmp) #3 +// CHECK:STDOUT: %call = call noundef i32 @_ZN6Carbon1A1FEi(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp, i32 noundef 123) +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %ref.tmp) #3 +// CHECK:STDOUT: ret i32 %call +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress nounwind uwtable +// CHECK:STDOUT: define internal noundef i32 @_ZN6Carbon1A1FEi(ptr noundef nonnull align 1 dereferenceable(1) %this, i32 noundef %0) #2 align 2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %retval = alloca i32, align 4 +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: %.addr = alloca i32, align 4 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !11 +// CHECK:STDOUT: store i32 %0, ptr %.addr, align 4, !tbaa !7 +// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK:STDOUT: call void @_CF__carbon_thunk.A.Main(ptr noundef nonnull align 1 dereferenceable(1) %this1, ptr noundef nonnull align 4 dereferenceable(4) %.addr, ptr noundef nonnull align 4 dereferenceable(4) %retval) +// CHECK:STDOUT: %1 = load i32, ptr %retval, align 4 +// CHECK:STDOUT: ret i32 %1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.end.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @_CF.A.Main(ptr %self, i32 %a) #3 !dbg !14 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 %a, !dbg !22 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CF__carbon_thunk.A.Main(ptr %self, ptr %_, ptr %_1) #3 !dbg !23 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc6_35.2 = load i32, ptr %_, align 4, !dbg !30 +// CHECK:STDOUT: %A.F.call = call i32 @_CF.A.Main(ptr %self, i32 %.loc6_35.2), !dbg !30 +// CHECK:STDOUT: store i32 %A.F.call, ptr %_1, align 4, !dbg !30 +// CHECK:STDOUT: ret void, !dbg !30 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @_CCallCallF.Main() #3 !dbg !31 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %CallF.call = call i32 @_Z5CallFv(), !dbg !34 +// CHECK:STDOUT: ret i32 %CallF.call, !dbg !35 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #2 = { alwaysinline mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #3 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// CHECK:STDOUT: !llvm.errno.tbaa = !{!7} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = !{i32 8, !"PIC Level", i32 2} +// CHECK:STDOUT: !3 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !4 = !{i32 7, !"uwtable", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "method_with_args_and_return.carbon", directory: "") +// CHECK:STDOUT: !7 = !{!8, !8, i64 0} +// CHECK:STDOUT: !8 = !{!"int", !9, i64 0} +// CHECK:STDOUT: !9 = !{!"omnipotent char", !10, i64 0} +// CHECK:STDOUT: !10 = !{!"Simple C++ TBAA"} +// CHECK:STDOUT: !11 = !{!12, !12, i64 0} +// CHECK:STDOUT: !12 = !{!"p1 _ZTSN6Carbon1AE", !13, i64 0} +// CHECK:STDOUT: !13 = !{!"any pointer", !9, i64 0} +// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "F", linkageName: "_CF.A.Main", scope: null, file: !6, line: 6, type: !15, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !19) +// CHECK:STDOUT: !15 = !DISubroutineType(types: !16) +// CHECK:STDOUT: !16 = !{!17, !18, !17} +// CHECK:STDOUT: !17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !19 = !{!20, !21} +// CHECK:STDOUT: !20 = !DILocalVariable(arg: 1, scope: !14, type: !18) +// CHECK:STDOUT: !21 = !DILocalVariable(arg: 2, scope: !14, type: !17) +// CHECK:STDOUT: !22 = !DILocation(line: 8, column: 5, scope: !14) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "F__carbon_thunk", linkageName: "_CF__carbon_thunk.A.Main", scope: null, file: !6, line: 6, type: !24, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !26) +// CHECK:STDOUT: !24 = !DISubroutineType(types: !25) +// CHECK:STDOUT: !25 = !{null, !18, !17, !17} +// CHECK:STDOUT: !26 = !{!27, !28, !29} +// CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !23, type: !18) +// CHECK:STDOUT: !28 = !DILocalVariable(arg: 2, scope: !23, type: !17) +// CHECK:STDOUT: !29 = !DILocalVariable(arg: 3, scope: !23, type: !17) +// CHECK:STDOUT: !30 = !DILocation(line: 6, column: 3, scope: !23) +// CHECK:STDOUT: !31 = distinct !DISubprogram(name: "CallCallF", linkageName: "_CCallCallF.Main", scope: null, file: !6, line: 18, type: !32, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !32 = !DISubroutineType(types: !33) +// CHECK:STDOUT: !33 = !{!17} +// CHECK:STDOUT: !34 = !DILocation(line: 18, column: 32, scope: !31) +// CHECK:STDOUT: !35 = !DILocation(line: 18, column: 25, scope: !31) // CHECK:STDOUT: ; ModuleID = 'static.carbon' // CHECK:STDOUT: source_filename = "static.carbon" // CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"