Skip to content

Commit d56ed65

Browse files
committed
Swift Optimizer: add Onone simplification of some builtin instructions
* `Builtin.isConcrete` * `Builtin.is_same_metatype`
1 parent 3f35a1d commit d56ed65

File tree

3 files changed

+367
-0
lines changed

3 files changed

+367
-0
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ swift_compiler_sources(Optimizer
1010
SimplifyApply.swift
1111
SimplifyBeginCOWMutation.swift
1212
SimplifyBranch.swift
13+
SimplifyBuiltin.swift
1314
SimplifyGlobalValue.swift
1415
SimplifyStrongRetainRelease.swift)
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//===--- SimplifyBuiltin.swift --------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
extension BuiltinInst : OnoneSimplifyable {
16+
func simplify(_ context: SimplifyContext) {
17+
switch id {
18+
case .IsConcrete:
19+
// Don't constant fold a Builtin.isConcrete of a type with archetypes in the middle
20+
// of the pipeline, because a generic specializer might run afterwards which turns the
21+
// type into a concrete type.
22+
optimizeIsConcrete(allowArchetypes: false, context)
23+
case .IsSameMetatype:
24+
optimizeIsSameMetatype(context)
25+
default:
26+
// TODO: handle other builtin types
27+
break
28+
}
29+
}
30+
}
31+
32+
extension BuiltinInst : LateOnoneSimplifyable {
33+
func simplifyLate(_ context: SimplifyContext) {
34+
if id == .IsConcrete {
35+
// At the end of the pipeline we can be sure that the isConcrete's type doesn't get "more" concrete.
36+
optimizeIsConcrete(allowArchetypes: true, context)
37+
} else {
38+
simplify(context)
39+
}
40+
}
41+
}
42+
43+
private extension BuiltinInst {
44+
func optimizeIsConcrete(allowArchetypes: Bool, _ context: SimplifyContext) {
45+
let hasArchetype = operands[0].value.type.hasArchetype
46+
if hasArchetype && !allowArchetypes {
47+
return
48+
}
49+
let builder = Builder(before: self, context)
50+
let result = builder.createIntegerLiteral(hasArchetype ? 0 : 1, type: type)
51+
uses.replaceAll(with: result, context)
52+
context.erase(instruction: self)
53+
}
54+
55+
func optimizeIsSameMetatype(_ context: SimplifyContext) {
56+
let lhs = operands[0].value
57+
let rhs = operands[1].value
58+
59+
guard let equal = typesOfValuesAreEqual(lhs, rhs) else {
60+
return
61+
}
62+
let builder = Builder(before: self, context)
63+
let result = builder.createIntegerLiteral(equal ? 1 : 0, type: type)
64+
65+
uses.replaceAll(with: result, context)
66+
}
67+
}
68+
69+
private func typesOfValuesAreEqual(_ lhs: Value, _ rhs: Value) -> Bool? {
70+
if lhs == rhs {
71+
return true
72+
}
73+
74+
guard let lhsExistential = lhs as? InitExistentialMetatypeInst,
75+
let rhsExistential = rhs as? InitExistentialMetatypeInst else {
76+
return nil
77+
}
78+
79+
let lhsTy = lhsExistential.operand.type.instanceTypeOfMetatype
80+
let rhsTy = rhsExistential.operand.type.instanceTypeOfMetatype
81+
82+
// Do we know the exact types? This is not the case e.g. if a type is passed as metatype
83+
// to the function.
84+
let typesAreExact = lhsExistential.operand is MetatypeInst &&
85+
rhsExistential.operand is MetatypeInst
86+
87+
switch (lhsTy.typeKind, rhsTy.typeKind) {
88+
case (_, .unknown), (.unknown, _):
89+
return nil
90+
case (let leftKind, let rightKind) where leftKind != rightKind:
91+
// E.g. a function type is always different than a struct, regardless of what archetypes
92+
// the two types may contain.
93+
return false
94+
case (.struct, .struct), (.enum, .enum):
95+
// Two different structs/enums are always not equal, regardless of what archetypes
96+
// the two types may contain.
97+
if lhsTy.nominal != rhsTy.nominal {
98+
return false
99+
}
100+
case (.class, .class):
101+
// In case of classes this only holds if we know the exact types.
102+
// Otherwise one class could be a sub-class of the other class.
103+
if typesAreExact && lhsTy.nominal != rhsTy.nominal {
104+
return false
105+
}
106+
default:
107+
break
108+
}
109+
110+
if !typesAreExact {
111+
// Types which e.g. come from type parameters may differ at runtime while the declared AST types are the same.
112+
return nil
113+
}
114+
115+
if lhsTy.hasArchetype || rhsTy.hasArchetype {
116+
// We don't know anything about archetypes. They may be identical at runtime or not.
117+
// We could do something more sophisticated here, e.g. look at conformances. But for simplicity,
118+
// we are just conservative.
119+
return nil
120+
}
121+
122+
// Generic ObjectiveC class, which are specialized for different NSObject types have different AST types
123+
// but the same runtime metatype.
124+
if lhsTy.isOrContainsObjectiveCClass || rhsTy.isOrContainsObjectiveCClass {
125+
return nil
126+
}
127+
128+
return lhsTy == rhsTy
129+
}
130+
131+
private extension Type {
132+
enum TypeKind {
133+
case `struct`, `class`, `enum`, tuple, function, unknown
134+
}
135+
136+
var typeKind: TypeKind {
137+
if isStruct { return .struct }
138+
if isClass { return .class }
139+
if isEnum { return .enum }
140+
if isTuple { return .tuple }
141+
if isFunction { return .function }
142+
return .unknown
143+
}
144+
}
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=builtin | %FileCheck %s --check-prefix=CHECK --check-prefix=EARLY
2+
// RUN: %target-sil-opt -enable-sil-verify-all %s -late-onone-simplification -simplify-instruction=builtin | %FileCheck %s --check-prefix=CHECK --check-prefix=LATE
3+
4+
// REQUIRES: swift_in_compiler
5+
6+
import Swift
7+
import Builtin
8+
9+
struct S1<T> {
10+
}
11+
12+
struct S2<T> {
13+
}
14+
15+
enum E1<T> {
16+
}
17+
18+
enum E2<T> {
19+
}
20+
21+
class C1<T> {
22+
}
23+
24+
class C2<T> : C1<T> {
25+
}
26+
27+
// CHECK-LABEL: sil @isConcrete_true
28+
// CHECK: bb0(%0 : $@thin Int.Type):
29+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, -1
30+
// CHECK: return [[R]]
31+
// CHECK: } // end sil function 'isConcrete_true'
32+
sil @isConcrete_true : $@convention(thin) (@thin Int.Type) -> Builtin.Int1 {
33+
bb0(%0 : $@thin Int.Type):
34+
%1 = builtin "isConcrete"(%0 : $@thin Int.Type) : $Builtin.Int1
35+
return %1 : $Builtin.Int1
36+
}
37+
38+
// CHECK-LABEL: sil @isConcrete_false
39+
// CHECK: bb0(%0 : $@thin T.Type):
40+
// CHECK-EARLY: [[R:%.*]] = builtin "isConcrete"<T>(%0 : $@thin T.Type) : $Builtin.Int1
41+
// CHECK-LATE: [[R:%.*]] = integer_literal $Builtin.Int1, -1
42+
// CHECK: return [[R]]
43+
// CHECK: } // end sil function 'isConcrete_false'
44+
sil @isConcrete_false : $@convention(thin) <T> (@thin T.Type) -> Builtin.Int1 {
45+
bb0(%0 : $@thin T.Type):
46+
%1 = builtin "isConcrete"<T>(%0 : $@thin T.Type) : $Builtin.Int1
47+
return %1 : $Builtin.Int1
48+
}
49+
50+
// CHECK-LABEL: sil @same_metatype_same_operand
51+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, -1
52+
// CHECK-NEXT: return [[R]]
53+
// CHECK: } // end sil function 'same_metatype_same_operand'
54+
sil @same_metatype_same_operand : $@convention(thin) <T> (@thick T.Type) -> Builtin.Int1 {
55+
bb0(%0 : $@thick T.Type):
56+
%1 = init_existential_metatype %0 : $@thick T.Type, $@thick Any.Type
57+
%3 = builtin "is_same_metatype"(%1 : $@thick Any.Type, %1 : $@thick Any.Type) : $Builtin.Int1
58+
return %3 : $Builtin.Int1
59+
}
60+
61+
// CHECK-LABEL: sil @unknown_same_metatype_int_and_T
62+
// CHECK: [[R:%.*]] = builtin "is_same_metatype"
63+
// CHECK: return [[R]]
64+
// CHECK: } // end sil function 'unknown_same_metatype_int_and_T'
65+
sil @unknown_same_metatype_int_and_T : $@convention(thin) <T> (@thick T.Type) -> Builtin.Int1 {
66+
bb0(%0 : $@thick T.Type):
67+
%1 = metatype $@thick T.Type
68+
%2 = metatype $@thick Int.Type
69+
%3 = init_existential_metatype %1 : $@thick T.Type, $@thick Any.Type
70+
%4 = init_existential_metatype %2 : $@thick Int.Type, $@thick Any.Type
71+
%5 = builtin "is_same_metatype"(%3 : $@thick Any.Type, %4 : $@thick Any.Type) : $Builtin.Int1
72+
return %5 : $Builtin.Int1
73+
}
74+
75+
// CHECK-LABEL: sil @unknown_same_metatype_same_struct_different_T
76+
// CHECK: [[R:%.*]] = builtin "is_same_metatype"
77+
// CHECK: return [[R]]
78+
// CHECK: } // end sil function 'unknown_same_metatype_same_struct_different_T'
79+
sil @unknown_same_metatype_same_struct_different_T : $@convention(thin) <T, U> (@thick T.Type, @thick U.Type) -> Builtin.Int1 {
80+
bb0(%0 : $@thick T.Type, %1 : $@thick U.Type):
81+
%2 = metatype $@thick S1<T>.Type
82+
%3 = metatype $@thick S1<U>.Type
83+
%4 = init_existential_metatype %2 : $@thick S1<T>.Type, $@thick Any.Type
84+
%5 = init_existential_metatype %3 : $@thick S1<U>.Type, $@thick Any.Type
85+
%6 = builtin "is_same_metatype"(%4 : $@thick Any.Type, %5 : $@thick Any.Type) : $Builtin.Int1
86+
return %6 : $Builtin.Int1
87+
}
88+
89+
// CHECK-LABEL: sil @same_metatype_same_type
90+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, -1
91+
// CHECK: return [[R]]
92+
// CHECK: } // end sil function 'same_metatype_same_type'
93+
sil @same_metatype_same_type : $@convention(thin) () -> Builtin.Int1 {
94+
bb0:
95+
%0 = metatype $@thick S1<Int>.Type
96+
%1 = metatype $@thick S1<Int>.Type
97+
%2 = init_existential_metatype %0 : $@thick S1<Int>.Type, $@thick Any.Type
98+
%3 = init_existential_metatype %1 : $@thick S1<Int>.Type, $@thick Any.Type
99+
%4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1
100+
return %4 : $Builtin.Int1
101+
}
102+
103+
// CHECK-LABEL: sil @same_metatype_different_struct_arg
104+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0
105+
// CHECK: return [[R]]
106+
// CHECK: } // end sil function 'same_metatype_different_struct_arg'
107+
sil @same_metatype_different_struct_arg : $@convention(thin) () -> Builtin.Int1 {
108+
bb0:
109+
%0 = metatype $@thick S1<Int>.Type
110+
%1 = metatype $@thick S1<Float>.Type
111+
%2 = init_existential_metatype %0 : $@thick S1<Int>.Type, $@thick Any.Type
112+
%3 = init_existential_metatype %1 : $@thick S1<Float>.Type, $@thick Any.Type
113+
%4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1
114+
return %4 : $Builtin.Int1
115+
}
116+
117+
// CHECK-LABEL: sil @same_metatype_same_tuple
118+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, -1
119+
// CHECK: return [[R]]
120+
// CHECK: } // end sil function 'same_metatype_same_tuple'
121+
sil @same_metatype_same_tuple : $@convention(thin) () -> Builtin.Int1 {
122+
bb0:
123+
%0 = metatype $@thick (Int, Float).Type
124+
%1 = metatype $@thick (Int, Float).Type
125+
%2 = init_existential_metatype %0 : $@thick (Int, Float).Type, $@thick Any.Type
126+
%3 = init_existential_metatype %1 : $@thick (Int, Float).Type, $@thick Any.Type
127+
%4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1
128+
return %4 : $Builtin.Int1
129+
}
130+
131+
// CHECK-LABEL: sil @same_metatype_different_tuple
132+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0
133+
// CHECK: return [[R]]
134+
// CHECK: } // end sil function 'same_metatype_different_tuple'
135+
sil @same_metatype_different_tuple : $@convention(thin) () -> Builtin.Int1 {
136+
bb0:
137+
%0 = metatype $@thick (Int, Float).Type
138+
%1 = metatype $@thick (Int, Int).Type
139+
%2 = init_existential_metatype %0 : $@thick (Int, Float).Type, $@thick Any.Type
140+
%3 = init_existential_metatype %1 : $@thick (Int, Int).Type, $@thick Any.Type
141+
%4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1
142+
return %4 : $Builtin.Int1
143+
}
144+
145+
// CHECK-LABEL: sil @same_metatype_different_struct
146+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0
147+
// CHECK-NEXT: return [[R]]
148+
// CHECK: } // end sil function 'same_metatype_different_struct'
149+
sil @same_metatype_different_struct : $@convention(thin) (@thick S1<Int>.Type, @thick S2<Int>.Type) -> Builtin.Int1 {
150+
bb0(%0 : $@thick S1<Int>.Type, %1 : $@thick S2<Int>.Type):
151+
%2 = init_existential_metatype %0 : $@thick S1<Int>.Type, $@thick Any.Type
152+
%3 = init_existential_metatype %1 : $@thick S2<Int>.Type, $@thick Any.Type
153+
%4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1
154+
return %4 : $Builtin.Int1
155+
}
156+
157+
// CHECK-LABEL: sil @unknown_same_metatype_same_enum_different_T
158+
// CHECK: [[R:%.*]] = builtin "is_same_metatype"
159+
// CHECK: return [[R]]
160+
// CHECK: } // end sil function 'unknown_same_metatype_same_enum_different_T'
161+
sil @unknown_same_metatype_same_enum_different_T : $@convention(thin) <T, U> (@thick T.Type, @thick U.Type) -> Builtin.Int1 {
162+
bb0(%0 : $@thick T.Type, %1 : $@thick U.Type):
163+
%2 = metatype $@thick E1<T>.Type
164+
%3 = metatype $@thick E1<U>.Type
165+
%4 = init_existential_metatype %2 : $@thick E1<T>.Type, $@thick Any.Type
166+
%5 = init_existential_metatype %3 : $@thick E1<U>.Type, $@thick Any.Type
167+
%6 = builtin "is_same_metatype"(%4 : $@thick Any.Type, %5 : $@thick Any.Type) : $Builtin.Int1
168+
return %6 : $Builtin.Int1
169+
}
170+
171+
// CHECK-LABEL: sil @same_metatype_different_enum
172+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0
173+
// CHECK-NEXT: return [[R]]
174+
// CHECK: } // end sil function 'same_metatype_different_enum'
175+
sil @same_metatype_different_enum : $@convention(thin) (@thick E1<Int>.Type, @thick E2<Int>.Type) -> Builtin.Int1 {
176+
bb0(%0 : $@thick E1<Int>.Type, %1 : $@thick E2<Int>.Type):
177+
%2 = init_existential_metatype %0 : $@thick E1<Int>.Type, $@thick Any.Type
178+
%3 = init_existential_metatype %1 : $@thick E2<Int>.Type, $@thick Any.Type
179+
%4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1
180+
return %4 : $Builtin.Int1
181+
}
182+
183+
// CHECK-LABEL: sil @unknown_same_metatype_same_class_different_T
184+
// CHECK: [[R:%.*]] = builtin "is_same_metatype"
185+
// CHECK: return [[R]]
186+
// CHECK: } // end sil function 'unknown_same_metatype_same_class_different_T'
187+
sil @unknown_same_metatype_same_class_different_T : $@convention(thin) <T, U> (@thick T.Type, @thick U.Type) -> Builtin.Int1 {
188+
bb0(%0 : $@thick T.Type, %1 : $@thick U.Type):
189+
%2 = metatype $@thick C1<T>.Type
190+
%3 = metatype $@thick C1<U>.Type
191+
%4 = init_existential_metatype %2 : $@thick C1<T>.Type, $@thick Any.Type
192+
%5 = init_existential_metatype %3 : $@thick C1<U>.Type, $@thick Any.Type
193+
%6 = builtin "is_same_metatype"(%4 : $@thick Any.Type, %5 : $@thick Any.Type) : $Builtin.Int1
194+
return %6 : $Builtin.Int1
195+
}
196+
197+
// CHECK-LABEL: sil @unknown_same_metatype_different_class
198+
// CHECK: [[R:%.*]] = builtin "is_same_metatype"
199+
// CHECK: return [[R]]
200+
// CHECK: } // end sil function 'unknown_same_metatype_different_class'
201+
sil @unknown_same_metatype_different_class : $@convention(thin) (@thick C1<Int>.Type, @thick C2<Int>.Type) -> Builtin.Int1 {
202+
bb0(%0 : $@thick C1<Int>.Type, %1 : $@thick C2<Int>.Type):
203+
%2 = init_existential_metatype %0 : $@thick C1<Int>.Type, $@thick Any.Type
204+
%3 = init_existential_metatype %1 : $@thick C2<Int>.Type, $@thick Any.Type
205+
%4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1
206+
return %4 : $Builtin.Int1
207+
}
208+
209+
// CHECK-LABEL: sil @same_metatype_different_concrete_class
210+
// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0
211+
// CHECK-NEXT: return [[R]]
212+
// CHECK: } // end sil function 'same_metatype_different_concrete_class'
213+
sil @same_metatype_different_concrete_class : $@convention(thin) () -> Builtin.Int1 {
214+
bb0:
215+
%0 = metatype $@thick C1<Int>.Type
216+
%1 = metatype $@thick C2<Int>.Type
217+
%2 = init_existential_metatype %0 : $@thick C1<Int>.Type, $@thick Any.Type
218+
%3 = init_existential_metatype %1 : $@thick C2<Int>.Type, $@thick Any.Type
219+
%4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1
220+
return %4 : $Builtin.Int1
221+
}
222+

0 commit comments

Comments
 (0)