Skip to content

Commit fbbb593

Browse files
committed
IRGen: Lower super_method SIL instruction for native classes
This is the first part of making class method dispatch resilient. If we have the following class hierarchy: // Module A class Parent { func foo() {} } class Child : Parent {} // Module B class Grandchild : Child { override func foo() { super.foo() } } dispatch to `Parent.foo` will be static via a `function_ref`, so if someone adds a `foo()` to `Child` later on, `Grandchild` won't know about it without recompiling. Stage in the IRGen portion of dynamic dispatch when calling methods on `super`: - Don't assert in IRGen if we see a native super_method instruction. - Perform virtual lookup on superclass's metadata. If we see a `super_method` instruction for a native class: - Get the address of the superclass's metadata at offset 1 - Load the superclass's metadata - Perform virtual lookup on this metadata instead TODO: SILGen super_method instructions for native classes. TODO: Devirtualize back down to static dispatch with a reslience lookup mechanism. rdar://problem/22749732
1 parent c7c180f commit fbbb593

File tree

5 files changed

+159
-7
lines changed

5 files changed

+159
-7
lines changed

lib/IRGen/GenMeta.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -4298,7 +4298,8 @@ llvm::Value *irgen::emitVirtualMethodValue(IRGenFunction &IGF,
42984298
llvm::Value *base,
42994299
SILType baseType,
43004300
SILDeclRef method,
4301-
CanSILFunctionType methodType) {
4301+
CanSILFunctionType methodType,
4302+
bool useSuperVTable) {
43024303
AbstractFunctionDecl *methodDecl
43034304
= cast<AbstractFunctionDecl>(method.getDecl());
43044305

@@ -4317,6 +4318,11 @@ llvm::Value *irgen::emitVirtualMethodValue(IRGenFunction &IGF,
43174318
/*suppress cast*/ true);
43184319
}
43194320

4321+
if (useSuperVTable) {
4322+
auto superField = emitAddressOfSuperclassRefInClassMetadata(IGF, metadata);
4323+
metadata = IGF.Builder.CreateLoad(superField);
4324+
}
4325+
43204326
// Use the type of the method we were type-checked against, not the
43214327
// type of the overridden method.
43224328
llvm::AttributeSet attrs;

lib/IRGen/GenMeta.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ namespace irgen {
214214
llvm::Value *base,
215215
SILType baseType,
216216
SILDeclRef method,
217-
CanSILFunctionType methodType);
217+
CanSILFunctionType methodType,
218+
bool useSuperVTable);
218219

219220
/// \brief Load a reference to the protocol descriptor for the given protocol.
220221
///

lib/IRGen/IRGenSIL.cpp

+24-5
Original file line numberDiff line numberDiff line change
@@ -4540,10 +4540,28 @@ void IRGenSILFunction::visitCondFailInst(swift::CondFailInst *i) {
45404540
}
45414541

45424542
void IRGenSILFunction::visitSuperMethodInst(swift::SuperMethodInst *i) {
4543-
assert(i->getMember().isForeign && "super_method to non_objc callee");
4544-
setLoweredObjCMethodBounded(SILValue(i, 0), i->getMember(),
4545-
i->getOperand().getType(),
4546-
/*startAtSuper=*/true);
4543+
if (i->getMember().isForeign) {
4544+
setLoweredObjCMethodBounded(SILValue(i, 0), i->getMember(),
4545+
i->getOperand().getType(),
4546+
/*startAtSuper=*/true);
4547+
return;
4548+
}
4549+
4550+
auto base = getLoweredExplosion(i->getOperand());
4551+
auto baseType = i->getOperand().getType();
4552+
llvm::Value *baseValue = base.claimNext();
4553+
4554+
auto method = i->getMember();
4555+
auto methodType = i->getType().castTo<SILFunctionType>();
4556+
4557+
llvm::Value *fnValue = emitVirtualMethodValue(*this, baseValue,
4558+
baseType,
4559+
method, methodType,
4560+
/*useSuperVTable*/ true);
4561+
fnValue = Builder.CreateBitCast(fnValue, IGM.Int8PtrTy);
4562+
Explosion e;
4563+
e.add(fnValue);
4564+
setLoweredExplosion(SILValue(i, 0), e);
45474565
}
45484566

45494567
void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) {
@@ -4564,7 +4582,8 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) {
45644582
// FIXME: better explosion kind, map as static.
45654583
llvm::Value *fnValue = emitVirtualMethodValue(*this, baseValue,
45664584
i->getOperand().getType(),
4567-
method, methodType);
4585+
method, methodType,
4586+
/*useSuperVTable*/ false);
45684587
fnValue = Builder.CreateBitCast(fnValue, IGM.Int8PtrTy);
45694588
Explosion e;
45704589
e.add(fnValue);

test/IRGen/super_class_method.sil

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %target-swift-frontend -emit-ir %s | FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
import Swift
7+
import SwiftShims
8+
9+
public class Parent {
10+
public class func foo() {}
11+
}
12+
13+
public class Child : Parent {}
14+
15+
public class Grandchild : Child {
16+
override public class func foo() {
17+
super.foo()
18+
}
19+
}
20+
21+
// static test2.Parent.foo () -> ()
22+
sil @_TZFC5test26Parent3foofT_T_ : $@convention(thin) (@thick Parent.Type) -> () {
23+
// %0 // user: %1
24+
bb0(%0 : $@thick Parent.Type):
25+
debug_value %0 : $@thick Parent.Type // let self // id: %1
26+
%2 = tuple () // user: %3
27+
return %2 : $() // id: %3
28+
}
29+
30+
// static test2.Grandchild.foo () -> ()
31+
sil @_TZFC5test210Grandchild3foofT_T_ : $@convention(thin) (@thick Grandchild.Type) -> () {
32+
// %0 // users: %1, %2
33+
bb0(%0 : $@thick Grandchild.Type):
34+
debug_value %0 : $@thick Grandchild.Type // let self // id: %1
35+
%2 = upcast %0 : $@thick Grandchild.Type to $@thick Child.Type // user: %3
36+
%3 = upcast %2 : $@thick Child.Type to $@thick Parent.Type // user: %5
37+
%4 = super_method %0 : $@thick Grandchild.Type, #Parent.foo!1 : (Parent.Type) -> () -> (), $@convention(thin) (@thick Parent.Type) -> () // user: %5
38+
%5 = apply %4(%3) : $@convention(thin) (@thick Parent.Type) -> ()
39+
%6 = tuple () // user: %7
40+
return %6 : $() // id: %7
41+
}
42+
43+
sil_vtable Parent {
44+
#Parent.foo!1: _TZFC5test26Parent3foofT_T_ // static test2.Parent.foo () -> ()
45+
}
46+
47+
sil_vtable Child {
48+
#Parent.foo!1: _TZFC5test26Parent3foofT_T_ // static test2.Parent.foo () -> ()
49+
}
50+
51+
sil_vtable Grandchild {
52+
#Parent.foo!1: _TZFC5test210Grandchild3foofT_T_ // static test2.Grandchild.foo () -> ()
53+
}
54+
55+
56+
// CHECK-LABEL: define void @_TZFC5test210Grandchild3foofT_T_(%swift.type*) #0
57+
// CHECK: [[OPAQUE_GRANDCHILD:%[0-9]+]] = bitcast %swift.type* %0 to %swift.type**
58+
// CHECK: [[SUPER_METADATA_PTR:%[0-9]+]] = getelementptr inbounds %swift.type*, %swift.type** [[OPAQUE_GRANDCHILD]], i32 1
59+
// CHECK: [[SUPER_METADATA:%[0-9]+]] = load %swift.type*, %swift.type** [[SUPER_METADATA_PTR]]
60+
// CHECK: [[FOO_VTABLE_SLOT:%[0-9]+]] = getelementptr inbounds void (%swift.type*)*, void (%swift.type*)**
61+
// CHECK: [[FOO_FNPTR:%[0-9]+]] = load void (%swift.type*)*, void (%swift.type*)** [[FOO_VTABLE_SLOT]]
62+
// CHECK: call void

test/IRGen/super_instance_method.sil

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %target-swift-frontend -emit-ir %s | FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
import Swift
7+
import SwiftShims
8+
9+
public class Parent {
10+
public func foo() {}
11+
}
12+
13+
public class Child : Parent {}
14+
15+
public class Grandchild : Child {
16+
override public func foo() { super.foo() }
17+
}
18+
19+
// test.Parent.foo () -> ()
20+
sil @_TFC4test6Parent3foofT_T_ : $@convention(method) (@guaranteed Parent) -> () {
21+
// %0 // user: %1
22+
bb0(%0 : $Parent):
23+
debug_value %0 : $Parent // let self // id: %1
24+
%2 = tuple () // user: %3
25+
return %2 : $() // id: %3
26+
}
27+
28+
// test.Grandchild.foo () -> ()
29+
sil @_TFC4test10Grandchild3foofT_T_ : $@convention(method) (@guaranteed Grandchild) -> () {
30+
// %0 // users: %1, %2, %3
31+
bb0(%0 : $Grandchild):
32+
debug_value %0 : $Grandchild // let self // id: %1
33+
strong_retain %0 : $Grandchild // id: %2
34+
%3 = upcast %0 : $Grandchild to $Child // user: %4
35+
%4 = upcast %3 : $Child to $Parent // users: %6, %7
36+
// function_ref test.Parent.foo () -> ()
37+
%5 = super_method %0 : $Grandchild, #Parent.foo!1 : (Parent) -> () -> (), $@convention(method) (@guaranteed Parent) -> ()
38+
%6 = apply %5(%4) : $@convention(method) (@guaranteed Parent) -> ()
39+
strong_release %4 : $Parent // id: %7
40+
%8 = tuple () // user: %9
41+
return %8 : $() // id: %9
42+
}
43+
44+
sil_vtable Parent {
45+
#Parent.foo!1: _TFC4test6Parent3foofT_T_ // test.Parent.foo () -> ()
46+
}
47+
48+
sil_vtable Child {
49+
#Parent.foo!1: _TFC4test6Parent3foofT_T_ // test.Parent.foo () -> ()
50+
}
51+
52+
sil_vtable Grandchild {
53+
#Parent.foo!1: _TFC4test10Grandchild3foofT_T_ // test.Grandchild.foo () -> ()
54+
}
55+
56+
// CHECK: define void @_TFC4test10Grandchild3foofT_T_(%C21super_instance_method10Grandchild*)
57+
// CHECK: [[OPAQUE_GRANDCHILD:%[0-9]+]] = bitcast %C21super_instance_method10Grandchild* %0 to %swift.type**
58+
// CHECK: [[GRANDCHILD_METADATA:%\..*]] = load %swift.type*, %swift.type** [[OPAQUE_GRANDCHILD]]
59+
// CHECK: [[OPAQUE_METADATA:%[0-9]+]] = bitcast %swift.type* [[GRANDCHILD_METADATA]] to %swift.type**
60+
// CHECK: [[SUPER_METADATA_PTR:%[0-9]+]] = getelementptr inbounds %swift.type*, %swift.type** [[OPAQUE_METADATA]], i32 1
61+
// CHECK: [[SUPER_METADATA:%[0-9]+]] = load %swift.type*, %swift.type** [[SUPER_METADATA_PTR]]
62+
// CHECK: [[FOO_VTABLE_SLOT:%[0-9]+]] = getelementptr inbounds void (%C21super_instance_method6Parent*)*, void (%C21super_instance_method6Parent*)**
63+
// CHECK: [[FOO_FNPTR:%[0-9]+]] = load void (%C21super_instance_method6Parent*)*, void (%C21super_instance_method6Parent*)** [[FOO_VTABLE_SLOT]]
64+
// CHECK: call void

0 commit comments

Comments
 (0)