Skip to content

Commit d522618

Browse files
committed
Add a new builtin called is_same_metatype for checking the equality between metatypes
Having such a builtin makes it easier for the optimizer to reason about what is actually happening. I plan to add later some optimizations which can optimize pieces of code dominated by such a check.
1 parent e1a5610 commit d522618

File tree

8 files changed

+128
-1
lines changed

8 files changed

+128
-1
lines changed

include/swift/AST/Builtins.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ BUILTIN_MISC_OPERATION(Strideof, "strideof", "n", Special)
393393
/// IsPOD has type T.Type -> Bool
394394
BUILTIN_MISC_OPERATION(IsPOD, "ispod", "n", Special)
395395

396+
/// IsSameMetatype has type (Any.Type, Any.Type) -> Bool
397+
BUILTIN_MISC_OPERATION(IsSameMetatype, "is_same_metatype", "n", Special)
398+
396399
/// Alignof has type T.Type -> Int
397400
BUILTIN_MISC_OPERATION(Alignof, "alignof", "n", Special)
398401

lib/AST/Builtins.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,12 @@ static ValueDecl *getIsOptionalOperation(ASTContext &Context, Identifier Id) {
754754
return builder.build(Id);
755755
}
756756

757+
static ValueDecl *getIsSameMetatypeOperation(ASTContext &Context, Identifier Id) {
758+
CanType anyMetatype = CanExistentialMetatypeType::get(Context.TheAnyType);
759+
auto ResultTy = BuiltinIntegerType::get(1,Context);
760+
return getBuiltinFunction(Id, {anyMetatype, anyMetatype}, ResultTy);
761+
}
762+
757763
static ValueDecl *getAllocOperation(ASTContext &Context, Identifier Id) {
758764
Type PtrSizeTy = BuiltinIntegerType::getWordType(Context);
759765
Type ResultTy = Context.TheRawPointerType;
@@ -1668,6 +1674,9 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
16681674
case BuiltinValueKind::IsOptionalType:
16691675
return getIsOptionalOperation(Context, Id);
16701676

1677+
case BuiltinValueKind::IsSameMetatype:
1678+
return getIsSameMetatypeOperation(Context, Id);
1679+
16711680
case BuiltinValueKind::AllocRaw:
16721681
return getAllocOperation(Context, Id);
16731682

lib/IRGen/GenBuiltin.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "Explosion.h"
2929
#include "GenCall.h"
30+
#include "GenCast.h"
3031
#include "IRGenFunction.h"
3132
#include "IRGenModule.h"
3233
#include "LoadableTypeInfo.h"
@@ -876,6 +877,18 @@ if (Builtin.ID == BuiltinValueKind::id) { \
876877
return;
877878
}
878879

880+
if (Builtin.ID == BuiltinValueKind::IsSameMetatype) {
881+
auto metatypeLHS = args.claimNext();
882+
auto metatypeRHS = args.claimNext();
883+
(void)args.claimAll();
884+
llvm::Value *metatypeLHSCasted =
885+
IGF.Builder.CreateBitCast(metatypeLHS, IGF.IGM.Int8PtrTy);
886+
llvm::Value *metatypeRHSCasted =
887+
IGF.Builder.CreateBitCast(metatypeRHS, IGF.IGM.Int8PtrTy);
888+
889+
out.add(IGF.Builder.CreateICmpEQ(metatypeLHSCasted, metatypeRHSCasted));
890+
return;
891+
}
879892

880893
llvm_unreachable("IRGen unimplemented for this builtin!");
881894
}

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, false, IntToFPWithOverflow)
11101110
CONSTANT_OWNERSHIP_BUILTIN(Trivial, false, IntToPtr)
11111111
CONSTANT_OWNERSHIP_BUILTIN(Trivial, false, IsOptionalType)
11121112
CONSTANT_OWNERSHIP_BUILTIN(Trivial, false, IsPOD)
1113+
CONSTANT_OWNERSHIP_BUILTIN(Trivial, false, IsSameMetatype)
11131114
CONSTANT_OWNERSHIP_BUILTIN(Trivial, false, LShr)
11141115
CONSTANT_OWNERSHIP_BUILTIN(Trivial, false, Mul)
11151116
CONSTANT_OWNERSHIP_BUILTIN(Trivial, false, OnFastPath)

lib/SIL/ValueOwnershipKindClassifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsOptionalType)
540540
CONSTANT_OWNERSHIP_BUILTIN(Trivial, Sizeof)
541541
CONSTANT_OWNERSHIP_BUILTIN(Trivial, Strideof)
542542
CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsPOD)
543+
CONSTANT_OWNERSHIP_BUILTIN(Trivial, IsSameMetatype)
543544
CONSTANT_OWNERSHIP_BUILTIN(Trivial, Alignof)
544545
CONSTANT_OWNERSHIP_BUILTIN(Trivial, AllocRaw)
545546
CONSTANT_OWNERSHIP_BUILTIN(Trivial, AssertConf)

stdlib/public/core/Builtin.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,12 @@ func != (lhs: Builtin.RawPointer, rhs: Builtin.RawPointer) -> Bool {
152152
/// `nil` or they both represent the same type.
153153
@_inlineable
154154
public func == (t0: Any.Type?, t1: Any.Type?) -> Bool {
155-
return unsafeBitCast(t0, to: Int.self) == unsafeBitCast(t1, to: Int.self)
155+
switch (t0, t1) {
156+
case (.none, .none): return true
157+
case let (.some(ty0), .some(ty1)):
158+
return Bool(Builtin.is_same_metatype(ty0, ty1))
159+
default: return false
160+
}
156161
}
157162

158163
/// Returns `false` iff `t0` is identical to `t1`; i.e. if they are both

test/IRGen/builtins.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,14 @@ func ispod_test() {
745745
var f = Builtin.ispod(Builtin.NativeObject)
746746
}
747747

748+
// CHECK-LABEL: define {{.*}} @{{.*}}is_same_metatype
749+
func is_same_metatype_test(_ t1: Any.Type, _ t2: Any.Type) {
750+
// CHECK: [[MT1_AS_PTR:%.*]] = bitcast %swift.type* %0 to i8*
751+
// CHECK: [[MT2_AS_PTR:%.*]] = bitcast %swift.type* %1 to i8*
752+
// CHECK: icmp eq i8* [[MT1_AS_PTR]], [[MT2_AS_PTR]]
753+
var t = Builtin.is_same_metatype(t1, t2)
754+
}
755+
748756
// CHECK-LABEL: define {{.*}} @{{.*}}generic_unsafeGuaranteed_test
749757
// CHECK: call void @{{.*}}swift_{{.*}}etain({{.*}}* %0)
750758
// CHECK: call void @{{.*}}swift_{{.*}}elease({{.*}}* %0)

test/SILOptimizer/compare_types.swift

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// RUN: %target-swift-frontend -O -emit-sil %s | %FileCheck %s
2+
3+
// Check type equality related optimizations.
4+
5+
// CHECK-LABEL: sil @{{.*}}areEqualTypes1{{.*}} : $@convention(thin)
6+
// CHECK: builtin "is_same_metatype"
7+
// CHECK: // end sil function '{{.*}}areEqualTypes1{{.*}}'
8+
public func areEqualTypes1<T1, T2>(_ t1: T1.Type, _ t2: T2.Type) -> Bool {
9+
return t1 == t2
10+
}
11+
12+
// CHECK-LABEL: sil @{{.*}}areEqualTypes2{{.*}} : $@convention(thin)
13+
// CHECK: builtin "is_same_metatype"
14+
// CHECK: // end sil function '{{.*}}areEqualTypes2{{.*}}'
15+
public func areEqualTypes2<T1, T2>(_ t1: T1.Type, _ t2o: T2.Type?) -> Bool {
16+
return t1 == t2o
17+
}
18+
19+
// CHECK-LABEL: sil @{{.*}}areEqualTypes3{{.*}} : $@convention(thin)
20+
// CHECK: builtin "is_same_metatype"
21+
// CHECK: // end sil function '{{.*}}areEqualTypes3{{.*}}'
22+
public func areEqualTypes3<T1, T2>(_ t1o: T1.Type?, _ t2: T2.Type) -> Bool {
23+
return t1o == t2
24+
}
25+
26+
// CHECK-LABEL: sil @{{.*}}areEqualTypes4{{.*}} : $@convention(thin)
27+
// CHECK: builtin "is_same_metatype"
28+
// CHECK: // end sil function '{{.*}}areEqualTypes4{{.*}}'
29+
public func areEqualTypes4<T1, T2>(_ t1o: T1.Type?, _ t2o: T2.Type?) -> Bool {
30+
return t1o == t2o
31+
}
32+
33+
// CHECK-LABEL: sil @{{.*}}areEqualTypes5{{.*}} : $@convention(thin)
34+
// CHECK-NOT: builtin "is_same_metatype"
35+
// CHECK: // end sil function '{{.*}}areEqualTypes5{{.*}}'
36+
public func areEqualTypes5<T1>(_ t1o: T1.Type?) -> Bool {
37+
return t1o == nil
38+
}
39+
40+
// CHECK-LABEL: sil @{{.*}}areEqualTypes6{{.*}} : $@convention(thin)
41+
// CHECK-NOT: builtin "is_same_metatype"
42+
// CHECK: // end sil function '{{.*}}areEqualTypes6{{.*}}'
43+
public func areEqualTypes6<T1>(_ t1o: T1.Type?) -> Bool {
44+
return nil == t1o
45+
}
46+
47+
// CHECK-LABEL: sil @{{.*}}areNotEqualTypes1{{.*}} : $@convention(thin)
48+
// CHECK: builtin "is_same_metatype"
49+
// CHECK: // end sil function '{{.*}}areNotEqualTypes1{{.*}}'
50+
public func areNotEqualTypes1<T1, T2>(_ t1: T1.Type, _ t2: T2.Type) -> Bool {
51+
return t1 != t2
52+
}
53+
54+
// CHECK-LABEL: sil @{{.*}}areNotEqualTypes2{{.*}} : $@convention(thin)
55+
// CHECK: builtin "is_same_metatype"
56+
// CHECK: // end sil function '{{.*}}areNotEqualTypes2{{.*}}'
57+
public func areNotEqualTypes2<T1, T2>(_ t1: T1.Type, _ t2o: T2.Type?) -> Bool {
58+
return t1 != t2o
59+
}
60+
61+
// CHECK-LABEL: sil @{{.*}}areNotEqualTypes3{{.*}} : $@convention(thin)
62+
// CHECK: builtin "is_same_metatype"
63+
// CHECK: // end sil function '{{.*}}areNotEqualTypes3{{.*}}'
64+
public func areNotEqualTypes3<T1, T2>(_ t1o: T1.Type?, _ t2: T2.Type) -> Bool {
65+
return t1o != t2
66+
}
67+
68+
// CHECK-LABEL: sil @{{.*}}areNotEqualTypes4{{.*}} : $@convention(thin)
69+
// CHECK: builtin "is_same_metatype"
70+
// CHECK: // end sil function '{{.*}}areNotEqualTypes4{{.*}}'
71+
public func areNotEqualTypes4<T1, T2>(_ t1o: T1.Type?, _ t2o: T2.Type?) -> Bool {
72+
return t1o != t2o
73+
}
74+
75+
// CHECK-LABEL: sil @{{.*}}areNotEqualTypes5{{.*}} : $@convention(thin)
76+
// CHECK-NOT: builtin "is_same_metatype"
77+
// CHECK: // end sil function '{{.*}}areNotEqualTypes5{{.*}}'
78+
public func areNotEqualTypes5<T1>(_ t1o: T1.Type?) -> Bool {
79+
return t1o != nil
80+
}
81+
82+
// CHECK-LABEL: sil @{{.*}}areNotEqualTypes6{{.*}} : $@convention(thin)
83+
// CHECK-NOT: builtin "is_same_metatype"
84+
// CHECK: // end sil function '{{.*}}areNotEqualTypes6{{.*}}'
85+
public func areNotEqualTypes6<T1>(_ t1o: T1.Type?) -> Bool {
86+
return nil != t1o
87+
}

0 commit comments

Comments
 (0)