Skip to content

Commit 4150450

Browse files
committed
[cxx-interop] Use a synthesized C++ method when accessing a base field or subscript from a derived class synthesized method
The use of a synthesized C++ method allows us to avoid making a copy of self when accessing the base field or subscript from Swift
1 parent 82f8d5d commit 4150450

15 files changed

+596
-90
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 235 additions & 38 deletions
Large diffs are not rendered by default.

test/Interop/Cxx/class/inheritance/Inputs/fields.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,37 @@ struct ClassTemplate {
6666
};
6767

6868
struct DerivedFromClassTemplate : ClassTemplate<int> {};
69+
70+
int &getCopyCounter() {
71+
static int copyCounter = 0;
72+
return copyCounter;
73+
}
74+
75+
class CopyTrackedBaseClass {
76+
public:
77+
CopyTrackedBaseClass(int x) : x(x) {}
78+
CopyTrackedBaseClass(const CopyTrackedBaseClass &other) : x(other.x) {
79+
++getCopyCounter();
80+
}
81+
82+
int x;
83+
};
84+
85+
class CopyTrackedDerivedClass: public CopyTrackedBaseClass {
86+
public:
87+
CopyTrackedDerivedClass(int x) : CopyTrackedBaseClass(x) {}
88+
};
89+
90+
class NonEmptyBase {
91+
public:
92+
int getY() const {
93+
return y;
94+
}
95+
private:
96+
int y = 11;
97+
};
98+
99+
class CopyTrackedDerivedDerivedClass: public NonEmptyBase, public CopyTrackedDerivedClass {
100+
public:
101+
CopyTrackedDerivedDerivedClass(int x) : CopyTrackedDerivedClass(x) {}
102+
};

test/Interop/Cxx/class/inheritance/Inputs/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ module SubTypes {
1515
header "sub-types.h"
1616
}
1717

18+
module Subscripts {
19+
header "subscripts.h"
20+
}
21+
1822
module TypeAliases {
1923
header "type-aliases.h"
2024
}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
#pragma once
2-
3-
#define FRT \
4-
__attribute__((swift_attr("import_reference"))) \
5-
__attribute__((swift_attr("retain:immortal"))) \
6-
__attribute__((swift_attr("release:immortal")))
7-
81
int &getCopyCounter() {
92
static int copyCounter = 0;
103
return copyCounter;
@@ -17,25 +10,17 @@ class CopyTrackedBaseClass {
1710
++getCopyCounter();
1811
}
1912

20-
int getX() const {
21-
return x;
13+
int operator [](int y) const {
14+
return y + x;
2215
}
2316
private:
2417
int x;
25-
} FRT;
18+
};
2619

2720
class CopyTrackedDerivedClass: public CopyTrackedBaseClass {
2821
public:
2922
CopyTrackedDerivedClass(int x) : CopyTrackedBaseClass(x) {}
30-
31-
int getDerivedX() const {
32-
return getX();
33-
}
34-
} FRT;
35-
36-
CopyTrackedDerivedClass *makeCopyTrackedDerivedClass(int x) {
37-
return new CopyTrackedDerivedClass(x);
38-
}
23+
};
3924

4025
class NonEmptyBase {
4126
public:
@@ -44,13 +29,42 @@ class NonEmptyBase {
4429
}
4530
private:
4631
int y = 11;
47-
} FRT;
32+
};
4833

4934
class CopyTrackedDerivedDerivedClass: public NonEmptyBase, public CopyTrackedDerivedClass {
5035
public:
5136
CopyTrackedDerivedDerivedClass(int x) : CopyTrackedDerivedClass(x) {}
52-
} FRT;
37+
};
5338

54-
CopyTrackedDerivedDerivedClass *makeCopyTrackedDerivedDerivedClass(int x) {
55-
return new CopyTrackedDerivedDerivedClass(x);
56-
}
39+
class SubscriptReturnsRef {
40+
public:
41+
const int &operator [](int y) const {
42+
return x[y];
43+
}
44+
int &operator [](int y) {
45+
return x[y];
46+
}
47+
48+
private:
49+
int x[10] = {0};
50+
};
51+
52+
class DerivedSubscriptReturnsRef: public SubscriptReturnsRef {
53+
public:
54+
inline DerivedSubscriptReturnsRef() : SubscriptReturnsRef() {}
55+
};
56+
57+
class NonConstSubscriptReturnsRef {
58+
public:
59+
int &operator [](int y) {
60+
return x[y];
61+
}
62+
63+
private:
64+
int x[10] = {0};
65+
};
66+
67+
class DerivedNonConstSubscriptReturnsRef: public NonConstSubscriptReturnsRef {
68+
public:
69+
inline DerivedNonConstSubscriptReturnsRef() : NonConstSubscriptReturnsRef() {}
70+
};
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-emit-irgen -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none -Xcc -fignore-exceptions | %FileCheck %s
2+
3+
import Fields
4+
5+
func testGetX() -> CInt {
6+
let derivedDerived = CopyTrackedDerivedDerivedClass(42)
7+
return derivedDerived.x
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{.*}}__synthesizedBaseCall___synthesizedBaseGetterAccessor{{.*}}(ptr {{.*}} %[[THIS_PTR:.*]])
13+
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i64 4
14+
// CHECK: call noundef i32 @{{.*}}__synthesizedBaseGetterAccessor{{.*}}(ptr {{.*}} %[[ADD_PTR]])
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-emit-sil -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none | %FileCheck %s
2+
3+
import Fields
4+
5+
func testGetX() -> CInt {
6+
let derived = CopyTrackedDerivedClass(42)
7+
return derived.x
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: sil shared [transparent] @$sSo23CopyTrackedDerivedClassV1xs5Int32Vvg : $@convention(method) (@in_guaranteed CopyTrackedDerivedClass) -> Int32
13+
// CHECK: {{.*}}(%[[SELF_VAL:.*]] : $*CopyTrackedDerivedClass):
14+
// CHECK: function_ref @{{.*}}__synthesizedBaseGetterAccessor_{{.*}} : $@convention(cxx_method) (@in_guaranteed CopyTrackedDerivedClass) -> Int32
15+
// CHECK-NEXT: apply %{{.*}}(%[[SELF_VAL]])

test/Interop/Cxx/class/inheritance/fields.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,31 @@ FieldsTestSuite.test("Same field from derived") {
8585
expectEqual(derived.a, 42)
8686
}
8787

88+
extension CopyTrackedBaseClass {
89+
var swiftProp: CInt {
90+
return x
91+
}
92+
}
93+
94+
FieldsTestSuite.test("Get field without copying base in the getter accessor") {
95+
let base = CopyTrackedBaseClass(0)
96+
var copyCounter = getCopyCounter().pointee
97+
expectEqual(base.swiftProp, 0)
98+
// Measure copy counter of a regular
99+
// property access for a C++ type to compare
100+
// it to see if any additional copies are
101+
// needed to access the property from the base class.
102+
let expectedCopyCountDiff = getCopyCounter().pointee - copyCounter
103+
104+
let derived = CopyTrackedDerivedClass(234)
105+
copyCounter = getCopyCounter().pointee
106+
expectEqual(derived.x, 234)
107+
expectEqual(copyCounter, getCopyCounter().pointee - expectedCopyCountDiff)
108+
109+
let derivedDerived = CopyTrackedDerivedDerivedClass(-11)
110+
copyCounter = getCopyCounter().pointee
111+
expectEqual(derivedDerived.x, -11)
112+
expectEqual(copyCounter, getCopyCounter().pointee - expectedCopyCountDiff)
113+
}
114+
88115
runAllTests()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-emit-irgen -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none -Xcc -fignore-exceptions | %FileCheck %s
2+
3+
import Subscripts
4+
5+
func testGetX() -> CInt {
6+
let derivedDerived = CopyTrackedDerivedDerivedClass(42)
7+
return derivedDerived[0]
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: define {{.*}}linkonce_odr{{.*}} i32 @{{.*}}__synthesizedBaseCall___synthesizedBaseCall_operatorSubscript{{.*}}(ptr {{.*}} %[[THIS_PTR:.*]], i32 {{.*}})
13+
// CHECK: %[[ADD_PTR:.*]] = getelementptr inbounds i8, ptr %{{.*}}, i64 4
14+
// CHECK: call noundef i32 @{{.*}}__synthesizedBaseCall_operatorSubscript{{.*}}(ptr {{.*}} %[[ADD_PTR]], i32 {{.*}})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: %target-swift-emit-sil -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none | %FileCheck %s
2+
3+
import Subscripts
4+
5+
func testGetX() -> CInt {
6+
let derived = CopyTrackedDerivedClass(42)
7+
return derived[0]
8+
}
9+
10+
let _ = testGetX()
11+
12+
// CHECK: sil shared [transparent] @$sSo23CopyTrackedDerivedClassVys5Int32VADcig : $@convention(method) (Int32, @in_guaranteed CopyTrackedDerivedClass) -> Int32
13+
// CHECK: {{.*}}(%[[INT_VAL:.*]] : $Int32, %[[SELF_VAL:.*]] : $*CopyTrackedDerivedClass):
14+
// CHECK: function_ref @{{.*}}__synthesizedBaseCall_operatorSubscript{{.*}} : $@convention(cxx_method) (Int32, @in_guaranteed CopyTrackedDerivedClass) -> Int32
15+
// CHECK-NEXT: apply %{{.*}}(%[[INT_VAL]], %[[SELF_VAL]])
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
5+
import CxxShim
6+
import StdlibUnittest
7+
import Subscripts
8+
9+
var FieldsTestSuite = TestSuite("Getting and setting subscripts in base classes")
10+
11+
FieldsTestSuite.test("Subscript from derived returning ref") {
12+
var derived = DerivedSubscriptReturnsRef()
13+
expectEqual(derived[0], 0)
14+
derived[0] = 42
15+
expectEqual(derived[0], 42)
16+
}
17+
18+
FieldsTestSuite.test("Non-const subscript from derived returning ref") {
19+
var derived = DerivedNonConstSubscriptReturnsRef()
20+
expectEqual(derived[1], 0)
21+
derived[1] = -11
22+
expectEqual(derived[1], -11)
23+
}
24+
25+
runAllTests()
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#pragma once
2+
3+
#define FRT \
4+
__attribute__((swift_attr("import_reference"))) \
5+
__attribute__((swift_attr("retain:immortal"))) \
6+
__attribute__((swift_attr("release:immortal")))
7+
8+
int &getCopyCounter() {
9+
static int copyCounter = 0;
10+
return copyCounter;
11+
}
12+
13+
class CopyTrackedBaseClass {
14+
public:
15+
CopyTrackedBaseClass(int x) : x(x), field(x + 1) {}
16+
CopyTrackedBaseClass(const CopyTrackedBaseClass &other) : x(other.x), field(other.field) {
17+
++getCopyCounter();
18+
}
19+
20+
int getX() const {
21+
return x;
22+
}
23+
24+
int field;
25+
26+
int operator[](int x) const {
27+
return x + field;
28+
}
29+
private:
30+
int x;
31+
} FRT;
32+
33+
class CopyTrackedDerivedClass: public CopyTrackedBaseClass {
34+
public:
35+
CopyTrackedDerivedClass(int x) : CopyTrackedBaseClass(x) {}
36+
37+
int getDerivedX() const {
38+
return getX();
39+
}
40+
} FRT;
41+
42+
CopyTrackedDerivedClass *makeCopyTrackedDerivedClass(int x) {
43+
return new CopyTrackedDerivedClass(x);
44+
}
45+
46+
class NonEmptyBase {
47+
public:
48+
int getY() const {
49+
return y;
50+
}
51+
private:
52+
int y = 11;
53+
} FRT;
54+
55+
class CopyTrackedDerivedDerivedClass: public NonEmptyBase, public CopyTrackedDerivedClass {
56+
public:
57+
CopyTrackedDerivedDerivedClass(int x) : CopyTrackedDerivedClass(x) {}
58+
} FRT;
59+
60+
CopyTrackedDerivedDerivedClass *makeCopyTrackedDerivedDerivedClass(int x) {
61+
return new CopyTrackedDerivedDerivedClass(x);
62+
}
63+
64+
class BaseReturningFRTFromSubscript {
65+
public:
66+
CopyTrackedDerivedClass &operator[](int x) const {
67+
return *new CopyTrackedDerivedClass(x);
68+
}
69+
} FRT;
70+
71+
BaseReturningFRTFromSubscript *makeBaseReturningFRTFromSubscript() {
72+
return new BaseReturningFRTFromSubscript();
73+
}
74+
75+
class DerivedFromBaseReturningFRTFromSubscript: public BaseReturningFRTFromSubscript {
76+
public:
77+
} FRT;
78+
79+
DerivedFromBaseReturningFRTFromSubscript *makeDerivedFromBaseReturningFRTFromSubscript() {
80+
return new DerivedFromBaseReturningFRTFromSubscript();
81+
}
82+
83+
class BaseReturningFRTFromSubscriptPointer {
84+
public:
85+
BaseReturningFRTFromSubscriptPointer(): value(new CopyTrackedDerivedClass(0)) {}
86+
87+
CopyTrackedDerivedClass *&operator[](int x) {
88+
return value;
89+
}
90+
91+
private:
92+
CopyTrackedDerivedClass *value;
93+
} FRT;
94+
95+
class DerivedFromBaseReturningFRTFromSubscriptPointer: public BaseReturningFRTFromSubscriptPointer {
96+
public:
97+
} FRT;
98+
99+
DerivedFromBaseReturningFRTFromSubscriptPointer *makeDerivedFromBaseReturningFRTFromSubscriptPointer() {
100+
return new DerivedFromBaseReturningFRTFromSubscriptPointer();
101+
}

test/Interop/Cxx/foreign-reference/Inputs/module.modulemap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ module MemberLayout {
3333
requires cplusplus
3434
}
3535

36-
module FunctionInheritance {
37-
header "function-inheritance.h"
36+
module MemberInheritance {
37+
header "member-inheritance.h"
3838
requires cplusplus
3939
}

test/Interop/Cxx/foreign-reference/function-inheritance-irgen.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// RUN: %target-swift-emit-irgen -I %S/Inputs -enable-experimental-cxx-interop %s -validate-tbd-against-ir=none -Xcc -fignore-exceptions | %FileCheck %s
22

3-
import FunctionInheritance
3+
import MemberInheritance
44

55
func testGetX() -> CInt {
66
let derivedDerived = makeCopyTrackedDerivedDerivedClass(42)!

0 commit comments

Comments
 (0)