diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index ae7298982999..1d56a4fa41ff 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -11,10 +11,14 @@ #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/type_mapping.h" #include "toolchain/check/function.h" +#include "toolchain/check/import_ref.h" #include "toolchain/check/pattern.h" #include "toolchain/check/thunk.h" #include "toolchain/check/type.h" +#include "toolchain/sem_ir/generic.h" #include "toolchain/sem_ir/mangler.h" +#include "toolchain/sem_ir/typed_insts.h" +#include "toolchain/sem_ir/vtable.h" namespace Carbon::Check { @@ -183,6 +187,9 @@ static auto GetStructTypeFields(Context& context, auto object_repr_type_id = class_info.GetObjectRepr(context.sem_ir(), SemIR::SpecificId::None); + if (object_repr_type_id == SemIR::ErrorInst::TypeId) { + return {}; + } auto struct_type = context.types().GetAs(object_repr_type_id); return context.struct_type_fields().Get(struct_type.fields_id); @@ -771,12 +778,45 @@ auto ExportFunctionToCpp(Context& context, SemIR::LocId loc_id, carbon_function_decl); } +// Returns whether the given class has any abstract methods. +static auto HasAnyAbstractMethods(Context& context, + const SemIR::Class& class_info, + SemIR::SpecificId class_specific_id) -> bool { + if (class_info.vtable_decl_id == SemIR::InstId::None) { + return false; + } + + LoadImportRef(context, class_info.vtable_decl_id); + auto vtable_decl_const_id = GetConstantValueInSpecific( + context.sem_ir(), class_specific_id, class_info.vtable_decl_id); + if (vtable_decl_const_id == SemIR::ErrorInst::ConstantId) { + return false; + } + auto vtable_id = context.constant_values() + .GetInstAs(vtable_decl_const_id) + .vtable_id; + const auto& vtable = context.vtables().Get(vtable_id); + for (auto virtual_fn_id : + context.inst_blocks().Get(vtable.virtual_functions_id)) { + auto virtual_fn = DecomposeVirtualFunction(context.sem_ir(), virtual_fn_id, + class_specific_id); + if (context.functions().Get(virtual_fn.function_id).virtual_modifier == + SemIR::Function::VirtualModifier::Abstract) { + return true; + } + } + return false; +} + auto ExportDestructorToCpp(Context& context, const SemIR::Class& class_info, clang::CXXRecordDecl* record_decl) -> clang::CXXDestructorDecl* { SemIR::LocId loc_id(class_info.first_decl_id()); auto clang_loc = record_decl->getLocation(); + // TODO: Add support for exporting specific classes. + const auto specific_id = SemIR::SpecificId::None; + // Create C++ destructor decl. auto class_type = context.ast_context().getCanonicalTagType(record_decl); auto name = @@ -784,7 +824,8 @@ auto ExportDestructorToCpp(Context& context, const SemIR::Class& class_info, clang::DeclarationNameInfo name_info(name, clang_loc); clang::QualType type = context.ast_context().getFunctionType( context.ast_context().VoidTy, llvm::ArrayRef(), - clang::FunctionProtoType::ExtProtoInfo()); + clang::FunctionProtoType::ExtProtoInfo().withExceptionSpec( + clang::EST_BasicNoexcept)); auto* cpp_destructor_decl = clang::CXXDestructorDecl::Create( context.ast_context(), record_decl, /*StartLoc=*/clang_loc, name_info, type, /*TInfo=*/nullptr, @@ -792,14 +833,33 @@ auto ExportDestructorToCpp(Context& context, const SemIR::Class& class_info, clang::ConstexprSpecKind::Unspecified); cpp_destructor_decl->setAccess(clang::AS_public); + clang::Sema& sema = context.clang_sema(); + + // Find and register any base class virtual destructors that this destructor + // overrides. This marks the destructor as implicitly virtual if needed. + sema.AddOverriddenMethods(record_decl, cpp_destructor_decl); + + // If the class is abstract and has no abstract methods, we need to mark the + // destructor as pure virtual. + if (class_info.inheritance_kind == SemIR::Class::InheritanceKind::Abstract && + !HasAnyAbstractMethods(context, class_info, specific_id)) { + if (cpp_destructor_decl->isVirtual()) { + cpp_destructor_decl->setIsPureVirtual(true); + } else { + context.TODO(class_info.definition_id, + "exporting abstract class with no abstract methods and " + "non-virtual destructor to C++"); + } + } + // Create Carbon thunk that destroys the object, and get a C++ // function decl for calling it. + // TODO: Once we support exporting specific classes, export the specific + // destructor here rather than a generic one. auto thunk_function_id = BuildDestroyThunk(context, loc_id, class_info); auto* cpp_function_decl = BuildCppFunctionDeclForCarbonFn(context, loc_id, thunk_function_id); - clang::Sema& sema = context.clang_sema(); - // Build the destructor body. clang::Sema::ContextRAII context_raii(sema, cpp_destructor_decl); sema.ActOnStartOfFunctionDef(nullptr, cpp_destructor_decl); diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index 19c77c3b8975..b8df6bf28b26 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -616,6 +616,15 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { class_decl->startDefinition(); CARBON_CHECK(class_decl->hasDefinition()); + // If the Carbon class is final, mark the C++ class as also being `final`. + // Abstract classes are handled when generating the destructor declaration. + if (class_info.inheritance_kind == SemIR::Class::InheritanceKind::Final) { + // TODO: Find the location of the `final` modifier and use it here. + class_decl->addAttr(clang::FinalAttr::Create( + context_->ast_context(), + GetCppLocation(*context_, SemIR::LocId(class_info.definition_id)))); + } + // If the Carbon class has a base class that we can map into C++, add that as // a C++ base class. auto base_type_id = diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index 856b41e72f5a..54faacb0c1f8 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -1801,9 +1801,16 @@ static auto ImportFunction(Context& context, SemIR::LocId loc_id, } if (virtual_modifier != SemIR::Function::VirtualModifier::None) { // TODO: Add support for Microsoft/non-Itanium vtables. + clang::GlobalDecl decl; + if (auto* dtor_decl = dyn_cast(method_decl)) { + // TODO: Do we want the index of the complete or base destructor? + decl = clang::GlobalDecl(dtor_decl, clang::Dtor_Complete); + } else { + decl = clang::GlobalDecl(method_decl); + } virtual_index = dyn_cast( context.ast_context().getVTableContext()) - ->getMethodVTableIndex(method_decl); + ->getMethodVTableIndex(decl); } } diff --git a/toolchain/check/testdata/interop/cpp/class/export/base.carbon b/toolchain/check/testdata/interop/cpp/class/export/base.carbon index 4b736a720796..2e6b4b48ed01 100644 --- a/toolchain/check/testdata/interop/cpp/class/export/base.carbon +++ b/toolchain/check/testdata/interop/cpp/class/export/base.carbon @@ -49,8 +49,113 @@ void Qualified() { } void MemberAccess(Carbon::A *a, Carbon::B *b) { - // TODO: These cause a crash. - // b->F(); - // b->G(); + b->F(); + b->G(); } '''; + +// --- fail_derive_from_final.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +class Final {} + +inline Cpp ''' +// CHECK:STDERR: fail_derive_from_final.carbon:[[@LINE+5]]:12: error: base 'Final' is marked 'final' [CppInteropParseError] +// CHECK:STDERR: 13 | struct A : Carbon::Final {}; +// CHECK:STDERR: | ^ +// CHECK:STDERR: note: 'Final' declared here [CppInteropParseNote] +// CHECK:STDERR: +struct A : Carbon::Final {}; +'''; + +// --- fail_abstract.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +// TODO: Add a way to give a class a virtual destructor without C++ interop. +inline Cpp ''' +struct VirtualDestructor { + virtual ~VirtualDestructor() {} +}; +'''; + +// CHECK:STDERR: fail_abstract.carbon:[[@LINE+4]]:1: error: cannot access member of interface `Core.Destroy` in type `Abstract` that does not implement that interface [MissingImplInMemberAccess] +// CHECK:STDERR: abstract class Abstract { +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +abstract class Abstract { + extend base: Cpp.VirtualDestructor; +} + +inline Cpp ''' +// CHECK:STDERR: fail_abstract.carbon:[[@LINE+5]]:18: error: variable type 'Carbon::Abstract' is an abstract class [CppInteropParseError] +// CHECK:STDERR: 26 | Carbon::Abstract x; +// CHECK:STDERR: | ^ +// CHECK:STDERR: note: unimplemented pure virtual method '~Abstract' in 'Abstract' [CppInteropParseNote] +// CHECK:STDERR: +Carbon::Abstract x; +'''; + +// --- fail_todo_abstract_nonvirtual_dtor.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +// TODO: Find a way to export this to C++ as an abstract class, despite not +// having a vptr. +// CHECK:STDERR: fail_todo_abstract_nonvirtual_dtor.carbon:[[@LINE+8]]:1: error: semantics TODO: `exporting abstract class with no abstract methods and non-virtual destructor to C++` [SemanticsTodo] +// CHECK:STDERR: abstract class Abstract {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_todo_abstract_nonvirtual_dtor.carbon:[[@LINE+4]]:1: error: cannot access member of interface `Core.Destroy` in type `Abstract` that does not implement that interface [MissingImplInMemberAccess] +// CHECK:STDERR: abstract class Abstract {} +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +abstract class Abstract {} + +inline Cpp ''' +class Derived : public Carbon::Abstract {}; +'''; + +// --- fail_todo_abstract_nonvirtual_dtor_but_virtual_fns.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +// TODO: We should support generating a base subobject destructor for an +// abstract class, even though we refuse to generate a complete object +// destructor. +// CHECK:STDERR: fail_todo_abstract_nonvirtual_dtor_but_virtual_fns.carbon:[[@LINE+4]]:1: error: cannot access member of interface `Core.Destroy` in type `Abstract` that does not implement that interface [MissingImplInMemberAccess] +// CHECK:STDERR: abstract class Abstract { +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +abstract class Abstract { + abstract fn F[self: Self](); +} + +inline Cpp ''' +class Derived : public Carbon::Abstract { +}; +'''; + +// --- override_virtual_dtor.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +inline Cpp ''' +struct A { + virtual ~A() = 0; +}; +'''; + +class B { + extend base: Cpp.A; +} + +inline Cpp ''' +Carbon::B b; +'''; diff --git a/toolchain/check/testdata/interop/cpp/class/export/field.carbon b/toolchain/check/testdata/interop/cpp/class/export/field.carbon index 263e167ca031..ddf6e256d5a1 100644 --- a/toolchain/check/testdata/interop/cpp/class/export/field.carbon +++ b/toolchain/check/testdata/interop/cpp/class/export/field.carbon @@ -121,3 +121,20 @@ void F() { a.x = 12; } '''; + +// --- fail_invalid.carbon +library "[[@TEST_NAME]]"; +import Cpp; + +// This should not cause a crash. +class A { + // CHECK:STDERR: fail_invalid.carbon:[[@LINE+4]]:16: error: name `Whoops` not found [NameNotFound] + // CHECK:STDERR: extend base: Whoops; + // CHECK:STDERR: ^~~~~~ + // CHECK:STDERR: + extend base: Whoops; +} + +inline Cpp ''' +Carbon::A x; +'''; diff --git a/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon b/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon index 5aa0428755dd..1a87134a6e52 100644 --- a/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon +++ b/toolchain/lower/testdata/interop/cpp/class/import/dynamic.carbon @@ -76,8 +76,6 @@ auto DoThing() -> int { // CHECK:STDOUT: // CHECK:STDOUT: $_ZN6Carbon7DerivedD2Ev = comdat any // CHECK:STDOUT: -// CHECK:STDOUT: $__clang_call_terminate = comdat any -// CHECK:STDOUT: // CHECK:STDOUT: $_ZN11FurtherBase17further_base_funcEv = comdat any // CHECK:STDOUT: // CHECK:STDOUT: $_ZN4Base4funcEv = comdat any @@ -130,33 +128,33 @@ auto DoThing() -> int { // CHECK:STDOUT: ret i32 %call // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: mustprogress uwtable -// CHECK:STDOUT: define dso_local void @_Z29WorkaroundToEmitCtorAndVtablev() #0 { +// CHECK:STDOUT: ; Function Attrs: mustprogress nounwind uwtable +// CHECK:STDOUT: define dso_local void @_Z29WorkaroundToEmitCtorAndVtablev() #1 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %d = alloca %"class.Carbon::Derived", align 8 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %d) #5 -// CHECK:STDOUT: call void @_ZN6Carbon7DerivedC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %d) #5 -// CHECK:STDOUT: call void @_ZN6Carbon7DerivedD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %d) -// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %d) #5 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %d) #4 +// CHECK:STDOUT: call void @_ZN6Carbon7DerivedC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %d) #4 +// CHECK:STDOUT: call void @_ZN6Carbon7DerivedD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %d) #4 +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %d) #4 // 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: declare void @llvm.lifetime.start.p0(ptr captures(none)) #2 // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable -// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon7DerivedC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #2 comdat align 2 { +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon7DerivedC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #3 comdat align 2 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %this.addr = alloca ptr, align 8 // CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !18 // CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 -// CHECK:STDOUT: call void @_ZN4BaseC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this1) #5 +// CHECK:STDOUT: call void @_ZN4BaseC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this1) #4 // CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTVN6Carbon7DerivedE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !16 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable -// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon7DerivedD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %this) unnamed_addr #2 comdat align 2 { +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon7DerivedD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %this) unnamed_addr #3 comdat align 2 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %this.addr = alloca ptr, align 8 // CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !18 @@ -167,7 +165,7 @@ auto DoThing() -> int { // 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: declare void @llvm.lifetime.end.p0(ptr captures(none)) #2 // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: mustprogress uwtable // CHECK:STDOUT: define dso_local noundef i32 @_Z7DoThingv() #0 personality ptr @__gxx_personality_v0 { @@ -175,14 +173,14 @@ auto DoThing() -> int { // CHECK:STDOUT: %d = alloca %"class.Carbon::Derived", align 8 // CHECK:STDOUT: %exn.slot = alloca ptr, align 8 // CHECK:STDOUT: %ehselector.slot = alloca i32, align 4 -// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %d) #5 -// CHECK:STDOUT: call void @_ZN6Carbon7DerivedC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %d) #5 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %d) #4 +// CHECK:STDOUT: call void @_ZN6Carbon7DerivedC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %d) #4 // CHECK:STDOUT: %call = invoke noundef i32 @_Z3UseR4Base(ptr noundef nonnull align 8 dereferenceable(12) %d) // CHECK:STDOUT: to label %invoke.cont unwind label %lpad // CHECK:STDOUT: // CHECK:STDOUT: invoke.cont: ; preds = %entry -// CHECK:STDOUT: call void @_ZN6Carbon7DerivedD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %d) -// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %d) #5 +// CHECK:STDOUT: call void @_ZN6Carbon7DerivedD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %d) #4 +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %d) #4 // CHECK:STDOUT: ret i32 %call // CHECK:STDOUT: // CHECK:STDOUT: lpad: ; preds = %entry @@ -192,26 +190,16 @@ auto DoThing() -> int { // CHECK:STDOUT: store ptr %1, ptr %exn.slot, align 8 // CHECK:STDOUT: %2 = extractvalue { ptr, i32 } %0, 1 // CHECK:STDOUT: store i32 %2, ptr %ehselector.slot, align 4 -// CHECK:STDOUT: invoke void @_ZN6Carbon7DerivedD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %d) -// CHECK:STDOUT: to label %invoke.cont1 unwind label %terminate.lpad -// CHECK:STDOUT: -// CHECK:STDOUT: invoke.cont1: ; preds = %lpad -// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %d) #5 +// CHECK:STDOUT: call void @_ZN6Carbon7DerivedD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %d) #4 +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %d) #4 // CHECK:STDOUT: br label %eh.resume // CHECK:STDOUT: -// CHECK:STDOUT: eh.resume: ; preds = %invoke.cont1 +// CHECK:STDOUT: eh.resume: ; preds = %lpad // CHECK:STDOUT: %exn = load ptr, ptr %exn.slot, align 8 // CHECK:STDOUT: %sel = load i32, ptr %ehselector.slot, align 4 // CHECK:STDOUT: %lpad.val = insertvalue { ptr, i32 } poison, ptr %exn, 0 -// CHECK:STDOUT: %lpad.val2 = insertvalue { ptr, i32 } %lpad.val, i32 %sel, 1 -// CHECK:STDOUT: resume { ptr, i32 } %lpad.val2 -// CHECK:STDOUT: -// CHECK:STDOUT: terminate.lpad: ; preds = %lpad -// CHECK:STDOUT: %3 = landingpad { ptr, i32 } -// CHECK:STDOUT: catch ptr null -// CHECK:STDOUT: %4 = extractvalue { ptr, i32 } %3, 0 -// CHECK:STDOUT: call void @__clang_call_terminate(ptr %4) #7 -// CHECK:STDOUT: unreachable +// CHECK:STDOUT: %lpad.val1 = insertvalue { ptr, i32 } %lpad.val, i32 %sel, 1 +// CHECK:STDOUT: resume { ptr, i32 } %lpad.val1 // CHECK:STDOUT: // CHECK:STDOUT: ; uselistorder directives // CHECK:STDOUT: uselistorder ptr %d, { 0, 2, 1, 3, 4, 5, 6 } @@ -219,19 +207,8 @@ auto DoThing() -> int { // CHECK:STDOUT: // CHECK:STDOUT: declare i32 @__gxx_personality_v0(...) // CHECK:STDOUT: -// CHECK:STDOUT: ; Function Attrs: noinline noreturn nounwind uwtable -// CHECK:STDOUT: define linkonce_odr hidden void @__clang_call_terminate(ptr noundef %0) #3 comdat { -// CHECK:STDOUT: %2 = call ptr @__cxa_begin_catch(ptr %0) #5 -// CHECK:STDOUT: call void @_ZSt9terminatev() #7 -// CHECK:STDOUT: unreachable -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: declare ptr @__cxa_begin_catch(ptr) -// CHECK:STDOUT: -// CHECK:STDOUT: declare void @_ZSt9terminatev() -// CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: mustprogress nounwind uwtable -// CHECK:STDOUT: define linkonce_odr dso_local noundef i32 @_ZN11FurtherBase17further_base_funcEv(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #4 comdat align 2 { +// CHECK:STDOUT: define linkonce_odr dso_local noundef i32 @_ZN11FurtherBase17further_base_funcEv(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #1 comdat align 2 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %this.addr = alloca ptr, align 8 // CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !20 @@ -240,7 +217,7 @@ auto DoThing() -> int { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: mustprogress nounwind uwtable -// CHECK:STDOUT: define linkonce_odr dso_local noundef i32 @_ZN4Base4funcEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #4 comdat align 2 { +// CHECK:STDOUT: define linkonce_odr dso_local noundef i32 @_ZN4Base4funcEv(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #1 comdat 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 @@ -249,43 +226,43 @@ auto DoThing() -> int { // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define i32 @_Cother_func.Derived.Main(ptr %self) #5 !dbg !22 { +// CHECK:STDOUT: define i32 @_Cother_func.Derived.Main(ptr %self) #4 !dbg !22 { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret i32 3, !dbg !29 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind -// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Derived.Main"(ptr %self) #6 !dbg !30 { +// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Derived.Main"(ptr %self) #5 !dbg !30 { // CHECK:STDOUT: entry: // CHECK:STDOUT: call void @"_COp.12e0d0434542305a:core.Destroy.Core"(ptr %self), !dbg !35 // CHECK:STDOUT: ret void, !dbg !35 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.bfe31446004e2b71:core.Destroy.Core"(ptr %self) #5 !dbg !36 { +// CHECK:STDOUT: define weak_odr void @"_COp.bfe31446004e2b71:core.Destroy.Core"(ptr %self) #4 !dbg !36 { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret void, !dbg !39 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define weak_odr void @"_COp.12e0d0434542305a:core.Destroy.Core"(ptr %self) #5 !dbg !40 { +// CHECK:STDOUT: define weak_odr void @"_COp.12e0d0434542305a:core.Destroy.Core"(ptr %self) #4 !dbg !40 { // CHECK:STDOUT: entry: // CHECK:STDOUT: ret void, !dbg !43 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable -// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN4BaseC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #2 comdat align 2 { +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN4BaseC2Ev(ptr noundef nonnull align 8 dereferenceable(12) %this) unnamed_addr #3 comdat 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 @_ZN11FurtherBaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) #5 +// CHECK:STDOUT: call void @_ZN11FurtherBaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) #4 // CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTV4Base, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !16 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable -// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN11FurtherBaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #2 comdat align 2 { +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN11FurtherBaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #3 comdat align 2 { // CHECK:STDOUT: entry: // CHECK:STDOUT: %this.addr = alloca ptr, align 8 // CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !20 @@ -302,13 +279,11 @@ auto DoThing() -> int { // CHECK:STDOUT: uselistorder ptr @_ZN4Base4funcEv, { 2, 1, 0 } // 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 = { inlinehint 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 = { noinline noreturn nounwind uwtable "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 #4 = { 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 #5 = { nounwind } -// CHECK:STDOUT: attributes #6 = { alwaysinline nounwind } -// CHECK:STDOUT: attributes #7 = { noreturn nounwind } +// CHECK:STDOUT: attributes #1 = { 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 #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #3 = { inlinehint 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 #4 = { nounwind } +// CHECK:STDOUT: attributes #5 = { alwaysinline nounwind } // CHECK:STDOUT: // CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} // CHECK:STDOUT: !llvm.dbg.cu = !{!5} diff --git a/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon b/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon new file mode 100644 index 000000000000..ae31da0c7515 --- /dev/null +++ b/toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon @@ -0,0 +1,338 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// EXTRA-ARGS: --clang-arg=-fno-exceptions +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/class/virtual_fn.carbon + +// --- override_virtual_dtor.carbon +library "[[@TEST_NAME]]"; + +import Cpp; + +inline Cpp ''' +struct A { + virtual ~A() = 0; +}; +'''; + +class Final { + extend base: Cpp.A; +} + +base class Base { + extend base: Cpp.A; +} + +inline Cpp ''' +void delete_new_Final() { + // vtable should refer to Carbon::Final's dtor. + auto* p = new Carbon::Final; + // Perform a devirtualized call to Final destructor. + delete p; +} + +void delete_new_Base() { + // vtable should refer to Carbon::Base's dtor. + auto* p = new Carbon::Base; + // Perform a virtual call to destructor. + delete p; +} +'''; + +// CHECK:STDOUT: ; ModuleID = 'override_virtual_dtor.carbon' +// CHECK:STDOUT: source_filename = "override_virtual_dtor.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: $_ZN6Carbon5FinalC2Ev = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZN6Carbon5FinalD2Ev = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZN6Carbon4BaseC2Ev = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZN1AC2Ev = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZN6Carbon5FinalD0Ev = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZN6Carbon4BaseD2Ev = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZN6Carbon4BaseD0Ev = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTVN6Carbon5FinalE = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTIN6Carbon5FinalE = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTSN6Carbon5FinalE = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTI1A = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTS1A = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTV1A = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTVN6Carbon4BaseE = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTIN6Carbon4BaseE = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: $_ZTSN6Carbon4BaseE = comdat any +// CHECK:STDOUT: +// CHECK:STDOUT: @"_CFinal.Main.$vtable" = unnamed_addr constant [0 x i32] zeroinitializer +// CHECK:STDOUT: @"_CBase.Main.$vtable" = unnamed_addr constant [0 x i32] zeroinitializer +// CHECK:STDOUT: @_ZTVN6Carbon5FinalE = linkonce_odr dso_local unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTIN6Carbon5FinalE, ptr @_ZN6Carbon5FinalD2Ev, ptr @_ZN6Carbon5FinalD0Ev] }, comdat, align 8 +// CHECK:STDOUT: @_ZTIN6Carbon5FinalE = linkonce_odr dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2), ptr @_ZTSN6Carbon5FinalE, ptr @_ZTI1A }, comdat, align 8 +// CHECK:STDOUT: @_ZTVN10__cxxabiv120__si_class_type_infoE = external global [0 x ptr] +// CHECK:STDOUT: @_ZTSN6Carbon5FinalE = linkonce_odr dso_local constant [16 x i8] c"N6Carbon5FinalE\00", comdat, align 1 +// CHECK:STDOUT: @_ZTI1A = linkonce_odr dso_local constant { ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 2), ptr @_ZTS1A }, comdat, align 8 +// CHECK:STDOUT: @_ZTVN10__cxxabiv117__class_type_infoE = external global [0 x ptr] +// CHECK:STDOUT: @_ZTS1A = linkonce_odr dso_local constant [3 x i8] c"1A\00", comdat, align 1 +// CHECK:STDOUT: @_ZTV1A = linkonce_odr dso_local unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1A, ptr @__cxa_pure_virtual, ptr @__cxa_pure_virtual] }, comdat, align 8 +// CHECK:STDOUT: @_ZTVN6Carbon4BaseE = linkonce_odr dso_local unnamed_addr constant { [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTIN6Carbon4BaseE, ptr @_ZN6Carbon4BaseD2Ev, ptr @_ZN6Carbon4BaseD0Ev] }, comdat, align 8 +// CHECK:STDOUT: @_ZTIN6Carbon4BaseE = linkonce_odr dso_local constant { ptr, ptr, ptr } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 2), ptr @_ZTSN6Carbon4BaseE, ptr @_ZTI1A }, comdat, align 8 +// CHECK:STDOUT: @_ZTSN6Carbon4BaseE = linkonce_odr dso_local constant [15 x i8] c"N6Carbon4BaseE\00", comdat, align 1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: mustprogress nounwind uwtable +// CHECK:STDOUT: define dso_local void @_Z16delete_new_Finalv() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %p = alloca ptr, align 8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %p) #7 +// CHECK:STDOUT: %call = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #8 +// CHECK:STDOUT: call void @_ZN6Carbon5FinalC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %call) #7 +// CHECK:STDOUT: store ptr %call, ptr %p, align 8, !tbaa !11 +// CHECK:STDOUT: %0 = load ptr, ptr %p, align 8, !tbaa !11 +// CHECK:STDOUT: %isnull = icmp eq ptr %0, null +// CHECK:STDOUT: br i1 %isnull, label %delete.end, label %delete.notnull +// CHECK:STDOUT: +// CHECK:STDOUT: delete.notnull: ; preds = %entry +// CHECK:STDOUT: call void @_ZN6Carbon5FinalD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %0) #7 +// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %0, i64 noundef 8) #9 +// CHECK:STDOUT: br label %delete.end +// CHECK:STDOUT: +// CHECK:STDOUT: delete.end: ; preds = %delete.notnull, %entry +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %p) #7 +// 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: nobuiltin allocsize(0) +// CHECK:STDOUT: declare noundef nonnull ptr @_Znwm(i64 noundef) #2 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon5FinalC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #3 comdat 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 @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) #7 +// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTVN6Carbon5FinalE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon5FinalD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this) unnamed_addr #3 comdat 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 @"_C__destroy_thunk:thunk.Final.Main"(ptr noundef nonnull align 8 dereferenceable(8) %this1) +// CHECK:STDOUT: call void @_ZN1AD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this1) #7 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nobuiltin nounwind +// CHECK:STDOUT: declare void @_ZdlPvm(ptr noundef, i64 noundef) #4 +// 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: mustprogress nounwind uwtable +// CHECK:STDOUT: define dso_local void @_Z15delete_new_Basev() #0 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %p = alloca ptr, align 8 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %p) #7 +// CHECK:STDOUT: %call = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #8 +// CHECK:STDOUT: call void @_ZN6Carbon4BaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %call) #7 +// CHECK:STDOUT: store ptr %call, ptr %p, align 8, !tbaa !16 +// CHECK:STDOUT: %0 = load ptr, ptr %p, align 8, !tbaa !16 +// CHECK:STDOUT: %isnull = icmp eq ptr %0, null +// CHECK:STDOUT: br i1 %isnull, label %delete.end, label %delete.notnull +// CHECK:STDOUT: +// CHECK:STDOUT: delete.notnull: ; preds = %entry +// CHECK:STDOUT: %vtable = load ptr, ptr %0, align 8, !tbaa !14 +// CHECK:STDOUT: %vfn = getelementptr inbounds ptr, ptr %vtable, i64 1 +// CHECK:STDOUT: %1 = load ptr, ptr %vfn, align 8 +// CHECK:STDOUT: call void %1(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %0) #7 +// CHECK:STDOUT: br label %delete.end +// CHECK:STDOUT: +// CHECK:STDOUT: delete.end: ; preds = %delete.notnull, %entry +// CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %p) #7 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon4BaseC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #3 comdat align 2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !16 +// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK:STDOUT: call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) #7 +// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTVN6Carbon4BaseE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Final.Main"(ptr %self) #5 !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_COp.4472d163614a5082:core.Destroy.Core"(ptr %self), !dbg !24 +// CHECK:STDOUT: ret void, !dbg !24 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: declare void @_ZN1AD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8)) unnamed_addr #6 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.446491dd079aaad2:core.Destroy.Core"(ptr %self) #7 !dbg !25 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !28 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.4472d163614a5082:core.Destroy.Core"(ptr %self) #7 !dbg !29 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline nounwind +// CHECK:STDOUT: define void @"_C__destroy_thunk:thunk.Base.Main"(ptr %self) #5 !dbg !33 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_COp.96a711151205ee34:core.Destroy.Core"(ptr %self), !dbg !36 +// CHECK:STDOUT: ret void, !dbg !36 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_COp.96a711151205ee34:core.Destroy.Core"(ptr %self) #7 !dbg !37 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !40 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this) unnamed_addr #3 comdat align 2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !41 +// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTV1A, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon5FinalD0Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this) unnamed_addr #3 comdat 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 @_ZN6Carbon5FinalD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this1) #7 +// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %this1, i64 noundef 8) #9 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @__cxa_pure_virtual() unnamed_addr +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: declare void @_ZN1AD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8)) unnamed_addr #6 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon4BaseD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this) unnamed_addr #3 comdat align 2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !16 +// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK:STDOUT: store ptr getelementptr inbounds inrange(-16, 16) ({ [4 x ptr] }, ptr @_ZTVN6Carbon4BaseE, i32 0, i32 0, i32 2), ptr %this1, align 8, !tbaa !14 +// CHECK:STDOUT: call void @"_C__destroy_thunk:thunk.Base.Main"(ptr noundef nonnull align 8 dereferenceable(8) %this1) +// CHECK:STDOUT: call void @_ZN1AD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this1) #7 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon4BaseD0Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this) unnamed_addr #3 comdat align 2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %this.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !16 +// CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 +// CHECK:STDOUT: call void @_ZN6Carbon4BaseD2Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %this1) #7 +// CHECK:STDOUT: call void @_ZdlPvm(ptr noundef %this1, i64 noundef 8) #9 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @_ZN6Carbon5FinalD2Ev, { 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @_ZN1AC2Ev, { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @__cxa_pure_virtual, { 1, 0 } +// CHECK:STDOUT: uselistorder ptr @_ZN6Carbon4BaseD2Ev, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { 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 #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #2 = { nobuiltin allocsize(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 = { inlinehint 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 #4 = { nobuiltin nounwind "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 #5 = { alwaysinline nounwind } +// CHECK:STDOUT: attributes #6 = { nounwind "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 #7 = { nounwind } +// CHECK:STDOUT: attributes #8 = { builtin allocsize(0) } +// CHECK:STDOUT: attributes #9 = { builtin 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: "override_virtual_dtor.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 _ZTSN6Carbon5FinalE", !13, i64 0} +// CHECK:STDOUT: !13 = !{!"any pointer", !9, i64 0} +// CHECK:STDOUT: !14 = !{!15, !15, i64 0} +// CHECK:STDOUT: !15 = !{!"vtable pointer", !10, i64 0} +// CHECK:STDOUT: !16 = !{!17, !17, i64 0} +// CHECK:STDOUT: !17 = !{!"p1 _ZTSN6Carbon4BaseE", !13, i64 0} +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Final.Main", scope: null, file: !6, line: 11, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !22) +// CHECK:STDOUT: !19 = !DISubroutineType(types: !20) +// CHECK:STDOUT: !20 = !{null, !21} +// CHECK:STDOUT: !21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !22 = !{!23} +// CHECK:STDOUT: !23 = !DILocalVariable(arg: 1, scope: !18, type: !21) +// CHECK:STDOUT: !24 = !DILocation(line: 11, column: 1, scope: !18) +// CHECK:STDOUT: !25 = distinct !DISubprogram(name: "Op", linkageName: "_COp.446491dd079aaad2:core.Destroy.Core", scope: null, file: !6, line: 11, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !26) +// CHECK:STDOUT: !26 = !{!27} +// CHECK:STDOUT: !27 = !DILocalVariable(arg: 1, scope: !25, type: !21) +// CHECK:STDOUT: !28 = !DILocation(line: 11, column: 1, scope: !25) +// CHECK:STDOUT: !29 = distinct !DISubprogram(name: "Op", linkageName: "_COp.4472d163614a5082:core.Destroy.Core", scope: null, file: !6, line: 11, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !30) +// CHECK:STDOUT: !30 = !{!31} +// CHECK:STDOUT: !31 = !DILocalVariable(arg: 1, scope: !29, type: !21) +// CHECK:STDOUT: !32 = !DILocation(line: 11, column: 1, scope: !29) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "__destroy_thunk", linkageName: "_C__destroy_thunk:thunk.Base.Main", scope: null, file: !6, line: 15, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !34) +// CHECK:STDOUT: !34 = !{!35} +// CHECK:STDOUT: !35 = !DILocalVariable(arg: 1, scope: !33, type: !21) +// CHECK:STDOUT: !36 = !DILocation(line: 15, column: 1, scope: !33) +// CHECK:STDOUT: !37 = distinct !DISubprogram(name: "Op", linkageName: "_COp.96a711151205ee34:core.Destroy.Core", scope: null, file: !6, line: 15, type: !19, spFlags: DISPFlagDefinition, unit: !5, retainedNodes: !38) +// CHECK:STDOUT: !38 = !{!39} +// CHECK:STDOUT: !39 = !DILocalVariable(arg: 1, scope: !37, type: !21) +// CHECK:STDOUT: !40 = !DILocation(line: 15, column: 1, scope: !37) +// CHECK:STDOUT: !41 = !{!42, !42, i64 0} +// CHECK:STDOUT: !42 = !{!"p1 _ZTS1A", !13, i64 0} diff --git a/toolchain/lower/testdata/interop/cpp/reverse/class.carbon b/toolchain/lower/testdata/interop/cpp/reverse/class.carbon index 790523600207..a473d98c0b2b 100644 --- a/toolchain/lower/testdata/interop/cpp/reverse/class.carbon +++ b/toolchain/lower/testdata/interop/cpp/reverse/class.carbon @@ -169,7 +169,7 @@ fn G() { // CHECK:STDOUT: %a = alloca %"class.Carbon::A", align 8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a) #5 // CHECK:STDOUT: call void @_ZN6CarbonL5CallAEPNS_1AE(ptr noundef %a) -// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %a) +// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %a) #5 // CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %a) #5 // CHECK:STDOUT: ret void // CHECK:STDOUT: } @@ -205,7 +205,7 @@ fn G() { // CHECK:STDOUT: %b = alloca %"class.Carbon::B", align 8 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %b) #5 // CHECK:STDOUT: call void @_ZN6CarbonL5CallBEPNS_1BE(ptr noundef %b) -// CHECK:STDOUT: call void @_ZN6Carbon1BD2Ev(ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) %b) +// CHECK:STDOUT: call void @_ZN6Carbon1BD2Ev(ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) %b) #5 // CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %b) #5 // CHECK:STDOUT: ret void // CHECK:STDOUT: } @@ -227,7 +227,7 @@ fn G() { // CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 // CHECK:STDOUT: call void @"_C__destroy_thunk:thunk.B.Main"(ptr noundef nonnull align 8 dereferenceable(16) %this1) // CHECK:STDOUT: %a = getelementptr inbounds nuw %"class.Carbon::B", ptr %this1, i32 0, i32 0 -// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %a) +// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %a) #5 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: @@ -238,7 +238,7 @@ fn G() { // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %c) #5 // CHECK:STDOUT: call void @_ZN6CarbonL5CallCEPNS_1CE(ptr noundef %c) // CHECK:STDOUT: call void @_ZN6CarbonL5CallAEPNS_1AE(ptr noundef %c) -// CHECK:STDOUT: call void @_ZN6Carbon1CD2Ev(ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) %c) +// CHECK:STDOUT: call void @_ZN6Carbon1CD2Ev(ptr noundef nonnull align 8 dead_on_return(16) dereferenceable(16) %c) #5 // CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %c) #5 // CHECK:STDOUT: ret void // CHECK:STDOUT: } @@ -259,7 +259,7 @@ fn G() { // CHECK:STDOUT: store ptr %this, ptr %this.addr, align 8, !tbaa !16 // CHECK:STDOUT: %this1 = load ptr, ptr %this.addr, align 8 // CHECK:STDOUT: call void @"_C__destroy_thunk:thunk.C.Main"(ptr noundef nonnull align 8 dereferenceable(16) %this1) -// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %this1) +// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 8 dead_on_return(12) dereferenceable(12) %this1) #5 // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/testdata/interop/cpp/reverse/method.carbon b/toolchain/lower/testdata/interop/cpp/reverse/method.carbon index cdeb9f401246..83cbb1be38b3 100644 --- a/toolchain/lower/testdata/interop/cpp/reverse/method.carbon +++ b/toolchain/lower/testdata/interop/cpp/reverse/method.carbon @@ -77,8 +77,8 @@ fn CallCallF() { Cpp.CallF(); } // CHECK:STDOUT: entry: // CHECK:STDOUT: %ref.tmp = alloca %"class.Carbon::A", align 1 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %ref.tmp) #4 -// CHECK:STDOUT: call void @_ZN6Carbon1A1FEv(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp) -// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp) +// CHECK:STDOUT: call void @_ZN6Carbon1A1FEv(ptr noundef nonnull align 1 %ref.tmp) +// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 1 %ref.tmp) #4 // CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %ref.tmp) #4 // CHECK:STDOUT: ret void // CHECK:STDOUT: } @@ -87,22 +87,22 @@ fn CallCallF() { Cpp.CallF(); } // 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: define internal void @_ZN6Carbon1A1FEv(ptr noundef nonnull align 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: call void @_CF__carbon_thunk.A.Main(ptr noundef nonnull align 1 %this1) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: inlinehint mustprogress nounwind uwtable -// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 1 dereferenceable(1) %this) unnamed_addr #3 comdat align 2 { +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 1 %this) unnamed_addr #3 comdat 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 @"_C__destroy_thunk:thunk.A.Main"(ptr noundef nonnull align 1 dereferenceable(1) %this1) +// CHECK:STDOUT: call void @"_C__destroy_thunk:thunk.A.Main"(ptr noundef nonnull align 1 %this1) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: @@ -193,8 +193,8 @@ fn CallCallF() { Cpp.CallF(); } // CHECK:STDOUT: entry: // CHECK:STDOUT: %ref.tmp = alloca %"class.Carbon::A", align 1 // CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %ref.tmp) #4 -// CHECK:STDOUT: %call = call noundef i32 @_ZN6Carbon1A1FEi(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp, i32 noundef 123) -// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 1 dereferenceable(1) %ref.tmp) +// CHECK:STDOUT: %call = call noundef i32 @_ZN6Carbon1A1FEi(ptr noundef nonnull align 1 %ref.tmp, i32 noundef 123) +// CHECK:STDOUT: call void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 1 %ref.tmp) #4 // CHECK:STDOUT: call void @llvm.lifetime.end.p0(ptr %ref.tmp) #4 // CHECK:STDOUT: ret i32 %call // CHECK:STDOUT: } @@ -203,7 +203,7 @@ fn CallCallF() { Cpp.CallF(); } // 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: define internal noundef i32 @_ZN6Carbon1A1FEi(ptr noundef nonnull align 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 @@ -211,18 +211,18 @@ fn CallCallF() { Cpp.CallF(); } // 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: call void @_CF__carbon_thunk.A.Main(ptr noundef nonnull align 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: inlinehint mustprogress nounwind uwtable -// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 1 dereferenceable(1) %this) unnamed_addr #3 comdat align 2 { +// CHECK:STDOUT: define linkonce_odr dso_local void @_ZN6Carbon1AD2Ev(ptr noundef nonnull align 1 %this) unnamed_addr #3 comdat 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 @"_C__destroy_thunk:thunk.A.Main"(ptr noundef nonnull align 1 dereferenceable(1) %this1) +// CHECK:STDOUT: call void @"_C__destroy_thunk:thunk.A.Main"(ptr noundef nonnull align 1 %this1) // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: