Skip to content

Commit 94cd804

Browse files
authored
Merge pull request swiftlang#29680 from gottesmm/pr-5cabc7ee4a3e006145b5d4716d59ba51ba0c547b
[builtin] Add a new SIL builtin convertStrongToUnsafeUnowned()
2 parents a174243 + f7f9888 commit 94cd804

File tree

11 files changed

+396
-2
lines changed

11 files changed

+396
-2
lines changed

include/swift/AST/Builtins.def

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,31 @@ BUILTIN_SIL_OPERATION(AllocWithTailElems, "allocWithTailElems", Special)
444444
/// Projects the first tail-allocated element of type E from a class C.
445445
BUILTIN_SIL_OPERATION(ProjectTailElems, "projectTailElems", Special)
446446

447+
/// Unsafely convert a value of type T to an unowned value.
448+
///
449+
/// It has type (T, @inout @unowned(unsafe) T) -> (). The reason for the weird
450+
/// signature is to work around issues with results in SILGen builtin emission.
451+
BUILTIN_SIL_OPERATION(ConvertStrongToUnownedUnsafe, "convertStrongToUnownedUnsafe", Special)
452+
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+
447472
#undef BUILTIN_SIL_OPERATION
448473

449474
// BUILTIN_RUNTIME_CALL - A call into a runtime function.
@@ -634,6 +659,12 @@ BUILTIN_MISC_OPERATION(PoundAssert, "poundAssert", "", Special)
634659

635660
// BUILTIN_MISC_OPERATION_WITH_SILGEN - Miscellaneous operations that are
636661
// specially emitted during SIL generation.
662+
//
663+
// The intention is that this is meant for builtins that need a named
664+
// builtin representation so one can create a builtin instruction in
665+
// SIL, but that also need special SILGen behavior. If an operation
666+
// just emits custom SIL and does not need to be able to form a
667+
// builtin instruction, please use BUILTIN_SIL_OPERATION.
637668
#ifndef BUILTIN_MISC_OPERATION_WITH_SILGEN
638669
#define BUILTIN_MISC_OPERATION_WITH_SILGEN(Id, Name, Attrs, Overload) \
639670
BUILTIN_MISC_OPERATION(Id, Name, Attrs, Overload)

include/swift/SIL/SILType.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,24 @@ class SILType {
459459
return SILType(getASTType().getReferenceStorageReferent(), getCategory());
460460
}
461461

462+
/// Return the reference ownership of this type if it is a reference storage
463+
/// type. Otherwse, return None.
464+
Optional<ReferenceOwnership> getReferenceStorageOwnership() const {
465+
auto type = getASTType()->getAs<ReferenceStorageType>();
466+
if (!type)
467+
return None;
468+
return type->getOwnership();
469+
}
470+
471+
/// Attempt to wrap the passed in type as a type with reference ownership \p
472+
/// ownership. For simplicity, we always return an address since reference
473+
/// storage types may not be loadable (e.x.: weak ownership).
474+
SILType getReferenceStorageType(const ASTContext &ctx,
475+
ReferenceOwnership ownership) const {
476+
auto *type = ReferenceStorageType::get(getASTType(), ownership, ctx);
477+
return SILType::getPrimitiveAddressType(type->getCanonicalType());
478+
}
479+
462480
/// Transform the function type SILType by replacing all of its interface
463481
/// generic args with the appropriate item from the substitution.
464482
///

lib/AST/Builtins.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,38 @@ static ValueDecl *getGlobalStringTablePointer(ASTContext &Context,
940940
return getBuiltinFunction(Id, {stringType}, Context.TheRawPointerType);
941941
}
942942

943+
static ValueDecl *getConvertStrongToUnownedUnsafe(ASTContext &ctx,
944+
Identifier id) {
945+
// We actually want this:
946+
//
947+
// (T, inout unowned (unsafe) T) -> ()
948+
//
949+
// But for simplicity, we actually accept T, U and do the checking
950+
// in the emission method that everything works up. This is a
951+
// builtin, so we can crash.
952+
BuiltinFunctionBuilder builder(ctx, 2);
953+
builder.addParameter(makeGenericParam(0));
954+
builder.addParameter(makeGenericParam(1), ValueOwnership::InOut);
955+
builder.setResult(makeConcrete(TupleType::getEmpty(ctx)));
956+
return builder.build(id);
957+
}
958+
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+
943975
static ValueDecl *getPoundAssert(ASTContext &Context, Identifier Id) {
944976
auto int1Type = BuiltinIntegerType::get(1, Context);
945977
auto optionalRawPointerType = BoundGenericEnumType::get(
@@ -1996,6 +2028,12 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
19962028
case BuiltinValueKind::GlobalStringTablePointer:
19972029
return getGlobalStringTablePointer(Context, Id);
19982030

2031+
case BuiltinValueKind::ConvertStrongToUnownedUnsafe:
2032+
return getConvertStrongToUnownedUnsafe(Context, Id);
2033+
2034+
case BuiltinValueKind::ConvertUnownedUnsafeToGuaranteed:
2035+
return getConvertUnownedUnsafeToGuaranteed(Context, Id);
2036+
19992037
case BuiltinValueKind::PoundAssert:
20002038
return getPoundAssert(Context, Id);
20012039

lib/SIL/ValueOwnership.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,6 @@ UNOWNED_OR_NONE_DEPENDING_ON_RESULT(ZeroInitializer)
547547
BuiltinInst *BI, StringRef Attr) { \
548548
llvm_unreachable("builtin should have been lowered in SILGen"); \
549549
}
550-
551550
#include "swift/AST/Builtins.def"
552551

553552
ValueOwnershipKind

lib/SILGen/LValue.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,16 @@ class LValue {
405405
assert(&component == Path.back().get());
406406
Path.pop_back();
407407
}
408-
408+
409+
/// Pop the last component off this LValue unsafely. Validates that the
410+
/// component is of kind \p kind as a sanity check.
411+
///
412+
/// Please be careful when using this!
413+
void unsafelyDropLastComponent(PathComponent::KindTy kind) & {
414+
assert(kind == Path.back()->getKind());
415+
Path.pop_back();
416+
}
417+
409418
/// Add a new component at the end of the access path of this lvalue.
410419
template <class T, class... As>
411420
void add(As &&... args) {

lib/SILGen/SILGenBuilder.cpp

Lines changed: 9 additions & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,123 @@ emitBuiltinGlobalStringTablePointer(SILGenFunction &SGF, SILLocation loc,
10451045
return SGF.emitManagedRValueWithCleanup(resultVal);
10461046
}
10471047

1048+
/// Emit SIL for the named builtin:
1049+
/// convertStrongToUnownedUnsafe. Unlike the default ownership
1050+
/// convention for named builtins, which is to take (non-trivial)
1051+
/// arguments as Owned, this builtin accepts owned as well as
1052+
/// guaranteed arguments, and hence doesn't require the arguments to
1053+
/// be at +1. Therefore, this builtin is emitted specially.
1054+
///
1055+
/// We assume our convention is (T, @inout @unmanaged Optional<T>) -> ()
1056+
static ManagedValue emitBuiltinConvertStrongToUnownedUnsafe(
1057+
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
1058+
PreparedArguments &&preparedArgs, SGFContext C) {
1059+
auto argsOrError = decomposeArguments(SGF, loc, std::move(preparedArgs), 2);
1060+
if (!argsOrError)
1061+
return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc));
1062+
1063+
auto args = *argsOrError;
1064+
1065+
// First get our object at +0 if we can.
1066+
auto object = SGF.emitRValue(args[0], SGFContext::AllowGuaranteedPlusZero)
1067+
.getAsSingleValue(SGF, args[0]);
1068+
1069+
// Borrow it and get the value.
1070+
SILValue objectSrcValue = object.borrow(SGF, loc).getValue();
1071+
1072+
// Then create our inout.
1073+
auto inout = cast<InOutExpr>(args[1]->getSemanticsProvidingExpr());
1074+
auto lv =
1075+
SGF.emitLValue(inout->getSubExpr(), SGFAccessKind::BorrowedAddressRead);
1076+
lv.unsafelyDropLastComponent(PathComponent::OwnershipKind);
1077+
if (!lv.isPhysical() || !lv.isLoadingPure()) {
1078+
llvm::report_fatal_error("Builtin.convertStrongToUnownedUnsafe passed "
1079+
"non-physical, non-pure lvalue as 2nd arg");
1080+
}
1081+
1082+
SILValue inoutDest =
1083+
SGF.emitAddressOfLValue(args[1], std::move(lv)).getLValueAddress();
1084+
SILType destType = inoutDest->getType().getObjectType();
1085+
1086+
// Make sure our types match up as we expect.
1087+
if (objectSrcValue->getType() !=
1088+
destType.getReferenceStorageReferentType().getOptionalObjectType()) {
1089+
llvm::errs()
1090+
<< "Invalid usage of Builtin.convertStrongToUnownedUnsafe. lhsType "
1091+
"must be T and rhsType must be inout unsafe(unowned) Optional<T>"
1092+
<< "lhsType: " << objectSrcValue->getType() << "\n"
1093+
<< "rhsType: " << inoutDest->getType() << "\n";
1094+
llvm::report_fatal_error("standard fatal error msg");
1095+
}
1096+
1097+
// Ok. We have the right types. First convert objectSrcValue to its
1098+
// unowned representation.
1099+
SILType optionalType = SILType::getOptionalType(objectSrcValue->getType());
1100+
SILValue someVal =
1101+
SGF.B.createOptionalSome(loc, objectSrcValue, optionalType);
1102+
1103+
SILType unmanagedOptType = someVal->getType().getReferenceStorageType(
1104+
SGF.getASTContext(), ReferenceOwnership::Unmanaged);
1105+
SILValue unownedObjectSrcValue = SGF.B.createRefToUnmanaged(
1106+
loc, someVal, unmanagedOptType.getObjectType());
1107+
SGF.B.emitStoreValueOperation(loc, unownedObjectSrcValue, inoutDest,
1108+
StoreOwnershipQualifier::Trivial);
1109+
return ManagedValue::forUnmanaged(SGF.emitEmptyTuple(loc));
1110+
}
1111+
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+
10481165
Optional<SpecializedEmitter>
10491166
SpecializedEmitter::forDecl(SILGenModule &SGM, SILDeclRef function) {
10501167
// Only consider standalone declarations in the Builtin module.

lib/SILGen/SILGenExpr.cpp

Lines changed: 24 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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(

0 commit comments

Comments
 (0)