Skip to content

Commit 87b3127

Browse files
Nuri AmariNuriAmari
authored andcommitted
[swift-vfe] Disable for @objc classes
Swift methods can be called from Objective-C without a vtable load, and more importantly without emitting an `llvm.type.checked.load` instruction. These are effectively virtual callsites that are not visible to the VFE logic in GlobalDCE. To to safely enable swift-vfe we must prevent vtables whose functions are callable from Objective-C from being eliminated. To do this we ensure such vtables have public vcall_visibility, which indirectly prevents GlobalDCE from stripping them.
1 parent 7c6dacb commit 87b3127

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

lib/IRGen/GenMeta.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,15 @@ void IRGenModule::addVTableTypeMetadata(
382382
using VCallVisibility = llvm::GlobalObject::VCallVisibility;
383383
VCallVisibility vis = VCallVisibility::VCallVisibilityPublic;
384384
auto AS = decl->getFormalAccessScope();
385-
if (AS.isFileScope()) {
385+
if (decl->isObjC()) {
386+
// Swift methods are called from Objective-C via objc_MsgSend
387+
// and thus such call sites are not taken into consideration
388+
// by VFE in GlobalDCE. We cannot for the timebeing at least
389+
// safely eliminate a virtual function that might be called from
390+
// Objective-C. Setting vcall_visibility to public ensures this is
391+
// prevented.
392+
vis = VCallVisibility::VCallVisibilityPublic;
393+
} else if (AS.isFileScope()) {
386394
vis = VCallVisibility::VCallVisibilityTranslationUnit;
387395
} else if (AS.isPrivate() || AS.isInternal()) {
388396
vis = VCallVisibility::VCallVisibilityLinkageUnit;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend -enable-llvm-vfe -internalize-at-link -emit-ir %s -o %t/ir.ll
4+
// RUN: %FileCheck --input-file %t/ir.ll %s --check-prefix=OBJC-BASE
5+
// RUN: %FileCheck --input-file %t/ir.ll %s --check-prefix=OBJC-DERIVED
6+
// RUN: %FileCheck --input-file %t/ir.ll %s --check-prefix=BASE
7+
// RUN: %FileCheck --input-file %t/ir.ll %s --check-prefix=DERIVED
8+
// RUN: echo '' > %t/used-symbols
9+
// RUN: %target-build-swift -emit-library -lto=llvm-full %lto_flags -Xfrontend -enable-llvm-vfe -Xfrontend -internalize-at-link -Xlinker -exported_symbols_list -Xlinker %t/used-symbols %s -o %t/main
10+
// RUN: %llvm-nm --defined-only %t/main | %FileCheck %s --check-prefix=NM
11+
12+
// REQUIRES: objc_interop
13+
// REQUIRES: no_asan
14+
// UNSUPPORTED: remote_run
15+
16+
// Tests that under -enable-llvm-vfe @objc virtual functions are not eligible for elimination
17+
18+
import Foundation
19+
20+
// Objective-C exposed types should have public vcall_visibility even under
21+
// -internalize-at-link
22+
23+
@objc public class ObjCBase : NSObject {
24+
@objc public func foo() { print("ObjCBase.foo") }
25+
@objc public func bar() { print("ObjCBase.bar") }
26+
}
27+
28+
// OBJC-BASE: @"$s2ir8ObjCBaseCMn" = {{.*}}!vcall_visibility ![[VCALL_VISIBILITY_METADATA_NODE:[0-9]+]]
29+
// OBJC-BASE: ![[VCALL_VISIBILITY_METADATA_NODE]] = !{i64 0
30+
31+
@objc public class ObjCDerived : ObjCBase {
32+
@objc override public func foo() { print("ObjCDerived.foo") }
33+
@objc override public func bar() { print("ObjCDerived.bar") }
34+
}
35+
36+
// OBJC-DERIVED: @"$s2ir11ObjCDerivedCMn" = {{.*}}!vcall_visibility ![[VCALL_VISIBILITY_METADATA_NODE:[0-9]+]]
37+
// OBJC-DERIVED: ![[VCALL_VISIBILITY_METADATA_NODE]] = !{i64 0
38+
39+
// Regular Swift types should have linkage unit vcall_visibility
40+
41+
public class Base {
42+
public func foo() { print("Base.foo") }
43+
public func bar() { print("Base.bar") }
44+
}
45+
46+
// BASE: @"$s2ir4BaseCMn" = {{.*}}!vcall_visibility ![[VCALL_VISIBILITY_METADATA_NODE:[0-9]+]]
47+
// BASE: ![[VCALL_VISIBILITY_METADATA_NODE]] = !{i64 1
48+
49+
public class Derived : Base {
50+
override public func foo() { print("Derived.foo") }
51+
override public func bar() { print("Derived.bar") }
52+
}
53+
54+
// DERIVED: @"$s2ir7DerivedCMn" = {{.*}}!vcall_visibility ![[VCALL_VISIBILITY_METADATA_NODE:[0-9]+]]
55+
// DERIVED: ![[VCALL_VISIBILITY_METADATA_NODE]] = !{i64 1
56+
57+
// NM: $s4main11ObjCDerivedC3baryyF{{$}}
58+
// NM: $s4main11ObjCDerivedC3fooyyF{{$}}
59+
// NM-NOT: $s4main4BaseC3baryyF{{$}}
60+
// NM-NOT: $s4main4BaseC3fooyyF{{$}}
61+
// NM-NOT: $s4main7DerivedC3baryyF{{$}}
62+
// NM-NOT: $s4main7DerivedC3fooyyF{{$}}
63+
// NM: $s4main8ObjCBaseC3baryyF{{$}}
64+
// NM: $s4main8ObjCBaseC3fooyyF{{$}}

0 commit comments

Comments
 (0)