Skip to content

Commit f7f9888

Browse files
committed
[builtin] Add a new SIL builtin convertUnownedUnsafeToGuaranteed()
(BaseT, @inout @unowned(unsafe) T) -> @guaranteed T The reason for the weird signature is that currently the Builtin infrastructure does not handle results well. Also, note that we are not actually performing a call here. We are SILGening directly so we can create a guaranteed result. The intended semantics is that one passes in a base value that guarantees the lifetime of the unowned(unsafe) value. The builtin then: 1. Borrows the base. 2. Loads the trivial unowned (unsafe), converts that value to a guaranteed ref after unsafely unwrapping the optional. 3. Uses mark dependence to tie the lifetimes of the guaranteed base to the guaranteed ref. I also updated my small UnsafeValue.swift test to make sure we get the codegen we expect.
1 parent 1137c00 commit f7f9888

File tree

8 files changed

+174
-3
lines changed

8 files changed

+174
-3
lines changed

include/swift/AST/Builtins.def

+19
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,25 @@ BUILTIN_SIL_OPERATION(ProjectTailElems, "projectTailElems", Special)
450450
/// signature is to work around issues with results in SILGen builtin emission.
451451
BUILTIN_SIL_OPERATION(ConvertStrongToUnownedUnsafe, "convertStrongToUnownedUnsafe", Special)
452452

453+
/// Unsafely convert a value of type @inout @unowned(unsafe) T to a loaded
454+
/// guaranteed T value that has a lifetime guaranteed by the passed in base
455+
/// value of type BaseTy.
456+
///
457+
/// It has type (@in_guaranteed BaseTy, @in_guaranteed @unowned (unsafe) T) -> @guaranteed T.
458+
///
459+
/// NOTE: Saying the result is a guaranteed T is a bit of a misnomer. We aren't
460+
/// emitting a builtin call, but are just emitting SIL directly.
461+
///
462+
/// NOTE: Even though the signature is as mentioned above, we actually tell the
463+
/// AST we have the signature:
464+
///
465+
/// <BaseT, T, U> (BaseT, T) -> U
466+
///
467+
/// We then perform the actual type checking in SILGen and assert on
468+
/// failure. This is an early, unsupported feature so this is sufficient for
469+
/// now.
470+
BUILTIN_SIL_OPERATION(ConvertUnownedUnsafeToGuaranteed, "convertUnownedUnsafeToGuaranteed", Special)
471+
453472
#undef BUILTIN_SIL_OPERATION
454473

455474
// BUILTIN_RUNTIME_CALL - A call into a runtime function.

lib/AST/Builtins.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,22 @@ static ValueDecl *getConvertStrongToUnownedUnsafe(ASTContext &ctx,
956956
return builder.build(id);
957957
}
958958

959+
static ValueDecl *getConvertUnownedUnsafeToGuaranteed(ASTContext &ctx,
960+
Identifier id) {
961+
// We actually want this:
962+
//
963+
/// <BaseT, T> (BaseT, @inout unowned(unsafe) T) -> T
964+
//
965+
// But for simplicity, we actually accept three generic params T, U and do the
966+
// checking in the emission method that everything works up. This is a
967+
// builtin, so we can crash.
968+
BuiltinFunctionBuilder builder(ctx, 3);
969+
builder.addParameter(makeGenericParam(0)); // Base
970+
builder.addParameter(makeGenericParam(1), ValueOwnership::InOut); // Unmanaged
971+
builder.setResult(makeGenericParam(2)); // Guaranteed Result
972+
return builder.build(id);
973+
}
974+
959975
static ValueDecl *getPoundAssert(ASTContext &Context, Identifier Id) {
960976
auto int1Type = BuiltinIntegerType::get(1, Context);
961977
auto optionalRawPointerType = BoundGenericEnumType::get(
@@ -2015,6 +2031,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
20152031
case BuiltinValueKind::ConvertStrongToUnownedUnsafe:
20162032
return getConvertStrongToUnownedUnsafe(Context, Id);
20172033

2034+
case BuiltinValueKind::ConvertUnownedUnsafeToGuaranteed:
2035+
return getConvertUnownedUnsafeToGuaranteed(Context, Id);
2036+
20182037
case BuiltinValueKind::PoundAssert:
20192038
return getPoundAssert(Context, Id);
20202039

lib/SILGen/SILGenBuilder.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -844,3 +844,12 @@ ManagedValue SILGenBuilder::createProjectBox(SILLocation loc, ManagedValue mv,
844844
auto *pbi = createProjectBox(loc, mv.getValue(), index);
845845
return ManagedValue::forUnmanaged(pbi);
846846
}
847+
848+
ManagedValue SILGenBuilder::createMarkDependence(SILLocation loc,
849+
ManagedValue value,
850+
ManagedValue base) {
851+
CleanupCloner cloner(*this, value);
852+
auto *mdi = createMarkDependence(loc, value.forward(getSILGenFunction()),
853+
base.forward(getSILGenFunction()));
854+
return cloner.clone(mdi);
855+
}

lib/SILGen/SILGenBuilder.h

+4
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,10 @@ class SILGenBuilder : public SILBuilder {
368368
using SILBuilder::createProjectBox;
369369
ManagedValue createProjectBox(SILLocation loc, ManagedValue mv,
370370
unsigned index);
371+
372+
using SILBuilder::createMarkDependence;
373+
ManagedValue createMarkDependence(SILLocation loc, ManagedValue value,
374+
ManagedValue base);
371375
};
372376

373377
} // namespace Lowering

lib/SILGen/SILGenBuiltin.cpp

+54-1
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,7 @@ static ManagedValue emitBuiltinConvertStrongToUnownedUnsafe(
10871087
if (objectSrcValue->getType() !=
10881088
destType.getReferenceStorageReferentType().getOptionalObjectType()) {
10891089
llvm::errs()
1090-
<< "Invalid usage of Builtin.convertStrongToUnsafeUnowned. lhsType "
1090+
<< "Invalid usage of Builtin.convertStrongToUnownedUnsafe. lhsType "
10911091
"must be T and rhsType must be inout unsafe(unowned) Optional<T>"
10921092
<< "lhsType: " << objectSrcValue->getType() << "\n"
10931093
<< "rhsType: " << inoutDest->getType() << "\n";
@@ -1109,6 +1109,59 @@ static ManagedValue emitBuiltinConvertStrongToUnownedUnsafe(
11091109
return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc));
11101110
}
11111111

1112+
/// Emit SIL for the named builtin: convertUnownedUnsafeToGuaranteed.
1113+
///
1114+
/// We assume our convention is:
1115+
///
1116+
/// <BaseT, T> (BaseT, @in_guaranteed @unmanaged Optional<T>) -> @guaranteed T
1117+
///
1118+
static ManagedValue emitBuiltinConvertUnownedUnsafeToGuaranteed(
1119+
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
1120+
PreparedArguments &&preparedArgs, SGFContext C) {
1121+
auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 2);
1122+
if (!argsOrError)
1123+
return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc));
1124+
1125+
auto args = *argsOrError;
1126+
1127+
// First grab our base and borrow it.
1128+
auto baseMV =
1129+
SGF.emitRValueAsSingleValue(args[0], SGFContext::AllowGuaranteedPlusZero)
1130+
.borrow(SGF, args[0]);
1131+
1132+
// Then grab our LValue operand, drop the last ownership component.
1133+
auto srcLV = SGF.emitLValue(args[1]->getSemanticsProvidingExpr(),
1134+
SGFAccessKind::BorrowedAddressRead);
1135+
srcLV.unsafelyDropLastComponent(PathComponent::OwnershipKind);
1136+
if (!srcLV.isPhysical() || !srcLV.isLoadingPure()) {
1137+
llvm::report_fatal_error("Builtin.convertUnownedUnsafeToGuaranteed passed "
1138+
"non-physical, non-pure lvalue as 2nd arg");
1139+
}
1140+
1141+
// Grab our address and load our unmanaged and convert it to a ref.
1142+
SILValue srcAddr =
1143+
SGF.emitAddressOfLValue(args[1], std::move(srcLV)).getLValueAddress();
1144+
SILValue srcValue = SGF.B.emitLoadValueOperation(
1145+
loc, srcAddr, LoadOwnershipQualifier::Trivial);
1146+
SILValue unownedNonTrivialRef = SGF.B.createUnmanagedToRef(
1147+
loc, srcValue, srcValue->getType().getReferenceStorageReferentType());
1148+
1149+
// Now convert our unownedNonTrivialRef from unowned ownership to guaranteed
1150+
// ownership and create a cleanup for it.
1151+
SILValue guaranteedNonTrivialRef = SGF.B.createUncheckedOwnershipConversion(
1152+
loc, unownedNonTrivialRef, ValueOwnershipKind::Guaranteed);
1153+
auto guaranteedNonTrivialRefMV =
1154+
SGF.emitManagedBorrowedRValueWithCleanup(guaranteedNonTrivialRef);
1155+
auto someDecl = SGF.getASTContext().getOptionalSomeDecl();
1156+
1157+
// Then unsafely extract from the optional.
1158+
auto extMV =
1159+
SGF.B.createUncheckedEnumData(loc, guaranteedNonTrivialRefMV, someDecl);
1160+
1161+
// Now create a mark dependence on our base and return the result.
1162+
return SGF.B.createMarkDependence(loc, extMV, baseMV);
1163+
}
1164+
11121165
Optional<SpecializedEmitter>
11131166
SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) {
11141167
// Only consider standalone declarations in the Builtin module.

lib/SILGen/SILGenExpr.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,30 @@ SILGenFunction::emitManagedBorrowedRValueWithCleanup(SILValue original,
297297
return emitManagedBorrowedRValueWithCleanup(original, borrowed, lowering);
298298
}
299299

300+
ManagedValue
301+
SILGenFunction::emitManagedBorrowedRValueWithCleanup(SILValue borrowed) {
302+
auto &lowering = getTypeLowering(borrowed->getType());
303+
return emitManagedBorrowedRValueWithCleanup(borrowed, lowering);
304+
}
305+
306+
ManagedValue SILGenFunction::emitManagedBorrowedRValueWithCleanup(
307+
SILValue borrowed, const TypeLowering &lowering) {
308+
assert(lowering.getLoweredType().getObjectType() ==
309+
borrowed->getType().getObjectType());
310+
if (lowering.isTrivial())
311+
return ManagedValue::forUnmanaged(borrowed);
312+
313+
if (borrowed->getType().isObject() &&
314+
borrowed.getOwnershipKind() == ValueOwnershipKind::None)
315+
return ManagedValue::forUnmanaged(borrowed);
316+
317+
if (borrowed->getType().isObject()) {
318+
Cleanups.pushCleanup<EndBorrowCleanup>(borrowed);
319+
}
320+
321+
return ManagedValue(borrowed, CleanupHandle::invalid());
322+
}
323+
300324
ManagedValue SILGenFunction::emitManagedBorrowedRValueWithCleanup(
301325
SILValue original, SILValue borrowed, const TypeLowering &lowering) {
302326
assert(lowering.getLoweredType().getObjectType() ==

lib/SILGen/SILGenFunction.h

+5
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
13381338
const TypeLowering &lowering);
13391339
ManagedValue emitManagedBeginBorrow(SILLocation loc, SILValue v);
13401340

1341+
ManagedValue
1342+
emitManagedBorrowedRValueWithCleanup(SILValue borrowedValue,
1343+
const TypeLowering &lowering);
1344+
ManagedValue emitManagedBorrowedRValueWithCleanup(SILValue borrowedValue);
1345+
13411346
ManagedValue emitManagedBorrowedRValueWithCleanup(SILValue original,
13421347
SILValue borrowedValue);
13431348
ManagedValue emitManagedBorrowedRValueWithCleanup(

test/SILGen/unsafevalue.swift

+40-2
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,48 @@ public struct UnsafeValue<Element: AnyObject> {
7878
}
7979

8080
// Access the underlying value at +0, guaranteeing its lifetime by base.
81+
//
82+
// CHECK-LABEL: sil [transparent] [serialized] [ossa] @$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF : $@convention(method) <Element where Element : AnyObject><Base, Result> (@in_guaranteed Base, @noescape @callee_guaranteed (@guaranteed Element) -> @out Result, UnsafeValue<Element>) -> @out Result {
83+
// CHECK: bb0([[RESULT:%.*]] : $*Result, [[BASE:%.*]] : $*Base, [[CLOSURE:%.*]] : $@noescape @callee_guaranteed (@guaranteed Element) -> @out Result, [[UNSAFE_VALUE:%.*]] : $UnsafeValue<Element>):
84+
// CHECK: [[COPY_BOX:%.*]] = alloc_box
85+
// CHECK: [[COPY_PROJ:%.*]] = project_box [[COPY_BOX]]
86+
// CHECK: store [[UNSAFE_VALUE]] to [trivial] [[COPY_PROJ]]
87+
// CHECK: [[VALUE_ADDR:%.*]] = begin_access [read] [unknown] [[COPY_PROJ]]
88+
// CHECK: [[STR_VALUE_ADDR:%.*]] = struct_element_addr [[VALUE_ADDR]]
89+
// CHECK: [[UNMANAGED_VALUE:%.*]] = load [trivial] [[STR_VALUE_ADDR]]
90+
// CHECK: [[UNOWNED_REF_OPTIONAL:%.*]] = unmanaged_to_ref [[UNMANAGED_VALUE]]
91+
// CHECK: [[GUARANTEED_REF_OPTIONAL:%.*]] = unchecked_ownership_conversion [[UNOWNED_REF_OPTIONAL]]
92+
// CHECK: [[GUARANTEED_REF:%.*]] = unchecked_enum_data [[GUARANTEED_REF_OPTIONAL]]
93+
// CHECK: [[GUARANTEED_REF_DEP_ON_BASE:%.*]] = mark_dependence [[GUARANTEED_REF]] : $Element on [[BASE]]
94+
// CHECK: end_access [[VALUE_ADDR]]
95+
// CHECK: apply [[CLOSURE]]([[RESULT]], [[GUARANTEED_REF_DEP_ON_BASE]])
96+
// CHECK: end_borrow [[GUARANTEED_REF_OPTIONAL]]
97+
// CHECK: destroy_value [[COPY_BOX]]
98+
// CHECK: } // end sil function '$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF'
99+
//
100+
// CANONICAL-LABEL: sil [transparent] [serialized] @$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF : $@convention(method) <Element where Element : AnyObject><Base, Result> (@in_guaranteed Base, @noescape @callee_guaranteed (@guaranteed Element) -> @out Result, UnsafeValue<Element>) -> @out Result {
101+
// CANONICAL: bb0([[RESULT:%.*]] : $*Result, [[BASE:%.*]] : $*Base, [[CLOSURE:%.*]] : $@noescape @callee_guaranteed (@guaranteed Element) -> @out Result, [[UNSAFE_VALUE:%.*]] : $UnsafeValue<Element>):
102+
// CANONICAL: [[UNMANAGED_VALUE:%.*]] = struct_extract [[UNSAFE_VALUE]]
103+
// CANONICAL: [[UNOWNED_REF_OPTIONAL:%.*]] = unmanaged_to_ref [[UNMANAGED_VALUE]]
104+
// CANONICAL: [[GUARANTEED_REF:%.*]] = unchecked_enum_data [[UNOWNED_REF_OPTIONAL]]
105+
// CANONICAL: [[GUARANTEED_REF_DEP_ON_BASE:%.*]] = mark_dependence [[GUARANTEED_REF]] : $Element on [[BASE]]
106+
// CANONICAL: apply [[CLOSURE]]([[RESULT]], [[GUARANTEED_REF_DEP_ON_BASE]])
107+
// CANONICAL: } // end sil function '$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF'
108+
//
109+
// OPT-LABEL: sil [transparent] @$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF : $@convention(method) <Element where Element : AnyObject><Base, Result> (@in_guaranteed Base, @noescape @callee_guaranteed (@guaranteed Element) -> @out Result, UnsafeValue<Element>) -> @out Result {
110+
// OPT: bb0([[RESULT:%.*]] : $*Result, [[BASE:%.*]] : $*Base, [[CLOSURE:%.*]] : $@noescape @callee_guaranteed (@guaranteed Element) -> @out Result, [[UNSAFE_VALUE:%.*]] : $UnsafeValue<Element>):
111+
// OPT: [[UNMANAGED_VALUE:%.*]] = struct_extract [[UNSAFE_VALUE]]
112+
// OPT: [[UNOWNED_REF_OPTIONAL:%.*]] = unmanaged_to_ref [[UNMANAGED_VALUE]]
113+
// OPT: [[GUARANTEED_REF:%.*]] = unchecked_enum_data [[UNOWNED_REF_OPTIONAL]]
114+
// OPT: [[GUARANTEED_REF_DEP_ON_BASE:%.*]] = mark_dependence [[GUARANTEED_REF]] : $Element on [[BASE]]
115+
// OPT: apply [[CLOSURE]]([[RESULT]], [[GUARANTEED_REF_DEP_ON_BASE]])
116+
// OPT: } // end sil function '$s11unsafevalue11UnsafeValueV20withGuaranteeingBase4base1fqd_0_qd___qd_0_xXEtr0_lF'
81117
@_transparent
82118
@inlinable
83-
func withGuaranteeingBase<Base>(base: Base, f: (Element) -> ()) {
84-
// TODO: Once we have a builtin for mark_dependence, fill this in.
119+
func withGuaranteeingBase<Base, Result>(base: Base, f: (Element) -> Result) -> Result {
120+
// Just so we can not mark self as mutating. This is just a bitwise copy.
121+
var tmp = self
122+
return f(Builtin.convertUnownedUnsafeToGuaranteed(base, &tmp._value))
85123
}
86124

87125
// If the unmanaged value was initialized with a copy, release the underlying value.

0 commit comments

Comments
 (0)