Skip to content

Commit 76fb0ee

Browse files
authored
Merge pull request #73515 from apple/egorzhdan/6.0-frt-release-nullptr
🍒[cxx-interop][IRGen] Do not try to retain/release a null pointer
2 parents 51cdcc6 + 527d3db commit 76fb0ee

File tree

8 files changed

+150
-31
lines changed

8 files changed

+150
-31
lines changed

lib/IRGen/ClassTypeInfo.h

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -99,38 +99,56 @@ class ClassTypeInfo : public HeapTypeInfo<ClassTypeInfo> {
9999
HeapTypeInfo::emitScalarRetain(IGF, value, atomicity);
100100
}
101101

102+
void strongCustomRetain(IRGenFunction &IGF, Explosion &e,
103+
bool needsNullCheck) const {
104+
assert(getReferenceCounting() == ReferenceCounting::Custom &&
105+
"only supported for custom ref-counting");
106+
107+
llvm::Value *value = e.claimNext();
108+
auto retainFn =
109+
evaluateOrDefault(
110+
getClass()->getASTContext().evaluator,
111+
CustomRefCountingOperation(
112+
{getClass(), CustomRefCountingOperationKind::retain}),
113+
{})
114+
.operation;
115+
IGF.emitForeignReferenceTypeLifetimeOperation(retainFn, value,
116+
needsNullCheck);
117+
}
118+
102119
// Implement the primary retain/release operations of ReferenceTypeInfo
103120
// using basic reference counting.
104121
void strongRetain(IRGenFunction &IGF, Explosion &e,
105122
Atomicity atomicity) const override {
106123
if (getReferenceCounting() == ReferenceCounting::Custom) {
107-
llvm::Value *value = e.claimNext();
108-
auto retainFn =
109-
evaluateOrDefault(
110-
getClass()->getASTContext().evaluator,
111-
CustomRefCountingOperation(
112-
{getClass(), CustomRefCountingOperationKind::retain}),
113-
{})
114-
.operation;
115-
IGF.emitForeignReferenceTypeLifetimeOperation(retainFn, value);
124+
strongCustomRetain(IGF, e, /*needsNullCheck*/ false);
116125
return;
117126
}
118127

119128
HeapTypeInfo::strongRetain(IGF, e, atomicity);
120129
}
121130

131+
void strongCustomRelease(IRGenFunction &IGF, Explosion &e,
132+
bool needsNullCheck) const {
133+
assert(getReferenceCounting() == ReferenceCounting::Custom &&
134+
"only supported for custom ref-counting");
135+
136+
llvm::Value *value = e.claimNext();
137+
auto releaseFn =
138+
evaluateOrDefault(
139+
getClass()->getASTContext().evaluator,
140+
CustomRefCountingOperation(
141+
{getClass(), CustomRefCountingOperationKind::release}),
142+
{})
143+
.operation;
144+
IGF.emitForeignReferenceTypeLifetimeOperation(releaseFn, value,
145+
needsNullCheck);
146+
}
147+
122148
void strongRelease(IRGenFunction &IGF, Explosion &e,
123149
Atomicity atomicity) const override {
124150
if (getReferenceCounting() == ReferenceCounting::Custom) {
125-
llvm::Value *value = e.claimNext();
126-
auto releaseFn =
127-
evaluateOrDefault(
128-
getClass()->getASTContext().evaluator,
129-
CustomRefCountingOperation(
130-
{getClass(), CustomRefCountingOperationKind::release}),
131-
{})
132-
.operation;
133-
IGF.emitForeignReferenceTypeLifetimeOperation(releaseFn, value);
151+
strongCustomRelease(IGF, e, /*needsNullCheck*/ false);
134152
return;
135153
}
136154

lib/IRGen/GenEnum.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,7 +2666,8 @@ namespace {
26662666
if (Refcounting == ReferenceCounting::Custom) {
26672667
Explosion e;
26682668
e.add(ptr);
2669-
getPayloadTypeInfo().as<ClassTypeInfo>().strongRetain(IGF, e, IGF.getDefaultAtomicity());
2669+
getPayloadTypeInfo().as<ClassTypeInfo>().strongCustomRetain(
2670+
IGF, e, /*needsNullCheck*/ true);
26702671
return;
26712672
}
26722673

@@ -2702,7 +2703,8 @@ namespace {
27022703
if (Refcounting == ReferenceCounting::Custom) {
27032704
Explosion e;
27042705
e.add(ptr);
2705-
getPayloadTypeInfo().as<ClassTypeInfo>().strongRelease(IGF, e, IGF.getDefaultAtomicity());
2706+
getPayloadTypeInfo().as<ClassTypeInfo>().strongCustomRelease(
2707+
IGF, e, /*needsNullCheck*/ true);
27062708
return;
27072709
}
27082710

lib/IRGen/GenObjC.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,7 +1712,7 @@ void IRGenFunction::emitBlockRelease(llvm::Value *value) {
17121712
}
17131713

17141714
void IRGenFunction::emitForeignReferenceTypeLifetimeOperation(
1715-
ValueDecl *fn, llvm::Value *value) {
1715+
ValueDecl *fn, llvm::Value *value, bool needsNullCheck) {
17161716
assert(fn->getClangDecl() && isa<clang::FunctionDecl>(fn->getClangDecl()));
17171717

17181718
auto clangFn = cast<clang::FunctionDecl>(fn->getClangDecl());
@@ -1723,6 +1723,28 @@ void IRGenFunction::emitForeignReferenceTypeLifetimeOperation(
17231723
cast<llvm::FunctionType>(llvmFn->getFunctionType())->getParamType(0);
17241724
value = Builder.CreateBitCast(value, argType);
17251725

1726-
auto call = Builder.CreateCall(llvmFn->getFunctionType(), llvmFn, value);
1726+
llvm::CallInst *call = nullptr;
1727+
if (needsNullCheck) {
1728+
// Check if the pointer is null.
1729+
auto nullValue = llvm::Constant::getNullValue(argType);
1730+
auto hasValue = Builder.CreateICmpNE(value, nullValue);
1731+
1732+
auto nonNullValueBB = createBasicBlock("lifetime.nonnull-value");
1733+
auto contBB = createBasicBlock("lifetime.cont");
1734+
1735+
// If null, just continue.
1736+
Builder.CreateCondBr(hasValue, nonNullValueBB, contBB);
1737+
1738+
// If non-null, emit a call to release/retain function.
1739+
Builder.emitBlock(nonNullValueBB);
1740+
call = Builder.CreateCall(llvmFn->getFunctionType(), llvmFn, value);
1741+
1742+
Builder.CreateBr(contBB);
1743+
1744+
Builder.emitBlock(contBB);
1745+
} else {
1746+
call = Builder.CreateCall(llvmFn->getFunctionType(), llvmFn, value);
1747+
}
1748+
17271749
call->setDoesNotThrow();
17281750
}

lib/IRGen/IRGenFunction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,8 @@ class IRGenFunction {
561561
void emitBlockRelease(llvm::Value *value);
562562

563563
void emitForeignReferenceTypeLifetimeOperation(ValueDecl *fn,
564-
llvm::Value *value);
564+
llvm::Value *value,
565+
bool needsNullCheck = false);
565566

566567
// Routines for an unknown reference-counting style (meaning,
567568
// dynamically something compatible with either the ObjC or Swift styles).

test/Interop/Cxx/foreign-reference/Inputs/reference-counted.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ __attribute__((swift_attr("release:LCRelease"))) LocalCount {
2727

2828
}
2929

30-
inline void LCRetain(NS::LocalCount *x) { x->value++; }
30+
inline void LCRetain(NS::LocalCount *x) {
31+
x->value++;
32+
finalLocalRefCount = x->value;
33+
}
3134
inline void LCRelease(NS::LocalCount *x) {
3235
x->value--;
3336
finalLocalRefCount = x->value;
@@ -46,6 +49,21 @@ __attribute__((swift_attr("release:GCRelease"))) GlobalCount {
4649
inline void GCRetain(GlobalCount *x) { globalCount++; }
4750
inline void GCRelease(GlobalCount *x) { globalCount--; }
4851

52+
struct __attribute__((swift_attr("import_as_ref")))
53+
__attribute__((swift_attr("retain:GCRetainNullableInit")))
54+
__attribute__((swift_attr("release:GCReleaseNullableInit")))
55+
GlobalCountNullableInit {
56+
static GlobalCountNullableInit *_Nullable create(bool wantNullptr) {
57+
if (wantNullptr)
58+
return nullptr;
59+
return new (malloc(sizeof(GlobalCountNullableInit)))
60+
GlobalCountNullableInit();
61+
}
62+
};
63+
64+
inline void GCRetainNullableInit(GlobalCountNullableInit *x) { globalCount++; }
65+
inline void GCReleaseNullableInit(GlobalCountNullableInit *x) { globalCount--; }
66+
4967
SWIFT_END_NULLABILITY_ANNOTATIONS
5068

5169
#endif // TEST_INTEROP_CXX_FOREIGN_REFERENCE_INPUTS_REFERENCE_COUNTED_H
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %target-swift-emit-irgen %s -I %S/Inputs -cxx-interoperability-mode=default -Xcc -fignore-exceptions -disable-availability-checking | %FileCheck %s
2+
// XFAIL: OS=linux-android, OS=linux-androideabi
3+
4+
import ReferenceCounted
5+
6+
7+
public func getLocalCount() -> NS.LocalCount {
8+
let result = NS.LocalCount.create()
9+
return result
10+
}
11+
12+
// CHECK: define {{.*}}swiftcc ptr @"$s4main13getLocalCountSo2NSO0cD0VyF"()
13+
// CHECK-NEXT: entry:
14+
// CHECK: %0 = call ptr @{{_ZN2NS10LocalCount6createEv|"\?create\@LocalCount\@NS\@\@SAPEAU12\@XZ"}}()
15+
// CHECK-NEXT: call void @{{_Z8LCRetainPN2NS10LocalCountE|"\?LCRetain\@\@YAXPEAULocalCount\@NS\@\@\@Z"}}(ptr %0)
16+
// CHECK: ret ptr %0
17+
// CHECK-NEXT: }
18+
19+
20+
public func get42() -> Int32 {
21+
let result = NS.LocalCount.create()
22+
return result.returns42()
23+
}
24+
25+
// CHECK: define {{.*}}swiftcc i32 @"$s4main5get42s5Int32VyF"()
26+
// CHECK-NEXT: entry:
27+
// CHECK: %0 = call ptr @{{_ZN2NS10LocalCount6createEv|"\?create\@LocalCount\@NS\@\@SAPEAU12\@XZ"}}()
28+
// CHECK-NEXT: call void @{{_Z8LCRetainPN2NS10LocalCountE|"\?LCRetain\@\@YAXPEAULocalCount\@NS\@\@\@Z"}}(ptr %0)
29+
// CHECK: %1 = call i32 @{{_ZN2NS10LocalCount9returns42Ev|"\?returns42\@LocalCount\@NS\@\@QEAAHXZ"}}
30+
// CHECK: ret i32 %1
31+
// CHECK-NEXT: }
32+
33+
34+
public func getNullable(wantNullptr: Bool) -> GlobalCountNullableInit? {
35+
let result = GlobalCountNullableInit.create(wantNullptr)
36+
return result
37+
}
38+
39+
// CHECK: define {{.*}}swiftcc i{{.*}} @"$s4main11getNullable11wantNullptrSo011GlobalCountC4InitVSgSb_tF"(i1 %0)
40+
// CHECK-NEXT: entry:
41+
// CHECK: %1 = call ptr @{{_ZN23GlobalCountNullableInit6createEb|"\?create\@GlobalCountNullableInit\@\@SAPEAU1\@_N\@Z"}}
42+
// CHECK-NEXT: %2 = ptrtoint ptr %1 to i64
43+
// CHECK-NEXT: %3 = inttoptr i64 %2 to ptr
44+
// CHECK-NEXT: %4 = icmp ne ptr %3, null
45+
// CHECK-NEXT: br i1 %4, label %lifetime.nonnull-value, label %lifetime.cont
46+
47+
// CHECK: lifetime.nonnull-value:
48+
// CHECK-NEXT: call void @{{_Z20GCRetainNullableInitP23GlobalCountNullableInit|"\?GCRetainNullableInit\@\@YAXPEAUGlobalCountNullableInit\@\@\@Z"}}(ptr %3)
49+
// CHECK-NEXT: br label %lifetime.cont
50+
51+
// CHECK: lifetime.cont:
52+
// CHECK: ret i64 %2
53+
// CHECK-NEXT: }

test/Interop/Cxx/foreign-reference/reference-counted.swift

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify)
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify -Xfrontend -disable-availability-checking -Onone -D NO_OPTIMIZATIONS)
2+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify -Xfrontend -disable-availability-checking -O)
23
//
34
// REQUIRES: executable_test
45
// TODO: This should work without ObjC interop in the future rdar://97497120
56
// REQUIRES: objc_interop
67

7-
// REQUIRES: rdar97532642
8-
98
import StdlibUnittest
109
import ReferenceCounted
1110

@@ -17,13 +16,17 @@ public func blackHole<T>(_ _: T) { }
1716
@inline(never)
1817
func localTest() {
1918
var x = NS.LocalCount.create()
20-
expectEqual(x.value, 6) // This is 6 because of "var x" "x.value" * 2 and "(x, x, x)".
19+
#if NO_OPTIMIZATIONS
20+
expectEqual(x.value, 8) // This is 8 because of "var x" "x.value" * 2, two method calls on x, and "(x, x, x)".
21+
#endif
2122

2223
expectEqual(x.returns42(), 42)
2324
expectEqual(x.constMethod(), 42)
2425

2526
let t = (x, x, x)
27+
#if NO_OPTIMIZATIONS
2628
expectEqual(x.value, 5)
29+
#endif
2730
}
2831

2932
ReferenceCountedTestSuite.test("Local") {
@@ -35,7 +38,6 @@ ReferenceCountedTestSuite.test("Local") {
3538
var globalOptional: NS.LocalCount? = nil
3639

3740
ReferenceCountedTestSuite.test("Global optional holding local ref count") {
38-
expectEqual(finalLocalRefCount, 0)
3941
globalOptional = NS.LocalCount.create()
4042
expectEqual(finalLocalRefCount, 1)
4143
}
@@ -44,14 +46,18 @@ ReferenceCountedTestSuite.test("Global optional holding local ref count") {
4446
func globalTest1() {
4547
var x = GlobalCount.create()
4648
let t = (x, x, x)
49+
#if NO_OPTIMIZATIONS
4750
expectEqual(globalCount, 4)
51+
#endif
4852
blackHole(t)
4953
}
5054

5155
@inline(never)
5256
func globalTest2() {
5357
var x = GlobalCount.create()
58+
#if NO_OPTIMIZATIONS
5459
expectEqual(globalCount, 1)
60+
#endif
5561
}
5662

5763
ReferenceCountedTestSuite.test("Global") {

test/Interop/Cxx/foreign-reference/witness-table.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify -g)
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-experimental-cxx-interop -Xfrontend -validate-tbd-against-ir=none -Xfrontend -disable-llvm-verify -Xfrontend -disable-availability-checking -g)
22
//
33
// REQUIRES: executable_test
4-
// REQUIRES: rdar95738946
54
// XFAIL: OS=windows-msvc
65

76
import StdlibUnittest

0 commit comments

Comments
 (0)