-
Notifications
You must be signed in to change notification settings - Fork 10.5k
/
Copy pathEagerSpecializer.cpp
926 lines (801 loc) · 36.6 KB
/
EagerSpecializer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
//===--- EagerSpecializer.cpp - Performs Eager Specialization -------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Eager Specializer
/// -----------------
///
/// This transform specializes functions that are annotated with the
/// @_specialize(<type list>) attribute. A function may be annotated with
/// multiple @_specialize attributes, each with a list of concrete types. For
/// each @_specialize attribute, this transform clones the annotated generic
/// function, creating a new function signature by substituting the concrete
/// types specified in the attribute into the function's generic
/// signature. Dispatch to each specialized function is implemented by inserting
/// call at the beginning of the original generic function guarded by a type
/// check.
///
/// TODO: We have not determined whether to support inexact type checks. It
/// will be a tradeoff between utility of the attribute vs. cost of the check.
///
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "eager-specializer"
#include "swift/AST/GenericEnvironment.h"
#include "swift/AST/Type.h"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
#include "swift/SILOptimizer/Utils/Generics.h"
#include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h"
#include "llvm/Support/Debug.h"
using namespace swift;
// Temporary flag.
llvm::cl::opt<bool> EagerSpecializeFlag(
"enable-eager-specializer", llvm::cl::init(true),
llvm::cl::desc("Run the eager-specializer pass."));
static void
cleanupCallArguments(SILBuilder &builder, SILLocation loc,
ArrayRef<SILValue> values,
ArrayRef<unsigned> valueIndicesThatNeedEndBorrow) {
for (int index : valueIndicesThatNeedEndBorrow) {
auto *lbi = cast<LoadBorrowInst>(values[index]);
builder.createEndBorrow(loc, lbi);
}
}
/// Returns true if the given return or throw block can be used as a merge point
/// for new return or error values.
static bool isTrivialReturnBlock(SILBasicBlock *RetBB) {
auto *RetInst = RetBB->getTerminator();
assert(RetInst->isFunctionExiting() &&
"expected a properly terminated return or throw block");
auto RetOperand = RetInst->getOperand(0);
// Allow:
// % = tuple ()
// return % : $()
if (RetOperand->getType().isVoid()) {
if (!RetBB->args_empty())
return false;
auto *TupleI = dyn_cast<TupleInst>(RetBB->begin());
if (!TupleI || !TupleI->getType().isVoid())
return false;
if (&*std::next(RetBB->begin()) != RetInst)
return false;
return RetOperand == TupleI;
}
// Allow:
// bb(% : $T)
// return % : $T
if (&*RetBB->begin() != RetInst)
return false;
if (RetBB->args_size() != 1)
return false;
return (RetOperand == RetBB->getArgument(0));
}
/// Adds a CFG edge from the unterminated NewRetBB to a merged "return" or
/// "throw" block. If the return block is not already a canonical merged return
/// block, then split it. If the return type is not Void, add a BBArg that
/// propagates NewRetVal to the return instruction.
static void addReturnValueImpl(SILBasicBlock *RetBB, SILBasicBlock *NewRetBB,
SILValue NewRetVal) {
auto *F = NewRetBB->getParent();
SILBuilder Builder(*F);
Builder.setCurrentDebugScope(F->getDebugScope());
SILLocation Loc = F->getLocation();
auto *RetInst = RetBB->getTerminator();
assert(RetInst->isFunctionExiting() &&
"expected a properly terminated return or throw block");
assert(RetInst->getOperand(0)->getType() == NewRetVal->getType() &&
"Mismatched return type");
SILBasicBlock *MergedBB = RetBB;
// Split the return block if it is nontrivial.
if (!isTrivialReturnBlock(RetBB)) {
if (NewRetVal->getType().isVoid()) {
// Canonicalize Void return type into something that isTrivialReturnBlock
// expects.
auto *TupleI = dyn_cast<TupleInst>(RetInst->getOperand(0));
if (TupleI && TupleI->hasOneUse()) {
TupleI->moveBefore(RetInst);
} else {
Builder.setInsertionPoint(RetInst);
TupleI = Builder.createTuple(RetInst->getLoc(), {});
RetInst->setOperand(0, TupleI);
}
MergedBB = RetBB->split(TupleI->getIterator());
Builder.setInsertionPoint(RetBB);
Builder.createBranch(Loc, MergedBB);
} else {
// Forward the existing return argument to a new BBArg.
MergedBB = RetBB->split(RetInst->getIterator());
SILValue OldRetVal = RetInst->getOperand(0);
RetInst->setOperand(0, MergedBB->createPhiArgument(OldRetVal->getType(),
OwnershipKind::Owned));
Builder.setInsertionPoint(RetBB);
Builder.createBranch(Loc, MergedBB, {OldRetVal});
}
}
// Create a CFG edge from NewRetBB to MergedBB.
Builder.setInsertionPoint(NewRetBB);
SmallVector<SILValue, 1> BBArgs;
if (!NewRetVal->getType().isVoid())
BBArgs.push_back(NewRetVal);
Builder.createBranch(Loc, MergedBB, BBArgs);
// Then split any critical edges we created to the merged block.
splitCriticalEdgesTo(MergedBB);
}
/// Adds a CFG edge from the unterminated NewRetBB to a merged "return" block.
static void addReturnValue(SILBasicBlock *NewRetBB, SILBasicBlock *OldRetBB,
SILValue NewRetVal) {
auto *RetBB = OldRetBB;
addReturnValueImpl(RetBB, NewRetBB, NewRetVal);
}
/// Adds a CFG edge from the unterminated NewThrowBB to a merged "throw" block.
static void addThrowValue(SILBasicBlock *NewThrowBB, SILValue NewErrorVal) {
auto *ThrowBB = &*NewThrowBB->getParent()->findThrowBB();
addReturnValueImpl(ThrowBB, NewThrowBB, NewErrorVal);
}
/// Emits a call to a throwing function as defined by FuncRef, and passes the
/// specified Args. Uses the provided Builder to insert a try_apply at the given
/// SILLocation and generates control flow to handle the rethrow.
///
/// TODO: Move this to Utils.
static SILValue
emitApplyWithRethrow(SILBuilder &Builder, SILLocation Loc, SILValue FuncRef,
CanSILFunctionType CanSILFuncTy, SubstitutionMap Subs,
ArrayRef<SILValue> CallArgs,
ArrayRef<unsigned> CallArgIndicesThatNeedEndBorrow) {
auto &F = Builder.getFunction();
SILFunctionConventions fnConv(CanSILFuncTy, Builder.getModule());
SILBasicBlock *ErrorBB = F.createBasicBlock();
SILBasicBlock *NormalBB = F.createBasicBlock();
Builder.createTryApply(Loc, FuncRef, Subs, CallArgs, NormalBB, ErrorBB);
{
// Emit the rethrow logic.
Builder.emitBlock(ErrorBB);
SILValue Error = ErrorBB->createPhiArgument(
fnConv.getSILErrorType(F.getTypeExpansionContext()),
OwnershipKind::Owned);
cleanupCallArguments(Builder, Loc, CallArgs,
CallArgIndicesThatNeedEndBorrow);
addThrowValue(ErrorBB, Error);
}
// Advance Builder to the fall-thru path and return a SILArgument holding the
// result value.
Builder.clearInsertionPoint();
Builder.emitBlock(NormalBB);
SILValue finalArgument = Builder.getInsertionBB()->createPhiArgument(
fnConv.getSILResultType(F.getTypeExpansionContext()),
OwnershipKind::Owned);
cleanupCallArguments(Builder, Loc, CallArgs, CallArgIndicesThatNeedEndBorrow);
return finalArgument;
}
/// Emits code to invoke the specified specialized CalleeFunc using the
/// provided SILBuilder.
///
/// TODO: Move this to Utils.
static SILValue emitInvocation(SILBuilder &Builder,
const ReabstractionInfo &ReInfo, SILLocation Loc,
SILFunction *CalleeFunc,
ArrayRef<SILValue> CallArgs,
ArrayRef<unsigned> ArgsNeedEndBorrow) {
auto *FuncRefInst = Builder.createFunctionRef(Loc, CalleeFunc);
auto CanSILFuncTy = CalleeFunc->getLoweredFunctionType();
auto CalleeSubstFnTy = CanSILFuncTy;
SubstitutionMap Subs;
if (CanSILFuncTy->isPolymorphic()) {
// Create a substituted callee type.
assert(CanSILFuncTy == ReInfo.getSpecializedType() &&
"Types should be the same");
// We form here the list of substitutions and the substituted callee
// type. For specializations with layout constraints, we claim that
// the substitution T satisfies the specialized requirement
// 'TS : LayoutConstraint', where LayoutConstraint could be
// e.g. _Trivial(64). We claim it, because we ensure it by the
// method how this call is constructed.
// This is a hack and works currently just by coincidence.
// But it is not quite true from the SIL type system
// point of view as we do not really cast at the SIL level the original
// parameter value of type T into a more specialized generic
// type 'TS : LayoutConstraint'.
//
// TODO: Introduce a proper way to express such a cast.
// It could be an instruction similar to checked_cast_br, e.g.
// something like:
// 'checked_constraint_cast_br %1 : T to $opened("") <TS : _Trivial(64)>',
// where <TS: _Trivial(64)> introduces a new archetype with the given
// constraints.
if (ReInfo.getSpecializedType()->isPolymorphic()) {
Subs = ReInfo.getCallerParamSubstitutionMap();
CalleeSubstFnTy = CanSILFuncTy->substGenericArgs(
Builder.getModule(), ReInfo.getCallerParamSubstitutionMap(),
Builder.getTypeExpansionContext());
assert(!CalleeSubstFnTy->isPolymorphic() &&
"Substituted callee type should not be polymorphic");
assert(!CalleeSubstFnTy->hasTypeParameter() &&
"Substituted callee type should not have type parameters");
}
}
auto CalleeSILSubstFnTy = SILType::getPrimitiveObjectType(CalleeSubstFnTy);
SILFunctionConventions fnConv(CalleeSILSubstFnTy.castTo<SILFunctionType>(),
Builder.getModule());
bool isNonThrowing = false;
// It is a function whose type claims it is throwing, but
// it actually never throws inside its body?
if (CanSILFuncTy->hasErrorResult() &&
CalleeFunc->findThrowBB() == CalleeFunc->end()) {
isNonThrowing = true;
}
// Is callee a non-throwing function according to its type
// or de-facto?
if (!CanSILFuncTy->hasErrorResult() ||
CalleeFunc->findThrowBB() == CalleeFunc->end()) {
ApplyOptions Options;
if (isNonThrowing)
Options |= ApplyFlags::DoesNotThrow;
auto *AI = Builder.createApply(CalleeFunc->getLocation(), FuncRefInst, Subs,
CallArgs, Options);
cleanupCallArguments(Builder, Loc, CallArgs, ArgsNeedEndBorrow);
return AI;
}
return emitApplyWithRethrow(Builder, CalleeFunc->getLocation(), FuncRefInst,
CalleeSubstFnTy, Subs, CallArgs,
ArgsNeedEndBorrow);
}
/// Returns the thick metatype for the given SILType.
/// e.g. $*T -> $@thick T.Type
static SILType getThickMetatypeType(CanType Ty) {
auto SwiftTy = CanMetatypeType::get(Ty, MetatypeRepresentation::Thick);
return SILType::getPrimitiveObjectType(SwiftTy);
}
namespace {
/// Helper class for emitting code to dispatch to a specialized function.
class EagerDispatch {
SILFunction *GenericFunc;
const ReabstractionInfo &ReInfo;
const SILFunctionConventions substConv;
SILBuilder Builder;
SILLocation Loc;
// Function to check if a given object is a class.
SILFunction *IsClassF;
public:
// Instantiate a SILBuilder for inserting instructions at the top of the
// original generic function.
EagerDispatch(SILFunction *GenericFunc,
const ReabstractionInfo &ReInfo)
: GenericFunc(GenericFunc), ReInfo(ReInfo),
substConv(ReInfo.getSubstitutedType(), GenericFunc->getModule()),
Builder(*GenericFunc), Loc(GenericFunc->getLocation()) {
Builder.setCurrentDebugScope(GenericFunc->getDebugScope());
IsClassF = Builder.getModule().loadFunction(
"_swift_isClassOrObjCExistentialType", SILModule::LinkingMode::LinkAll);
assert(IsClassF);
}
void emitDispatchTo(SILFunction *NewFunc);
protected:
void emitTypeCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy);
void emitTrivialAndSizeCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy,
LayoutConstraint Layout);
void emitIsTrivialCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy,
LayoutConstraint Layout);
void emitRefCountedObjectCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy,
LayoutConstraint Layout);
void emitLayoutCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy);
SILValue emitArgumentCast(CanSILFunctionType CalleeSubstFnTy,
SILFunctionArgument *OrigArg, unsigned Idx);
SILValue
emitArgumentConversion(SmallVectorImpl<SILValue> &CallArgs,
SmallVectorImpl<unsigned> &ArgAtIndexNeedsEndBorrow);
};
} // end anonymous namespace
/// Inserts type checks in the original generic function for dispatching to the
/// given specialized function. Converts call arguments. Emits an invocation of
/// the specialized function. Handle the return value.
void EagerDispatch::emitDispatchTo(SILFunction *NewFunc) {
SILBasicBlock *OldReturnBB = nullptr;
auto ReturnBB = GenericFunc->findReturnBB();
if (ReturnBB != GenericFunc->end())
OldReturnBB = &*ReturnBB;
// 1. Emit a cascading sequence of type checks blocks.
// First split the entry BB, moving all instructions to the FailedTypeCheckBB.
auto &EntryBB = GenericFunc->front();
SILBasicBlock *FailedTypeCheckBB = EntryBB.split(EntryBB.begin());
Builder.setInsertionPoint(&EntryBB, EntryBB.begin());
// Iterate over all dependent types in the generic signature, which will match
// the specialized attribute's substitution list. Visit only
// SubstitutableTypes, skipping DependentTypes.
auto GenericSig =
GenericFunc->getLoweredFunctionType()->getInvocationGenericSignature();
auto SubMap = ReInfo.getClonerParamSubstitutionMap();
GenericSig->forEachParam([&](GenericTypeParamType *ParamTy, bool Canonical) {
if (!Canonical)
return;
auto Replacement = Type(ParamTy).subst(SubMap);
assert(!Replacement->hasTypeParameter());
if (!Replacement->hasArchetype()) {
// Dispatch on concrete type.
emitTypeCheck(FailedTypeCheckBB, ParamTy, Replacement);
} else if (auto Archetype = Replacement->getAs<ArchetypeType>()) {
// If Replacement has a layout constraint, then dispatch based
// on its size and the fact that it is trivial.
auto LayoutInfo = Archetype->getLayoutConstraint();
if (LayoutInfo && LayoutInfo->isTrivial()) {
// Emit a check that it is a trivial type of a certain size.
emitTrivialAndSizeCheck(FailedTypeCheckBB, ParamTy,
Replacement, LayoutInfo);
} else if (LayoutInfo && LayoutInfo->isRefCounted()) {
// Emit a check that it is an object of a reference counted type.
emitRefCountedObjectCheck(FailedTypeCheckBB, ParamTy,
Replacement, LayoutInfo);
}
}
});
static_cast<void>(FailedTypeCheckBB);
if (OldReturnBB == &EntryBB) {
OldReturnBB = FailedTypeCheckBB;
}
// 2. Convert call arguments, casting and adjusting for calling convention.
SmallVector<SILValue, 8> CallArgs;
SmallVector<unsigned, 8> ArgAtIndexNeedsEndBorrow;
SILValue StoreResultTo =
emitArgumentConversion(CallArgs, ArgAtIndexNeedsEndBorrow);
// 3. Emit an invocation of the specialized function.
// Emit any rethrow with no cleanup since all args have been forwarded and
// nothing has been locally allocated or copied.
SILValue Result = emitInvocation(Builder, ReInfo, Loc, NewFunc, CallArgs,
ArgAtIndexNeedsEndBorrow);
// 4. Handle the return value.
auto VoidTy = Builder.getModule().Types.getEmptyTupleType();
if (StoreResultTo) {
// Store the direct result to the original result address.
Builder.emitStoreValueOperation(Loc, Result, StoreResultTo,
StoreOwnershipQualifier::Init);
// And return Void.
Result = Builder.createTuple(Loc, VoidTy, { });
}
// Ensure that void return types original from a tuple instruction.
else if (Result->getType().isVoid())
Result = Builder.createTuple(Loc, VoidTy, { });
// Function marked as @NoReturn must be followed by 'unreachable'.
if (NewFunc->isNoReturnFunction(Builder.getTypeExpansionContext()) ||
!OldReturnBB)
Builder.createUnreachable(Loc);
else {
auto resultTy = GenericFunc->getConventions().getSILResultType(
Builder.getTypeExpansionContext());
auto GenResultTy = GenericFunc->mapTypeIntoContext(resultTy);
SILValue CastResult =
Builder.createUncheckedForwardingCast(Loc, Result, GenResultTy);
addReturnValue(Builder.getInsertionBB(), OldReturnBB, CastResult);
}
}
// Emits a type check in the current block.
// Advances the builder to the successful type check's block.
//
// Precondition: Builder's current insertion block is not terminated.
//
// Postcondition: Builder's insertion block is a new block that defines the
// specialized call argument and has not been terminated.
//
// The type check is emitted in the current block as:
// metatype $@thick T.Type
// %a = unchecked_bitwise_cast % to $Builtin.Int64
// metatype $@thick <Specialized>.Type
// %b = unchecked_bitwise_cast % to $Builtin.Int64
// builtin "cmp_eq_Int64"(%a : $Builtin.Int64, %b : $Builtin.Int64)
// : $Builtin.Int1
// cond_br %
void EagerDispatch::
emitTypeCheck(SILBasicBlock *FailedTypeCheckBB, SubstitutableType *ParamTy,
Type SubTy) {
// Instantiate a thick metatype for T.Type
auto ContextTy = GenericFunc->mapTypeIntoContext(ParamTy);
auto GenericMT = Builder.createMetatype(
Loc, getThickMetatypeType(ContextTy->getCanonicalType()));
// Instantiate a thick metatype for <Specialized>.Type
auto SpecializedMT = Builder.createMetatype(
Loc, getThickMetatypeType(SubTy->getCanonicalType()));
auto &Ctx = Builder.getASTContext();
auto WordTy = SILType::getBuiltinWordType(Ctx);
auto GenericMTVal =
Builder.createUncheckedBitwiseCast(Loc, GenericMT, WordTy);
auto SpecializedMTVal =
Builder.createUncheckedBitwiseCast(Loc, SpecializedMT, WordTy);
auto Cmp =
Builder.createBuiltinBinaryFunction(Loc, "cmp_eq", WordTy,
SILType::getBuiltinIntegerType(1, Ctx),
{GenericMTVal, SpecializedMTVal});
auto *SuccessBB = Builder.getFunction().createBasicBlock();
auto *FailBB = createSplitBranchTarget(FailedTypeCheckBB, Builder, Loc);
Builder.createCondBranch(Loc, Cmp, SuccessBB, FailBB);
Builder.emitBlock(SuccessBB);
}
static SubstitutionMap getSingleSubstitutionMap(SILFunction *F,
Type Ty) {
return SubstitutionMap::get(
F->getGenericEnvironment()->getGenericSignature(),
[&](SubstitutableType *type) { return Ty; },
MakeAbstractConformanceForGenericType());
}
void EagerDispatch::emitIsTrivialCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy, Type SubTy,
LayoutConstraint Layout) {
auto &Ctx = Builder.getASTContext();
// Instantiate a thick metatype for T.Type
auto ContextTy = GenericFunc->mapTypeIntoContext(ParamTy);
auto GenericMT = Builder.createMetatype(
Loc, getThickMetatypeType(ContextTy->getCanonicalType()));
auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx);
SubstitutionMap SubMap = getSingleSubstitutionMap(GenericFunc, ContextTy);
// Emit a check that it is a pod object.
auto IsPOD = Builder.createBuiltin(Loc, Ctx.getIdentifier("ispod"), BoolTy,
SubMap, {GenericMT});
auto *SuccessBB = Builder.getFunction().createBasicBlock();
auto *FailBB = createSplitBranchTarget(FailedTypeCheckBB, Builder, Loc);
Builder.createCondBranch(Loc, IsPOD, SuccessBB, FailBB);
Builder.emitBlock(SuccessBB);
}
void EagerDispatch::emitTrivialAndSizeCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy,
Type SubTy,
LayoutConstraint Layout) {
if (Layout->isAddressOnlyTrivial()) {
emitIsTrivialCheck(FailedTypeCheckBB, ParamTy, SubTy, Layout);
return;
}
auto &Ctx = Builder.getASTContext();
// Instantiate a thick metatype for T.Type
auto ContextTy = GenericFunc->mapTypeIntoContext(ParamTy);
auto GenericMT = Builder.createMetatype(
Loc, getThickMetatypeType(ContextTy->getCanonicalType()));
auto WordTy = SILType::getBuiltinWordType(Ctx);
auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx);
SubstitutionMap SubMap = getSingleSubstitutionMap(GenericFunc, ContextTy);
auto ParamSize = Builder.createBuiltin(Loc, Ctx.getIdentifier("sizeof"),
WordTy, SubMap, { GenericMT });
auto LayoutSize =
Builder.createIntegerLiteral(Loc, WordTy, Layout->getTrivialSizeInBytes());
const char *CmpOpName = Layout->isFixedSizeTrivial() ? "cmp_eq" : "cmp_le";
auto Cmp =
Builder.createBuiltinBinaryFunction(Loc, CmpOpName, WordTy,
BoolTy,
{ParamSize, LayoutSize});
auto *SuccessBB1 = Builder.getFunction().createBasicBlock();
auto *FailBB1 = createSplitBranchTarget(FailedTypeCheckBB, Builder, Loc);
Builder.createCondBranch(Loc, Cmp, SuccessBB1, FailBB1);
Builder.emitBlock(SuccessBB1);
// Emit a check that it is a pod object.
// TODO: Perform this check before all the fixed size checks!
auto IsPOD = Builder.createBuiltin(Loc, Ctx.getIdentifier("ispod"),
BoolTy, SubMap, { GenericMT });
auto *SuccessBB2 = Builder.getFunction().createBasicBlock();
auto *FailBB2 = createSplitBranchTarget(FailedTypeCheckBB, Builder, Loc);
Builder.createCondBranch(Loc, IsPOD, SuccessBB2, FailBB2);
Builder.emitBlock(SuccessBB2);
}
void EagerDispatch::emitRefCountedObjectCheck(SILBasicBlock *FailedTypeCheckBB,
SubstitutableType *ParamTy,
Type SubTy,
LayoutConstraint Layout) {
auto &Ctx = Builder.getASTContext();
// Instantiate a thick metatype for T.Type
auto ContextTy = GenericFunc->mapTypeIntoContext(ParamTy);
auto GenericMT = Builder.createMetatype(
Loc, getThickMetatypeType(ContextTy->getCanonicalType()));
auto Int8Ty = SILType::getBuiltinIntegerType(8, Ctx);
auto BoolTy = SILType::getBuiltinIntegerType(1, Ctx);
SubstitutionMap SubMap = getSingleSubstitutionMap(GenericFunc, ContextTy);
// Emit a check that it is a reference-counted object.
// TODO: Perform this check before all fixed size checks.
// FIXME: What builtin do we use to check it????
auto CanBeClass = Builder.createBuiltin(
Loc, Ctx.getIdentifier("canBeClass"), Int8Ty, SubMap, {GenericMT});
auto ClassConst =
Builder.createIntegerLiteral(Loc, Int8Ty, 1);
auto Cmp1 =
Builder.createBuiltinBinaryFunction(Loc, "cmp_eq", Int8Ty,
BoolTy,
{CanBeClass, ClassConst});
auto *SuccessBB = Builder.getFunction().createBasicBlock();
auto *MayBeClassCheckBB = Builder.getFunction().createBasicBlock();
auto *SwiftClassBB = createSplitBranchTarget(SuccessBB, Builder, Loc);
Builder.createCondBranch(Loc, Cmp1, SwiftClassBB, MayBeClassCheckBB);
Builder.emitBlock(MayBeClassCheckBB);
auto MayBeClassConst =
Builder.createIntegerLiteral(Loc, Int8Ty, 2);
auto Cmp2 =
Builder.createBuiltinBinaryFunction(Loc, "cmp_eq", Int8Ty,
BoolTy,
{CanBeClass, MayBeClassConst});
auto *IsClassCheckBB = Builder.getFunction().createBasicBlock();
auto *FailClassCheckBB =
createSplitBranchTarget(FailedTypeCheckBB, Builder, Loc);
Builder.createCondBranch(Loc, Cmp2, IsClassCheckBB, FailClassCheckBB);
Builder.emitBlock(IsClassCheckBB);
auto *FRI = Builder.createFunctionRef(Loc, IsClassF);
auto IsClassRuntimeCheck = Builder.createApply(Loc, FRI, SubMap,
{GenericMT});
// Extract the i1 from the Bool struct.
StructDecl *BoolStruct = cast<StructDecl>(Ctx.getBoolDecl());
auto Members = BoolStruct->getStoredProperties();
assert(Members.size() == 1 &&
"Bool should have only one property with name '_value'");
auto Member = Members[0];
auto BoolValue =
Builder.emitStructExtract(Loc, IsClassRuntimeCheck, Member, BoolTy);
auto *FailBB = createSplitBranchTarget(FailedTypeCheckBB, Builder, Loc);
auto *ObjCOrExistentialBB = createSplitBranchTarget(SuccessBB, Builder, Loc);
Builder.createCondBranch(Loc, BoolValue, ObjCOrExistentialBB, FailBB);
Builder.emitBlock(SuccessBB);
}
/// Cast a generic argument to its specialized type.
SILValue EagerDispatch::emitArgumentCast(CanSILFunctionType CalleeSubstFnTy,
SILFunctionArgument *OrigArg,
unsigned Idx) {
SILFunctionConventions substConv(CalleeSubstFnTy,
Builder.getModule());
auto CastTy =
substConv.getSILArgumentType(Idx, Builder.getTypeExpansionContext());
assert(CastTy.isAddress()
== (OrigArg->isIndirectResult()
|| substConv.isSILIndirect(OrigArg->getKnownParameterInfo()))
&& "bad arg type");
if (CastTy.isAddress())
return Builder.createUncheckedAddrCast(Loc, OrigArg, CastTy);
return Builder.createUncheckedForwardingCast(Loc, OrigArg, CastTy);
}
/// Converts each generic function argument into a SILValue that can be passed
/// to the specialized call by emitting a cast followed by a load.
///
/// Populates the CallArgs with the converted arguments.
///
/// Returns the SILValue to store the result into if the specialized function
/// has a direct result.
SILValue EagerDispatch::emitArgumentConversion(
SmallVectorImpl<SILValue> &CallArgs,
SmallVectorImpl<unsigned> &ArgAtIndexNeedsEndBorrow) {
auto OrigArgs = GenericFunc->begin()->getSILFunctionArguments();
assert(OrigArgs.size() == substConv.getNumSILArguments()
&& "signature mismatch");
// Create a substituted callee type.
auto SubstitutedType = ReInfo.getSubstitutedType();
auto SpecializedType = ReInfo.getSpecializedType();
auto CanSILFuncTy = SubstitutedType;
auto CalleeSubstFnTy = CanSILFuncTy;
if (CanSILFuncTy->isPolymorphic()) {
CalleeSubstFnTy = CanSILFuncTy->substGenericArgs(
Builder.getModule(), ReInfo.getCallerParamSubstitutionMap(),
Builder.getTypeExpansionContext());
assert(!CalleeSubstFnTy->isPolymorphic() &&
"Substituted callee type should not be polymorphic");
assert(!CalleeSubstFnTy->hasTypeParameter() &&
"Substituted callee type should not have type parameters");
SubstitutedType = CalleeSubstFnTy;
SpecializedType =
ReInfo.createSpecializedType(SubstitutedType, Builder.getModule());
}
assert(!substConv.useLoweredAddresses()
|| OrigArgs.size() == ReInfo.getNumArguments() &&
"signature mismatch");
CallArgs.reserve(OrigArgs.size());
SILValue StoreResultTo;
for (auto *OrigArg : OrigArgs) {
unsigned ArgIdx = OrigArg->getIndex();
auto CastArg = emitArgumentCast(SubstitutedType, OrigArg, ArgIdx);
LLVM_DEBUG(llvm::dbgs() << " Cast generic arg: ";
CastArg->print(llvm::dbgs()));
if (!substConv.useLoweredAddresses()) {
CallArgs.push_back(CastArg);
continue;
}
if (ArgIdx < substConv.getSILArgIndexOfFirstParam()) {
// Handle result arguments.
unsigned formalIdx =
substConv.getIndirectFormalResultIndexForSILArg(ArgIdx);
if (ReInfo.isFormalResultConverted(formalIdx)) {
// The result is converted from indirect to direct. We need to insert
// a store later.
assert(!StoreResultTo);
StoreResultTo = CastArg;
continue;
}
CallArgs.push_back(CastArg);
continue;
}
// Handle arguments for formal parameters.
unsigned paramIdx = ArgIdx - substConv.getSILArgIndexOfFirstParam();
if (!ReInfo.isParamConverted(paramIdx)) {
CallArgs.push_back(CastArg);
continue;
}
// An argument is converted from indirect to direct. Instead of the
// address we pass the loaded value.
//
// FIXME: If type of CastArg is an archetype, but it is loadable because
// of a layout constraint on the caller side, we have a problem here
// We need to load the value on the caller side, but this archetype is
// not statically known to be loadable on the caller side (though we
// have proven dynamically that it has a fixed size).
//
// We can try to load it as an int value of width N, but then it is not
// clear how to convert it into a value of the archetype type, which is
// expected. May be we should pass it as @in parameter and make it
// loadable on the caller's side?
auto argConv = substConv.getSILArgumentConvention(ArgIdx);
SILValue Val;
if (!argConv.isGuaranteedConventionInCaller()) {
Val = Builder.emitLoadValueOperation(Loc, CastArg,
LoadOwnershipQualifier::Take);
} else {
Val = Builder.emitLoadBorrowOperation(Loc, CastArg);
if (Val->getOwnershipKind() == OwnershipKind::Guaranteed)
ArgAtIndexNeedsEndBorrow.push_back(CallArgs.size());
}
CallArgs.push_back(Val);
}
return StoreResultTo;
}
namespace {
class EagerSpecializerTransform : public SILFunctionTransform {
const bool onlyCreatePrespecializations;
public:
EagerSpecializerTransform(bool onlyPrespecialize)
: onlyCreatePrespecializations(onlyPrespecialize) {}
void run() override;
};
} // end anonymous namespace
/// Specializes a generic function for a concrete type list.
static SILFunction *eagerSpecialize(SILOptFunctionBuilder &FuncBuilder,
SILFunction *GenericFunc,
const SILSpecializeAttr &SA,
const ReabstractionInfo &ReInfo,
SmallVectorImpl<SILFunction *> &newFunctions) {
assert(ReInfo.getSpecializedType());
LLVM_DEBUG(llvm::dbgs() << "Specializing " << GenericFunc->getName() << "\n");
LLVM_DEBUG(auto FT = GenericFunc->getLoweredFunctionType();
llvm::dbgs() << " Generic Sig:"; llvm::dbgs().indent(2);
FT->getInvocationGenericSignature()->print(llvm::dbgs());
llvm::dbgs() << " Generic Env:"; llvm::dbgs().indent(2);
GenericFunc->getGenericEnvironment()->dump(llvm::dbgs());
llvm::dbgs() << " Specialize Attr:"; SA.print(llvm::dbgs());
llvm::dbgs() << "\n");
GenericFuncSpecializer
FuncSpecializer(FuncBuilder, GenericFunc,
ReInfo.getClonerParamSubstitutionMap(),
ReInfo);
SILFunction *NewFunc = FuncSpecializer.lookupSpecialization();
if (!NewFunc) {
NewFunc = FuncSpecializer.tryCreateSpecialization(
true /*forcePrespecialization*/);
if (NewFunc)
newFunctions.push_back(NewFunc);
}
if (!NewFunc) {
LLVM_DEBUG(llvm::dbgs() << " Failed. Cannot specialize function.\n");
} else if (SA.isExported()) {
NewFunc->setLinkage(NewFunc->isDefinition() ? SILLinkage::Public
: SILLinkage::PublicExternal);
NewFunc->setAvailabilityForLinkage(SA.getAvailability());
}
return NewFunc;
}
/// Run the pass.
void EagerSpecializerTransform::run() {
if (!EagerSpecializeFlag)
return;
SILOptFunctionBuilder FuncBuilder(*this);
auto &F = *getFunction();
// Process functions in any order.
if (!F.shouldOptimize() && !onlyCreatePrespecializations) {
LLVM_DEBUG(llvm::dbgs() << " Cannot specialize function " << F.getName()
<< " because it is marked to be "
"excluded from optimizations.\n");
return;
}
// Only specialize functions in their home module.
if (F.isExternalDeclaration() || F.isAvailableExternally())
return;
if (F.isDynamicallyReplaceable())
return;
if (!F.getLoweredFunctionType()->getInvocationGenericSignature())
return;
// Create a specialized function with ReabstractionInfo for each attribute.
SmallVector<SILFunction *, 8> SpecializedFuncs;
SmallVector<ReabstractionInfo, 4> ReInfoVec;
ReInfoVec.reserve(F.getSpecializeAttrs().size());
SmallVector<SILFunction *, 8> newFunctions;
// TODO: Use a decision-tree to reduce the amount of dynamic checks being
// performed.
SmallVector<SILSpecializeAttr *, 8> attrsToRemove;
bool onlyCreatePrespecializations = this->onlyCreatePrespecializations;
for (auto *SA : F.getSpecializeAttrs()) {
if (onlyCreatePrespecializations && !SA->isExported()) {
attrsToRemove.push_back(SA);
continue;
}
auto *targetFunc = &F;
// If the _specialize attribute specifies another target function use it
// instead to specialize.
if (SA->getTargetFunction()) {
targetFunc = SA->getTargetFunction();
if (!targetFunc->isDefinition()) {
auto &module = FuncBuilder.getModule();
bool success = module.loadFunction(targetFunc,
SILModule::LinkingMode::LinkAll);
assert(success);
}
onlyCreatePrespecializations = true;
} else if (targetFunc->getLinkage() == SILLinkage::Shared) {
// We have `shared` linkage if we deserialize a public serialized
// function.
// That means we are loading it from another module. In this case, we
// don't want to create a pre-specialization.
SpecializedFuncs.push_back(nullptr);
ReInfoVec.emplace_back(ReabstractionInfo(F.getModule()));
continue;
}
ReInfoVec.emplace_back(FuncBuilder.getModule().getSwiftModule(),
FuncBuilder.getModule().isWholeModule(), targetFunc,
SA->getSpecializedSignature(), SA->isExported());
auto *NewFunc = eagerSpecialize(FuncBuilder, targetFunc, *SA,
ReInfoVec.back(), newFunctions);
SpecializedFuncs.push_back(
(onlyCreatePrespecializations ||
SA->isExported() /*currently we don't handle coroutines in emitDispatchTo*/)
? nullptr
: NewFunc);
if (!SA->isExported())
attrsToRemove.push_back(SA);
}
// TODO: Optimize the dispatch code to minimize the amount
// of checks. Use decision trees for this purpose.
bool Changed = false;
CalleeCache *calleeCache = getAnalysis<BasicCalleeAnalysis>()->getCalleeCache();
if (!onlyCreatePrespecializations)
for_each3(F.getSpecializeAttrs(), SpecializedFuncs, ReInfoVec,
[&](const SILSpecializeAttr *SA, SILFunction *NewFunc,
const ReabstractionInfo &ReInfo) {
if (NewFunc) {
NewFunc->verify(calleeCache);
Changed = true;
EagerDispatch(&F, ReInfo).emitDispatchTo(NewFunc);
}
});
// Invalidate everything since we delete calls as well as add new
// calls and branches.
if (Changed) {
invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);
}
// As specializations are created, the non-exported attributes should be
// removed.
for (auto *SA : attrsToRemove)
F.removeSpecializeAttr(SA);
// If any specializations were created, reverify the original body now that it
// has checks.
if (!newFunctions.empty())
F.verify(calleeCache);
for (SILFunction *newF : newFunctions) {
addFunctionToPassManagerWorklist(newF, nullptr);
}
}
SILTransform *swift::createEagerSpecializer() {
return new EagerSpecializerTransform(false/*onlyCreatePrespecializations*/);
}
SILTransform *swift::createOnonePrespecializations() {
return new EagerSpecializerTransform(true /*onlyCreatePrespecializations*/);
}