Skip to content

Commit e72af82

Browse files
committed
Remove the unused isSome Optional intrinsics.
We already have detailed knowledge of Optional's layout in SILGen, so these intrinsics were almost unused. They were only used in a few obscure places by some optional-to-bool conversions, used by 'is [A]' collection tests and the codegen for 'lazy' properties. Change these over to generate an EnumIsCaseExpr that we can directly lower to a 'select_enum' instruction in SILGen, leading to better codegen and obviating the need for these intrinsic functions.
1 parent b4b44c6 commit e72af82

File tree

14 files changed

+139
-79
lines changed

14 files changed

+139
-79
lines changed

include/swift/AST/ASTContext.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -454,10 +454,6 @@ class ASTContext {
454454
FuncDecl *get##Name(LazyResolver *resolver) const;
455455
#include "swift/AST/KnownDecls.def"
456456

457-
/// Swift._stdlib_{,ImplicitlyUnwrapped}Optional_isSome.
458-
FuncDecl *getOptionalIsSomeDecl(LazyResolver *resolver,
459-
OptionalTypeKind kind) const;
460-
461457
/// Retrieve the declaration of
462458
/// Swift._stdlib_{,ImplicitlyUnwrapped}Optional_unwrapped.
463459
FuncDecl *getOptionalUnwrappedDecl(LazyResolver *resolver,

include/swift/AST/Expr.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ namespace swift {
5151
class TypeDecl;
5252
class PatternBindingDecl;
5353
class ParameterList;
54+
class EnumElementDecl;
5455

5556
enum class ExprKind : uint8_t {
5657
#define EXPR(Id, Parent) Id,
@@ -3655,6 +3656,33 @@ class IfExpr : public Expr {
36553656
}
36563657
};
36573658

3659+
/// EnumIsCaseExpr - A boolean expression that is true if an enum value is of
3660+
/// a particular case.
3661+
class EnumIsCaseExpr : public Expr {
3662+
Expr *SubExpr;
3663+
EnumElementDecl *Element;
3664+
3665+
public:
3666+
EnumIsCaseExpr(Expr *SubExpr, EnumElementDecl *Element)
3667+
: Expr(ExprKind::EnumIsCase, /*implicit*/ true),
3668+
SubExpr(SubExpr), Element(Element)
3669+
{}
3670+
3671+
Expr *getSubExpr() const { return SubExpr; }
3672+
void setSubExpr(Expr *e) { SubExpr = e; }
3673+
3674+
EnumElementDecl *getEnumElement() const { return Element; }
3675+
void setEnumElement(EnumElementDecl *elt) { Element = elt; }
3676+
3677+
SourceLoc getLoc() const { return SubExpr->getLoc(); }
3678+
SourceLoc getStartLoc() const { return SubExpr->getStartLoc(); }
3679+
SourceLoc getEndLoc() const { return SubExpr->getEndLoc(); }
3680+
3681+
static bool classof(const Expr *E) {
3682+
return E->getKind() == ExprKind::EnumIsCase;
3683+
}
3684+
};
3685+
36583686
/// AssignExpr - A value assignment, like "x = y".
36593687
class AssignExpr : public Expr {
36603688
Expr *Dest;

include/swift/AST/ExprNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ ABSTRACT_EXPR(ExplicitCast, Expr)
152152
EXPR_RANGE(ExplicitCast, ForcedCheckedCast, Coerce)
153153
UNCHECKED_EXPR(Arrow, Expr)
154154
EXPR(If, Expr)
155+
EXPR(EnumIsCase, Expr)
155156
EXPR(Assign, Expr)
156157
EXPR(DefaultValue, Expr)
157158
EXPR(CodeCompletion, Expr)

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,6 @@ struct ASTContext::Implementation {
168168
#define FUNC_DECL(Name, Id) FuncDecl *Get##Name = nullptr;
169169
#include "swift/AST/KnownDecls.def"
170170

171-
/// func _stdlib_Optional_isSome<T>(v: Optional<T>) -> Bool
172-
FuncDecl *OptionalIsSomeSomeDecls[NumOptionalTypeKinds] = {};
173-
174171
/// func _stdlib_Optional_unwrapped<T>(v: Optional<T>) -> T
175172
FuncDecl *OptionalUnwrappedDecls[NumOptionalTypeKinds] = {};
176173

@@ -1121,35 +1118,6 @@ static unsigned asIndex(OptionalTypeKind optionalKind) {
11211118
? (PREFIX "Optional" SUFFIX) \
11221119
: (PREFIX "ImplicitlyUnwrappedOptional" SUFFIX))
11231120

1124-
FuncDecl *ASTContext::getOptionalIsSomeDecl(
1125-
LazyResolver *resolver, OptionalTypeKind optionalKind) const {
1126-
auto &cache = Impl.OptionalIsSomeSomeDecls[asIndex(optionalKind)];
1127-
if (cache)
1128-
return cache;
1129-
1130-
auto name =
1131-
getOptionalIntrinsicName("_stdlib_", optionalKind, "_isSome");
1132-
1133-
// Look for a generic function.
1134-
CanType input, output, param;
1135-
auto decl = findLibraryIntrinsic(*this, name, resolver);
1136-
if (!decl || !isGenericIntrinsic(decl, input, output, param))
1137-
return nullptr;
1138-
1139-
// Input must be Optional<T>.
1140-
if (!isOptionalType(*this, optionalKind, input, param))
1141-
return nullptr;
1142-
1143-
// Output must be a global type named Bool.
1144-
auto nominalType = dyn_cast<NominalType>(output);
1145-
if (!nominalType || nominalType.getParent() ||
1146-
nominalType->getDecl()->getName().str() != "Bool")
1147-
return nullptr;
1148-
1149-
cache = decl;
1150-
return decl;
1151-
}
1152-
11531121
FuncDecl *ASTContext::getOptionalUnwrappedDecl(LazyResolver *resolver,
11541122
OptionalTypeKind optionalKind) const {
11551123
auto &cache = Impl.OptionalUnwrappedDecls[asIndex(optionalKind)];
@@ -1178,8 +1146,7 @@ FuncDecl *ASTContext::getOptionalUnwrappedDecl(LazyResolver *resolver,
11781146

11791147
static bool hasOptionalIntrinsics(const ASTContext &ctx, LazyResolver *resolver,
11801148
OptionalTypeKind optionalKind) {
1181-
return ctx.getOptionalIsSomeDecl(resolver, optionalKind) &&
1182-
ctx.getOptionalUnwrappedDecl(resolver, optionalKind);
1149+
return ctx.getOptionalUnwrappedDecl(resolver, optionalKind);
11831150
}
11841151

11851152
bool ASTContext::hasOptionalIntrinsics(LazyResolver *resolver) const {

lib/AST/ASTDumper.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,6 +2160,12 @@ class PrintExpr : public ExprVisitor<PrintExpr> {
21602160
printRec(E->getSrc());
21612161
OS << ')';
21622162
}
2163+
void visitEnumIsCaseExpr(EnumIsCaseExpr *E) {
2164+
printCommon(E, "enum_is_case_expr") << ' ' <<
2165+
E->getEnumElement()->getName() << "\n";
2166+
printRec(E->getSubExpr());
2167+
2168+
}
21632169
void visitUnresolvedPatternExpr(UnresolvedPatternExpr *E) {
21642170
printCommon(E, "unresolved_pattern_expr") << '\n';
21652171
printRec(E->getSubPattern());

lib/AST/ASTVerifier.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,26 @@ struct ASTNodeBase {};
835835
checkSameType(lhsTy, S->getSrc()->getType(), "assignment operands");
836836
verifyCheckedBase(S);
837837
}
838+
839+
void verifyChecked(EnumIsCaseExpr *E) {
840+
auto nom = E->getSubExpr()->getType()->getAnyNominal();
841+
if (!nom || !isa<EnumDecl>(nom)) {
842+
Out << "enum_is_decl operand is not an enum: ";
843+
E->getSubExpr()->getType().print(Out);
844+
Out << '\n';
845+
abort();
846+
}
847+
848+
if (nom != E->getEnumElement()->getParentEnum()) {
849+
Out << "enum_is_decl case is not member of enum:\n";
850+
Out << " case: ";
851+
E->getEnumElement()->print(Out);
852+
Out << "\n type: ";
853+
E->getSubExpr()->getType().print(Out);
854+
Out << '\n';
855+
abort();
856+
}
857+
}
838858

839859
void verifyChecked(TupleExpr *E) {
840860
const TupleType *exprTy = E->getType()->castTo<TupleType>();

lib/AST/ASTWalker.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,16 @@ class Traversal : public ASTVisitor<Traversal, Expr*, Stmt*,
747747
return AE;
748748
}
749749

750+
Expr *visitEnumIsCaseExpr(EnumIsCaseExpr *E) {
751+
if (Expr *Sub = E->getSubExpr()) {
752+
if (!(Sub = doIt(Sub)))
753+
return nullptr;
754+
E->setSubExpr(Sub);
755+
}
756+
757+
return E;
758+
}
759+
750760

751761
Expr *visitIfExpr(IfExpr *E) {
752762
if (Expr *Cond = E->getCondExpr()) {

lib/AST/Expr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ void Expr::propagateLValueAccessKind(AccessKind accessKind,
316316
NON_LVALUE_EXPR(CodeCompletion)
317317
NON_LVALUE_EXPR(ObjCSelector)
318318
NON_LVALUE_EXPR(ObjCKeyPath)
319+
NON_LVALUE_EXPR(EnumIsCase)
319320

320321
#define UNCHECKED_EXPR(KIND, BASE) \
321322
NON_LVALUE_EXPR(KIND)
@@ -600,6 +601,7 @@ bool Expr::canAppendCallParentheses() const {
600601
case ExprKind::LValueToPointer:
601602
case ExprKind::ForeignObjectConversion:
602603
case ExprKind::UnevaluatedInstance:
604+
case ExprKind::EnumIsCase:
603605
// Implicit conversion nodes have no syntax of their own; defer to the
604606
// subexpression.
605607
return cast<ImplicitConversionExpr>(this)->getSubExpr()

lib/SILGen/SILGenExpr.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ namespace {
216216

217217
RValue visitDefaultValueExpr(DefaultValueExpr *E, SGFContext C);
218218
RValue visitAssignExpr(AssignExpr *E, SGFContext C);
219+
RValue visitEnumIsCaseExpr(EnumIsCaseExpr *E, SGFContext C);
219220

220221
RValue visitBindOptionalExpr(BindOptionalExpr *E, SGFContext C);
221222
RValue visitOptionalEvaluationExpr(OptionalEvaluationExpr *E,
@@ -1425,14 +1426,42 @@ RValue RValueEmitter::visitIsExpr(IsExpr *E, SGFContext C) {
14251426
E->getCastTypeLoc().getType(), E->getCastKind());
14261427

14271428
// Call the _getBool library intrinsic.
1428-
ASTContext &ctx = SGF.SGM.M.getASTContext();
1429+
ASTContext &ctx = SGF.getASTContext();
14291430
auto result =
14301431
SGF.emitApplyOfLibraryIntrinsic(E, ctx.getGetBoolDecl(nullptr), {},
14311432
ManagedValue::forUnmanaged(isa),
14321433
C);
14331434
return result;
14341435
}
14351436

1437+
RValue RValueEmitter::visitEnumIsCaseExpr(EnumIsCaseExpr *E,
1438+
SGFContext C) {
1439+
ASTContext &ctx = SGF.getASTContext();
1440+
// Get the enum value.
1441+
auto subExpr = SGF.emitRValueAsSingleValue(E->getSubExpr(),
1442+
SGFContext(SGFContext::AllowImmediatePlusZero));
1443+
// Test its case.
1444+
auto i1Ty = SILType::getBuiltinIntegerType(1, SGF.getASTContext());
1445+
auto t = SGF.B.createIntegerLiteral(E, i1Ty, 1);
1446+
auto f = SGF.B.createIntegerLiteral(E, i1Ty, 0);
1447+
1448+
SILValue selected;
1449+
if (subExpr.getType().isAddress()) {
1450+
selected = SGF.B.createSelectEnumAddr(E, subExpr.getValue(), i1Ty, f,
1451+
{{E->getEnumElement(), t}});
1452+
} else {
1453+
selected = SGF.B.createSelectEnum(E, subExpr.getValue(), i1Ty, f,
1454+
{{E->getEnumElement(), t}});
1455+
}
1456+
1457+
// Call the _getBool library intrinsic.
1458+
auto result =
1459+
SGF.emitApplyOfLibraryIntrinsic(E, ctx.getGetBoolDecl(nullptr), {},
1460+
ManagedValue::forUnmanaged(selected),
1461+
C);
1462+
return result;
1463+
}
1464+
14361465
RValue RValueEmitter::visitCoerceExpr(CoerceExpr *E, SGFContext C) {
14371466
return visit(E->getSubExpr(), C);
14381467
}

lib/Sema/CSApply.cpp

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3299,6 +3299,13 @@ namespace {
32993299
llvm_unreachable("Already type-checked");
33003300
}
33013301

3302+
Expr *visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {
3303+
// Should already be type-checked.
3304+
Type valueType = simplifyType(expr->getType());
3305+
expr->setType(valueType);
3306+
return expr;
3307+
}
3308+
33023309
Expr *visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
33033310
Type valueType = simplifyType(E->getType());
33043311
E->setType(valueType);
@@ -6819,32 +6826,10 @@ Expr *Solution::convertOptionalToBool(Expr *expr,
68196826
auto &tc = cs.getTypeChecker();
68206827
tc.requireOptionalIntrinsics(expr->getLoc());
68216828

6822-
// Find the library intrinsic.
6829+
// Match the optional value against its `Some` case.
68236830
auto &ctx = tc.Context;
6824-
auto *fn = ctx.getOptionalIsSomeDecl(&tc, OTK_Optional);
6825-
tc.validateDecl(fn);
6826-
6827-
// Form a reference to the function. This library intrinsic is generic, so we
6828-
// need to form substitutions and compute the resulting type.
6829-
auto unwrappedOptionalType = expr->getType()->getOptionalObjectType();
6830-
6831-
Substitution sub(unwrappedOptionalType, {});
6832-
ConcreteDeclRef fnSpecRef(ctx, fn, sub);
6833-
auto *fnRef =
6834-
new (ctx) DeclRefExpr(fnSpecRef, DeclNameLoc(), /*Implicit=*/true);
6835-
6836-
TypeSubstitutionMap subMap;
6837-
auto genericParam = fn->getGenericSignatureOfContext()->getGenericParams()[0];
6838-
subMap[genericParam->getCanonicalType()->castTo<SubstitutableType>()] =
6839-
unwrappedOptionalType;
6840-
fnRef->setType(fn->getInterfaceType().subst(
6841-
constraintSystem->DC->getParentModule(), subMap, None));
6842-
6843-
Expr *call = new (ctx) CallExpr(fnRef, expr, /*Implicit=*/true);
6844-
6845-
bool failed = tc.typeCheckExpressionShallow(call, cs.DC);
6846-
assert(!failed && "Could not call library intrinsic?");
6847-
(void)failed;
6848-
return call;
6831+
auto isSomeExpr = new (ctx) EnumIsCaseExpr(expr, ctx.getOptionalSomeDecl());
6832+
isSomeExpr->setType(tc.lookupBoolType(cs.DC));
6833+
return isSomeExpr;
68496834
}
68506835

lib/Sema/CSGen.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,6 +2810,11 @@ namespace {
28102810
Type visitOpenExistentialExpr(OpenExistentialExpr *expr) {
28112811
llvm_unreachable("Already type-checked");
28122812
}
2813+
2814+
Type visitEnumIsCaseExpr(EnumIsCaseExpr *expr) {
2815+
// Should already be type-checked.
2816+
return expr->getType();
2817+
}
28132818

28142819
Type visitEditorPlaceholderExpr(EditorPlaceholderExpr *E) {
28152820
if (E->getTypeLoc().isNull()) {

stdlib/public/core/ImplicitlyUnwrappedOptional.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,6 @@ extension ImplicitlyUnwrappedOptional : CustomDebugStringConvertible {
6363
}
6464
}
6565

66-
@_transparent
67-
public // COMPILER_INTRINSIC
68-
func _stdlib_ImplicitlyUnwrappedOptional_isSome<Wrapped>
69-
(_ `self`: Wrapped!) -> Bool {
70-
71-
return `self` != nil
72-
}
73-
7466
@_transparent
7567
public // COMPILER_INTRINSIC
7668
func _stdlib_ImplicitlyUnwrappedOptional_unwrapped<Wrapped>

stdlib/public/core/Optional.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -284,12 +284,6 @@ extension Optional : CustomReflectable {
284284
}
285285
}
286286

287-
@_transparent
288-
public // COMPILER_INTRINSIC
289-
func _stdlib_Optional_isSome<Wrapped>(_ `self`: Wrapped?) -> Bool {
290-
return `self` != nil
291-
}
292-
293287
@_transparent
294288
public // COMPILER_INTRINSIC
295289
func _stdlib_Optional_unwrapped<Wrapped>(_ `self`: Wrapped?) -> Wrapped {

test/SILGen/optional_to_bool.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime
2+
3+
public protocol P {}
4+
extension Int: P {}
5+
6+
public class A {}
7+
public class B: A {
8+
// CHECK-LABEL: sil @_TFC16optional_to_bool1Bg1x
9+
// CHECK: select_enum {{%.*}} : $Optional<Int>
10+
public lazy var x: Int = 0
11+
// CHECK-LABEL: sil @_TFC16optional_to_bool1Bg1y
12+
// CHECK: select_enum_addr {{%.*}} : $*Optional<P>
13+
public lazy var y: P = 0
14+
}
15+
16+
// Collection casting is not implemented in non-ObjC runtime
17+
#if _runtime(_ObjC)
18+
19+
// CHECK-objc-LABEL: sil @_TF16optional_to_bool3foo
20+
public func foo(x: inout [A]) -> Bool {
21+
// CHECK-objc: select_enum {{%.*}} : $Optional<Array<B>>
22+
return x is [B]
23+
}
24+
25+
#endif

0 commit comments

Comments
 (0)