Skip to content

Commit

Permalink
SILGen: Drop '_unwrapped' intrinsics and use _diagnoseUnexpectedNilOp…
Browse files Browse the repository at this point in the history
…tional.

Being generic, the '_unwrapped' intrinsics force trafficking through memory, and while they're transparent so always get inlined, we don't do memory promotion in -Onone. Emitting the branch inline lets loadable optionals stay values leading to better -Onone codegen. (It also lets us throw away a surprising amount of support code for these optional intrinsics.)
  • Loading branch information
jckarter committed Jun 8, 2016
1 parent e72af82 commit c9ba2bf
Show file tree
Hide file tree
Showing 18 changed files with 51 additions and 236 deletions.
5 changes: 0 additions & 5 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,6 @@ class ASTContext {
FuncDecl *get##Name(LazyResolver *resolver) const;
#include "swift/AST/KnownDecls.def"

/// Retrieve the declaration of
/// Swift._stdlib_{,ImplicitlyUnwrapped}Optional_unwrapped.
FuncDecl *getOptionalUnwrappedDecl(LazyResolver *resolver,
OptionalTypeKind kind) const;

/// Check whether the standard library provides all the correct
/// intrinsic support for Optional<T>.
///
Expand Down
84 changes: 1 addition & 83 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,6 @@ struct ASTContext::Implementation {
#define FUNC_DECL(Name, Id) FuncDecl *Get##Name = nullptr;
#include "swift/AST/KnownDecls.def"

/// func _stdlib_Optional_unwrapped<T>(v: Optional<T>) -> T
FuncDecl *OptionalUnwrappedDecls[NumOptionalTypeKinds] = {};

/// The declaration of Swift.ImplicitlyUnwrappedOptional<T>.
EnumDecl *ImplicitlyUnwrappedOptionalDecl = nullptr;

Expand Down Expand Up @@ -1055,29 +1052,6 @@ FuncDecl *ASTContext::getIsOSVersionAtLeastDecl(LazyResolver *resolver) const {
return decl;
}

/// Check whether the given function is generic over a single,
/// unconstrained archetype.
static bool isGenericIntrinsic(FuncDecl *fn, CanType &input, CanType &output,
CanType &param) {
auto fnType =
dyn_cast<GenericFunctionType>(fn->getInterfaceType()->getCanonicalType());
if (!fnType || fnType->getGenericParams().size() != 1)
return false;

bool hasRequirements = std::any_of(fnType->getRequirements().begin(),
fnType->getRequirements().end(),
[](const Requirement &req) -> bool {
return req.getKind() != RequirementKind::WitnessMarker;
});
if (hasRequirements)
return false;

param = CanGenericTypeParamType(fnType->getGenericParams().front());
input = stripImmediateLabels(fnType.getInput());
output = stripImmediateLabels(fnType.getResult());
return true;
}

// Find library intrinsic function.
static FuncDecl *findLibraryFunction(const ASTContext &ctx, FuncDecl *&cache,
StringRef name, LazyResolver *resolver) {
Expand All @@ -1094,67 +1068,11 @@ FuncDecl *ASTContext::get##Name(LazyResolver *resolver) const { \
}
#include "swift/AST/KnownDecls.def"

/// Check whether the given type is Optional applied to the given
/// type argument.
static bool isOptionalType(const ASTContext &ctx,
OptionalTypeKind optionalKind,
CanType type, CanType arg) {
if (auto boundType = dyn_cast<BoundGenericType>(type)) {
return (boundType->getDecl()->classifyAsOptionalType() == optionalKind &&
boundType.getGenericArgs().size() == 1 &&
boundType.getGenericArgs()[0] == arg);
}
return false;
}

/// Turn an OptionalTypeKind into an index into one of the caches.
static unsigned asIndex(OptionalTypeKind optionalKind) {
assert(optionalKind && "passed a non-optional type kind?");
return unsigned(optionalKind) - 1;
}

#define getOptionalIntrinsicName(PREFIX, KIND, SUFFIX) \
((KIND) == OTK_Optional \
? (PREFIX "Optional" SUFFIX) \
: (PREFIX "ImplicitlyUnwrappedOptional" SUFFIX))

FuncDecl *ASTContext::getOptionalUnwrappedDecl(LazyResolver *resolver,
OptionalTypeKind optionalKind) const {
auto &cache = Impl.OptionalUnwrappedDecls[asIndex(optionalKind)];
if (cache) return cache;

auto name = getOptionalIntrinsicName(
"_stdlib_", optionalKind, "_unwrapped");

// Look for the function.
CanType input, output, param;
auto decl = findLibraryIntrinsic(*this, name, resolver);
if (!decl || !isGenericIntrinsic(decl, input, output, param))
return nullptr;

// Input must be Optional<T>.
if (!isOptionalType(*this, optionalKind, input, param))
return nullptr;

// Output must be T.
if (output != param)
return nullptr;

cache = decl;
return decl;
}

static bool hasOptionalIntrinsics(const ASTContext &ctx, LazyResolver *resolver,
OptionalTypeKind optionalKind) {
return ctx.getOptionalUnwrappedDecl(resolver, optionalKind);
}

bool ASTContext::hasOptionalIntrinsics(LazyResolver *resolver) const {
return getOptionalDecl() &&
getOptionalSomeDecl() &&
getOptionalNoneDecl() &&
::hasOptionalIntrinsics(*this, resolver, OTK_Optional) &&
::hasOptionalIntrinsics(*this, resolver, OTK_ImplicitlyUnwrappedOptional);
getDiagnoseUnexpectedNilOptional(resolver);
}

bool ASTContext::hasPointerArgumentIntrinsics(LazyResolver *resolver) const {
Expand Down
54 changes: 13 additions & 41 deletions lib/SILGen/SILGenConvert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,6 @@ getOptionalSomeValue(SILLocation loc, ManagedValue value,
return emitManagedRValueWithCleanup(result, optTL);
}

static Substitution getSimpleSubstitution(GenericSignature *genericSig,
CanType typeArg) {
assert(genericSig->getGenericParams().size() == 1);
return Substitution{typeArg, {}};
}

/// Create the correct substitution for calling the given function at
/// the given type.
static Substitution getSimpleSubstitution(FuncDecl *fn, CanType typeArg) {
auto genericFnType =
cast<GenericFunctionType>(fn->getInterfaceType()->getCanonicalType());
return getSimpleSubstitution(genericFnType->getGenericSignature(), typeArg);
}

static CanType getOptionalValueType(SILType optType,
OptionalTypeKind &optionalKind) {
auto generic = cast<BoundGenericType>(optType.getSwiftRValueType());
Expand All @@ -172,19 +158,24 @@ static CanType getOptionalValueType(SILType optType,
}

void SILGenFunction::emitPreconditionOptionalHasValue(SILLocation loc,
SILValue addr) {
SILValue optional) {
OptionalTypeKind OTK;
getOptionalValueType(addr->getType().getObjectType(), OTK);
getOptionalValueType(optional->getType().getObjectType(), OTK);

// Generate code to the optional is present, and if not abort with a message
// Generate code to the optional is present, and if not, abort with a message
// (provided by the stdlib).
SILBasicBlock *contBB = createBasicBlock();
SILBasicBlock *failBB = createBasicBlock();

auto NoneEnumElementDecl = getASTContext().getOptionalNoneDecl(OTK);
B.createSwitchEnumAddr(loc, addr, /*defaultDest*/contBB,
{ { NoneEnumElementDecl, failBB }});

if (optional->getType().isAddress()) {
B.createSwitchEnumAddr(loc, optional, /*defaultDest*/contBB,
{ { NoneEnumElementDecl, failBB }});
} else {
B.createSwitchEnum(loc, optional, /*defaultDest*/contBB,
{ { NoneEnumElementDecl, failBB }});

}
B.emitBlock(failBB);

// Call the standard library implementation of _diagnoseUnexpectedNilOptional.
Expand Down Expand Up @@ -221,27 +212,8 @@ ManagedValue SILGenFunction::emitCheckedGetOptionalValueFrom(SILLocation loc,
ManagedValue src,
const TypeLowering &optTL,
SGFContext C) {
SILType optType = src.getType().getObjectType();
OptionalTypeKind optionalKind;
CanType valueType = getOptionalValueType(optType, optionalKind);

FuncDecl *fn = getASTContext().getOptionalUnwrappedDecl(
nullptr, optionalKind);
Substitution sub = getSimpleSubstitution(fn, valueType);

// The intrinsic takes its parameter indirectly.
if (src.getType().isObject()) {
auto buf = emitTemporaryAllocation(loc, src.getType());
B.createStore(loc, src.forward(*this), buf);
src = emitManagedBufferWithCleanup(buf);
}

RValue result = emitApplyOfLibraryIntrinsic(loc, fn, sub, src, C);
if (result) {
return std::move(result).getAsSingleValue(*this, loc);
} else {
return ManagedValue::forInContext();
}
emitPreconditionOptionalHasValue(loc, src.getValue());
return emitUncheckedGetOptionalValueFrom(loc, src, optTL, C);
}

ManagedValue SILGenFunction::emitUncheckedGetOptionalValueFrom(SILLocation loc,
Expand Down
9 changes: 3 additions & 6 deletions lib/SILGen/SILGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3119,14 +3119,11 @@ RValue RValueEmitter::emitForceValue(SILLocation loc, Expr *E,
return SGF.emitRValue(injection->getSubExpr(), C);
}

// Otherwise, emit the value into memory and use the optional intrinsic.
// Otherwise, emit the optional and force its value out.
const TypeLowering &optTL = SGF.getTypeLowering(E->getType());
auto optTemp = SGF.emitTemporary(E, optTL);
SGF.emitExprInto(E, optTemp.get());

ManagedValue opt = SGF.emitRValueAsSingleValue(E);
ManagedValue V =
SGF.emitCheckedGetOptionalValueFrom(loc,
optTemp->getManagedAddress(), optTL, C);
SGF.emitCheckedGetOptionalValueFrom(loc, opt, optTL, C);
return RValue(SGF, loc, valueType->getCanonicalType(), V);
}

Expand Down
14 changes: 0 additions & 14 deletions stdlib/public/core/ImplicitlyUnwrappedOptional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,6 @@ extension ImplicitlyUnwrappedOptional : CustomDebugStringConvertible {
}
}

@_transparent
public // COMPILER_INTRINSIC
func _stdlib_ImplicitlyUnwrappedOptional_unwrapped<Wrapped>
(_ `self`: Wrapped!) -> Wrapped {

switch `self` {
case .some(let wrapped):
return wrapped
case .none:
_preconditionFailure(
"unexpectedly found nil while unwrapping an Optional value")
}
}

#if _runtime(_ObjC)
extension ImplicitlyUnwrappedOptional : _ObjectiveCBridgeable {
public func _bridgeToObjectiveC() -> AnyObject {
Expand Down
12 changes: 0 additions & 12 deletions stdlib/public/core/Optional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -284,18 +284,6 @@ extension Optional : CustomReflectable {
}
}

@_transparent
public // COMPILER_INTRINSIC
func _stdlib_Optional_unwrapped<Wrapped>(_ `self`: Wrapped?) -> Wrapped {
switch `self` {
case let wrapped?:
return wrapped
case .none:
_preconditionFailure(
"unexpectedly found nil while unwrapping an Optional value")
}
}

@_transparent
public // COMPILER_INTRINSIC
func _diagnoseUnexpectedNilOptional() {
Expand Down
2 changes: 0 additions & 2 deletions test/SILGen/dynamic_lookup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ func opt_to_property(_ obj: AnyObject) {
// CHECK: store [[OBJ]] to [[PBOBJ]] : $*AnyObject
// CHECK-NEXT: [[INT_BOX:%[0-9]+]] = alloc_box $Int
// CHECK-NEXT: project_box [[INT_BOX]]
// CHECK-NEXT: [[UNKNOWN_USE:%.*]] = alloc_stack $ImplicitlyUnwrappedOptional<Int>
// CHECK-NEXT: [[OBJ:%[0-9]+]] = load [[PBOBJ]] : $*AnyObject
// CHECK-NEXT: strong_retain [[OBJ]] : $AnyObject
// CHECK-NEXT: [[RAWOBJ_SELF:%[0-9]+]] = open_existential_ref [[OBJ]] : $AnyObject
Expand Down Expand Up @@ -162,7 +161,6 @@ func direct_to_subscript(_ obj: AnyObject, i: Int) {
// CHECK-NEXT: store [[I]] to [[PBI]] : $*Int
// CHECK-NEXT: alloc_box $Int
// CHECK-NEXT: project_box
// CHECK-NEXT: [[UNKNOWN_USE:%.*]] = alloc_stack $ImplicitlyUnwrappedOptional<Int>
// CHECK-NEXT: [[OBJ:%[0-9]+]] = load [[PBOBJ]] : $*AnyObject
// CHECK-NEXT: strong_retain [[OBJ]] : $AnyObject
// CHECK-NEXT: [[OBJ_REF:%[0-9]+]] = open_existential_ref [[OBJ]] : $AnyObject to $@opened({{.*}}) AnyObject
Expand Down
7 changes: 5 additions & 2 deletions test/SILGen/expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -541,10 +541,13 @@ func dontEmitIgnoredLoadExpr(_ a : NonTrivialStruct) -> NonTrivialStruct.Type {
// CHECK: bb0(%0 : $Optional<((Int, Int), Int)>):
func implodeRecursiveTuple(_ expr: ((Int, Int), Int)?) {

// CHECK: [[WHOLE:%[0-9]+]] = load {{.*}} : $*((Int, Int), Int)
// CHECK: [[WHOLE:%[0-9]+]] = unchecked_enum_data {{.*}} : $Optional<((Int, Int), Int)>
// CHECK-NEXT: [[X:%[0-9]+]] = tuple_extract [[WHOLE]] : $((Int, Int), Int), 0
// CHECK-NEXT: debug_value [[X]] : $(Int, Int), let, name "x"
// CHECK-NEXT: [[X0:%[0-9]+]] = tuple_extract [[X]] : $(Int, Int), 0
// CHECK-NEXT: [[X1:%[0-9]+]] = tuple_extract [[X]] : $(Int, Int), 1
// CHECK-NEXT: [[Y:%[0-9]+]] = tuple_extract [[WHOLE]] : $((Int, Int), Int), 1
// CHECK-NEXT: [[X:%[0-9]+]] = tuple ([[X0]] : $Int, [[X1]] : $Int)
// CHECK-NEXT: debug_value [[X]] : $(Int, Int), let, name "x"
// CHECK-NEXT: debug_value [[Y]] : $Int, let, name "y"

let (x, y) = expr!
Expand Down
1 change: 0 additions & 1 deletion test/SILGen/force_cast_chained_optional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class D: C {}
// CHECK: [[PAYLOAD_ADDR:%.*]] = unchecked_take_enum_data_addr {{%.*}} : $*ImplicitlyUnwrappedOptional<Bar>
// CHECK: [[BAR:%.*]] = load [[PAYLOAD_ADDR]]
// CHECK: class_method {{%.*}} : $Bar, #Bar.bas!getter.1 : (Bar) -> () -> C! , $@convention(method) (@guaranteed Bar) ->
// CHECK: function_ref @_TFs45_stdlib_ImplicitlyUnwrappedOptional_unwrappedurFGSQx_x
// CHECK: unconditional_checked_cast {{%.*}} : $C to $D
// CHECK: [[TRAP]]:
// CHECK: unreachable
Expand Down
6 changes: 4 additions & 2 deletions test/SILGen/implicitly_unwrapped_optional.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ func wrap<T>(x x: T) -> T! { return x }

// CHECK-LABEL: sil hidden @_TF29implicitly_unwrapped_optional16wrap_then_unwrap
func wrap_then_unwrap<T>(x x: T) -> T {
// CHECK: [[FORCE:%.*]] = function_ref @_TFs45_stdlib_ImplicitlyUnwrappedOptional_unwrappedurFGSQx_x
// CHECK: apply [[FORCE]]<{{.*}}>(%0, {{%.*}})
// CHECK: switch_enum_addr {{%.*}}, case #ImplicitlyUnwrappedOptional.none!enumelt: [[FAIL:.*]], default [[OK:bb[0-9]+]]
// CHECK: [[FAIL]]:
// CHECK: unreachable
// CHECK: [[OK]]:
return wrap(x: x)!
}

Expand Down
12 changes: 3 additions & 9 deletions test/SILGen/objc_bridging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ func getDescription(_ o: NSObject) -> String {
// CHECK: [[BRIDGED_BOX:%.*]] = enum $Optional<NSString>, #Optional.some!enumelt.1, [[BRIDGED]]
// CHECK: [[NATIVE:%.*]] = apply [[NSSTRING_TO_STRING]]([[BRIDGED_BOX]],
// CHECK: [[OPT_NATIVE:%.*]] = enum $ImplicitlyUnwrappedOptional<String>, #ImplicitlyUnwrappedOptional.some!enumelt.1, [[NATIVE]]
// CHECK: [[T0:%.*]] = function_ref @_TFs45_stdlib_ImplicitlyUnwrappedOptional_unwrappedurFGSQx_x
// CHECK: apply [[T0]]<String>([[NATIVE_BUF:%[0-9]*]],
// CHECK: [[NATIVE:%.*]] = load [[NATIVE_BUF]]
// CHECK: [[NATIVE:%.*]] = unchecked_enum_data {{.*}} : $ImplicitlyUnwrappedOptional<String>
// CHECK: return [[NATIVE]]
// CHECK:}

Expand All @@ -43,9 +41,7 @@ func getUppercaseString(_ s: NSString) -> String {
// CHECK: [[BRIDGED_BOX:%.*]] = enum $Optional<NSString>, #Optional.some!enumelt.1, [[BRIDGED]]
// CHECK: [[NATIVE:%.*]] = apply [[NSSTRING_TO_STRING]]([[BRIDGED_BOX]]
// CHECK: [[OPT_NATIVE:%.*]] = enum $ImplicitlyUnwrappedOptional<String>, #ImplicitlyUnwrappedOptional.some!enumelt.1, [[NATIVE]]
// CHECK: [[T0:%.*]] = function_ref @_TFs45_stdlib_ImplicitlyUnwrappedOptional_unwrappedurFGSQx_x
// CHECK: apply [[T0]]<String>([[NATIVE_BUF:%[0-9]*]],
// CHECK: [[NATIVE:%.*]] = load [[NATIVE_BUF]]
// CHECK: [[NATIVE:%.*]] = unchecked_enum_data {{.*}} : $ImplicitlyUnwrappedOptional<String>
// CHECK: return [[NATIVE]]
// CHECK: }

Expand Down Expand Up @@ -164,9 +160,7 @@ func callBar() -> String {
// CHECK: [[BRIDGED_BOX:%.*]] = enum $Optional<NSString>, #Optional.some!enumelt.1, [[BRIDGED]]
// CHECK: [[NATIVE:%.*]] = apply [[NSSTRING_TO_STRING]]([[BRIDGED_BOX]]
// CHECK: [[OPT_NATIVE:%.*]] = enum $ImplicitlyUnwrappedOptional<String>, #ImplicitlyUnwrappedOptional.some!enumelt.1, [[NATIVE]]
// CHECK: [[T0:%.*]] = function_ref @_TFs45_stdlib_ImplicitlyUnwrappedOptional_unwrappedurFGSQx_x
// CHECK: apply [[T0]]<String>([[NATIVE_BUF:%[0-9]*]],
// CHECK: [[NATIVE:%.*]] = load [[NATIVE_BUF]]
// CHECK: [[NATIVE:%.*]] = unchecked_enum_data {{.*}} : $ImplicitlyUnwrappedOptional<String>
// CHECK: return [[NATIVE]]
// CHECK: }

Expand Down
1 change: 0 additions & 1 deletion test/SILGen/objc_init_ref_delegation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ extension Gizmo {
// CHECK: [[SELF:%[0-9]+]] = load [[SELFMUI]] : $*Gizmo
// CHECK: [[INIT_DELEG:%[0-9]+]] = class_method [volatile] [[SELF]] : $Gizmo, #Gizmo.init!initializer.1.foreign : Gizmo.Type -> (bellsOn: Int) -> Gizmo! , $@convention(objc_method) (Int, @owned Gizmo) -> @owned ImplicitlyUnwrappedOptional<Gizmo>
// CHECK: [[SELF_RET:%[0-9]+]] = apply [[INIT_DELEG]]([[I]], [[SELF]]) : $@convention(objc_method) (Int, @owned Gizmo) -> @owned ImplicitlyUnwrappedOptional<Gizmo>
// CHECK: store [[SELF_RET]] to [[SELFMUI:%[0-9]+]] : $*ImplicitlyUnwrappedOptional<Gizmo>
// CHECK: strong_retain [[SELF4:%[0-9]+]] : $Gizmo
// CHECK: strong_release [[SELF_BOX:%[0-9]+]] : $@box Gizmo
// CHECK: return [[SELF4]] : $Gizmo
Expand Down
Loading

0 comments on commit c9ba2bf

Please sign in to comment.