Skip to content

Commit a050194

Browse files
authored
Merge pull request #65119 from slavapestov/sil-optimizer-peepholes-for-packs
Simple SILOptimizer peepholes for packs
2 parents 8f2f9d9 + fa28d14 commit a050194

File tree

13 files changed

+405
-7
lines changed

13 files changed

+405
-7
lines changed

lib/AST/SubstitutionMap.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "swift/AST/InFlightSubstitution.h"
3232
#include "swift/AST/LazyResolver.h"
3333
#include "swift/AST/Module.h"
34+
#include "swift/AST/PackConformance.h"
3435
#include "swift/AST/ProtocolConformance.h"
3536
#include "swift/AST/TypeCheckRequests.h"
3637
#include "swift/AST/Types.h"
@@ -792,13 +793,29 @@ bool SubstitutionMap::isIdentity() const {
792793
if (empty())
793794
return true;
794795

796+
for (auto conf : getConformances()) {
797+
if (conf.isAbstract())
798+
continue;
799+
800+
if (conf.isPack()) {
801+
auto patternConfs = conf.getPack()->getPatternConformances();
802+
if (patternConfs.size() == 1 && patternConfs[0].isAbstract())
803+
continue;
804+
}
805+
806+
return false;
807+
}
808+
795809
GenericSignature sig = getGenericSignature();
796810
bool hasNonIdentityReplacement = false;
797811
auto replacements = getReplacementTypesBuffer();
798812

799813
sig->forEachParam([&](GenericTypeParamType *paramTy, bool isCanonical) {
800814
if (isCanonical) {
801-
if (!paramTy->isEqual(replacements[0]))
815+
Type wrappedParamTy = paramTy;
816+
if (paramTy->isParameterPack())
817+
wrappedParamTy = PackType::getSingletonPackExpansion(paramTy);
818+
if (!wrappedParamTy->isEqual(replacements[0]))
802819
hasNonIdentityReplacement = true;
803820
}
804821

lib/IRGen/Fulfillment.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ static CanType getSingletonPackExpansionParameter(CanPackType packType,
188188
return expansion.getPatternType();
189189
}
190190
}
191+
192+
return CanType();
191193
}
192194

193195
bool FulfillmentMap::searchTypeMetadataPack(IRGenModule &IGM,

lib/IRGen/GenReflection.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,10 +490,8 @@ IRGenModule::emitWitnessTableRefString(CanType type,
490490
type = genericEnv->mapTypeIntoContext(type)->getCanonicalType();
491491
}
492492
if (origType->hasTypeParameter()) {
493-
auto origSig = genericEnv->getGenericSignature();
494493
conformance = conformance.subst(origType,
495-
QueryInterfaceTypeSubstitutions(genericEnv),
496-
LookUpConformanceInSignature(origSig.getPointer()));
494+
genericEnv->getForwardingSubstitutionMap());
497495
}
498496
auto ret = emitWitnessTableRef(IGF, type, conformance);
499497
IGF.Builder.CreateRet(ret);

lib/SIL/IR/SILInstruction.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,22 @@ namespace {
881881
return true;
882882
}
883883

884+
bool visitScalarPackIndexInst(const ScalarPackIndexInst *RHS) {
885+
auto *X = cast<ScalarPackIndexInst>(LHS);
886+
return (X->getIndexedPackType() == RHS->getIndexedPackType() &&
887+
X->getComponentIndex() == RHS->getComponentIndex());
888+
}
889+
890+
bool visitDynamicPackIndexInst(const DynamicPackIndexInst *RHS) {
891+
auto *X = cast<DynamicPackIndexInst>(LHS);
892+
return X->getIndexedPackType() == RHS->getIndexedPackType();
893+
}
894+
895+
bool visitTuplePackElementAddrInst(const TuplePackElementAddrInst *RHS) {
896+
auto *X = cast<TuplePackElementAddrInst>(LHS);
897+
return X->getElementType() == RHS->getElementType();
898+
}
899+
884900
private:
885901
const SILInstruction *LHS;
886902
};

lib/SILOptimizer/LoopTransforms/LoopUnroll.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,21 @@ static Optional<uint64_t> getMaxLoopTripCount(SILLoop *Loop,
163163

164164
SILValue RecNext = Cmp->getArguments()[0];
165165
SILPhiArgument *RecArg;
166+
167+
// Match signed add with overflow, unsigned add with overflow and
168+
// add without overflow.
166169
if (!match(RecNext, m_TupleExtractOperation(
167170
m_ApplyInst(BuiltinValueKind::SAddOver,
168171
m_SILPhiArgument(RecArg), m_One()),
169-
0)))
172+
0)) &&
173+
!match(RecNext, m_TupleExtractOperation(
174+
m_ApplyInst(BuiltinValueKind::UAddOver,
175+
m_SILPhiArgument(RecArg), m_One()),
176+
0)) &&
177+
!match(RecNext, m_ApplyInst(BuiltinValueKind::Add,
178+
m_SILPhiArgument(RecArg), m_One()))) {
170179
return None;
180+
}
171181

172182
if (RecArg->getParent() != Header)
173183
return None;

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ class SILCombiner :
300300
SILInstruction *
301301
visitDifferentiableFunctionExtractInst(DifferentiableFunctionExtractInst *DFEI);
302302

303+
SILInstruction *visitPackLengthInst(PackLengthInst *PLI);
304+
SILInstruction *visitPackElementGetInst(PackElementGetInst *PEGI);
305+
SILInstruction *visitTuplePackElementAddrInst(TuplePackElementAddrInst *TPEAI);
306+
SILInstruction *visitCopyAddrInst(CopyAddrInst *CAI);
307+
303308
SILInstruction *legacyVisitGlobalValueInst(GlobalValueInst *globalValue);
304309

305310
#define PASS(ID, TAG, DESCRIPTION)

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2391,3 +2391,136 @@ SILCombiner::visitDifferentiableFunctionExtractInst(DifferentiableFunctionExtrac
23912391
replaceInstUsesWith(*DFEI, newValue);
23922392
return eraseInstFromFunction(*DFEI);
23932393
}
2394+
2395+
// Simplify `pack_length` with constant-length pack.
2396+
//
2397+
// Before:
2398+
// %len = pack_length $Pack{Int, String, Float}
2399+
//
2400+
// After:
2401+
// %len = integer_literal Builtin.Word, 3
2402+
SILInstruction *SILCombiner::visitPackLengthInst(PackLengthInst *PLI) {
2403+
auto PackTy = PLI->getPackType();
2404+
if (!PackTy->containsPackExpansionType()) {
2405+
return Builder.createIntegerLiteral(PLI->getLoc(), PLI->getType(),
2406+
PackTy->getNumElements());
2407+
}
2408+
2409+
return nullptr;
2410+
}
2411+
2412+
// Simplify `pack_element_get` where the index is a `dynamic_pack_index` with
2413+
// a constant operand.
2414+
//
2415+
// Before:
2416+
// %idx = integer_literal Builtin.Word, N
2417+
// %pack_idx = dynamic_pack_index %Pack{Int, String, Float}, %idx
2418+
// %pack_elt = pack_element_get %pack_value, %pack_idx, @element("...")
2419+
//
2420+
// After:
2421+
// %pack_idx = scalar_pack_index %Pack{Int, String, Float}, N
2422+
// %concrete_elt = pack_element_get %pack_value, %pack_idx, <<concrete type>>
2423+
// %pack_elt = unchecked_addr_cast %concrete_elt, @element("...")
2424+
SILInstruction *SILCombiner::visitPackElementGetInst(PackElementGetInst *PEGI) {
2425+
auto *DPII = dyn_cast<DynamicPackIndexInst>(PEGI->getIndex());
2426+
if (DPII == nullptr)
2427+
return nullptr;
2428+
2429+
auto PackTy = PEGI->getPackType();
2430+
if (PackTy->containsPackExpansionType())
2431+
return nullptr;
2432+
2433+
auto *Op = dyn_cast<IntegerLiteralInst>(DPII->getOperand());
2434+
if (Op == nullptr)
2435+
return nullptr;
2436+
2437+
if (Op->getValue().uge(PackTy->getNumElements()))
2438+
return nullptr;
2439+
2440+
unsigned Index = Op->getValue().getZExtValue();
2441+
auto *SPII = Builder.createScalarPackIndex(
2442+
DPII->getLoc(), Index, DPII->getIndexedPackType());
2443+
2444+
auto ElementTy = SILType::getPrimitiveAddressType(
2445+
PEGI->getPackType().getElementType(Index));
2446+
auto *NewPEGI = Builder.createPackElementGet(
2447+
PEGI->getLoc(), SPII, PEGI->getPack(),
2448+
ElementTy);
2449+
2450+
return Builder.createUncheckedAddrCast(
2451+
PEGI->getLoc(), NewPEGI, PEGI->getElementType());
2452+
}
2453+
2454+
// Simplify `tuple_pack_element_addr` where the index is a `dynamic_pack_index`
2455+
//with a constant operand.
2456+
//
2457+
// Before:
2458+
// %idx = integer_literal Builtin.Word, N
2459+
// %pack_idx = dynamic_pack_index %Pack{Int, String, Float}, %idx
2460+
// %tuple_elt = tuple_pack_element_addr %tuple_value, %pack_idx, @element("...")
2461+
//
2462+
// After:
2463+
// %concrete_elt = tuple_element_addr %tuple_value, N
2464+
// %tuple_elt = unchecked_addr_cast %concrete_elt, @element("...")
2465+
SILInstruction *
2466+
SILCombiner::visitTuplePackElementAddrInst(TuplePackElementAddrInst *TPEAI) {
2467+
auto *DPII = dyn_cast<DynamicPackIndexInst>(TPEAI->getIndex());
2468+
if (DPII == nullptr)
2469+
return nullptr;
2470+
2471+
auto PackTy = DPII->getIndexedPackType();
2472+
if (PackTy->containsPackExpansionType())
2473+
return nullptr;
2474+
2475+
auto *Op = dyn_cast<IntegerLiteralInst>(DPII->getOperand());
2476+
if (Op == nullptr)
2477+
return nullptr;
2478+
2479+
if (Op->getValue().uge(PackTy->getNumElements()))
2480+
return nullptr;
2481+
2482+
unsigned Index = Op->getValue().getZExtValue();
2483+
2484+
auto *TEAI = Builder.createTupleElementAddr(
2485+
TPEAI->getLoc(), TPEAI->getTuple(), Index);
2486+
return Builder.createUncheckedAddrCast(
2487+
TPEAI->getLoc(), TEAI, TPEAI->getElementType());
2488+
}
2489+
2490+
// This is a hack. When optimizing a simple pack expansion expression which
2491+
// forms a tuple from a pack, like `(repeat each t)`, after the above
2492+
// peepholes we end up with:
2493+
//
2494+
// %src = unchecked_addr_cast %real_src, @element("...")
2495+
// %dst = unchecked_addr_cast %real_dst, @element("...")
2496+
// copy_addr %src, %dst
2497+
//
2498+
// Simplify this to
2499+
//
2500+
// copy_addr %real_src, %real_dst
2501+
//
2502+
// Assuming that %real_src and %real_dst have the same type.
2503+
//
2504+
// In this simple case, this eliminates the opened element archetype entirely.
2505+
// However, a more principled peephole would be to transform an
2506+
// open_pack_element with a scalar index by replacing all usages of the
2507+
// element archetype with a concrete type.
2508+
SILInstruction *
2509+
SILCombiner::visitCopyAddrInst(CopyAddrInst *CAI) {
2510+
auto *Src = dyn_cast<UncheckedAddrCastInst>(CAI->getSrc());
2511+
auto *Dst = dyn_cast<UncheckedAddrCastInst>(CAI->getDest());
2512+
2513+
if (Src == nullptr || Dst == nullptr)
2514+
return nullptr;
2515+
2516+
if (Src->getType() != Dst->getType() ||
2517+
!Src->getType().is<ElementArchetypeType>())
2518+
return nullptr;
2519+
2520+
if (Src->getOperand()->getType() != Dst->getOperand()->getType())
2521+
return nullptr;
2522+
2523+
return Builder.createCopyAddr(
2524+
CAI->getLoc(), Src->getOperand(), Dst->getOperand(),
2525+
CAI->isTakeOfSrc(), CAI->isInitializationOfDest());
2526+
}

lib/SILOptimizer/Transforms/CSE.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,24 @@ class HashVisitor : public SILInstructionVisitor<HashVisitor, llvm::hash_code> {
515515
X->getKind(), tryLookThroughOwnershipInsts(&X->getOperandRef()),
516516
llvm::hash_combine_range(ConformsTo.begin(), ConformsTo.end()));
517517
}
518+
519+
hash_code visitScalarPackIndexInst(ScalarPackIndexInst *X) {
520+
return llvm::hash_combine(
521+
X->getKind(), X->getIndexedPackType(), X->getComponentIndex());
522+
}
523+
524+
hash_code visitDynamicPackIndexInst(DynamicPackIndexInst *X) {
525+
return llvm::hash_combine(
526+
X->getKind(), X->getIndexedPackType(), &X->getOperandRef());
527+
}
528+
529+
hash_code visitTuplePackElementAddrInst(TuplePackElementAddrInst *X) {
530+
OperandValueArrayRef Operands(X->getAllOperands());
531+
return llvm::hash_combine(
532+
X->getKind(),
533+
llvm::hash_combine_range(Operands.begin(), Operands.end()),
534+
X->getElementType());
535+
}
518536
};
519537
} // end anonymous namespace
520538

@@ -1225,6 +1243,9 @@ bool CSE::canHandle(SILInstruction *Inst) {
12251243
case SILInstructionKind::MarkDependenceInst:
12261244
case SILInstructionKind::InitExistentialMetatypeInst:
12271245
case SILInstructionKind::WitnessMethodInst:
1246+
case SILInstructionKind::ScalarPackIndexInst:
1247+
case SILInstructionKind::DynamicPackIndexInst:
1248+
case SILInstructionKind::TuplePackElementAddrInst:
12281249
// Intentionally we don't handle (prev_)dynamic_function_ref.
12291250
// They change at runtime.
12301251
#define LOADABLE_REF_STORAGE(Name, ...) \

lib/SILOptimizer/Utils/ConstantFolding.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,8 +588,31 @@ static SILValue constantFoldBinary(BuiltinInst *BI,
588588
// Are there valid uses for these in stdlib?
589589
case BuiltinValueKind::Add:
590590
case BuiltinValueKind::Mul:
591-
case BuiltinValueKind::Sub:
592-
return nullptr;
591+
case BuiltinValueKind::Sub: {
592+
OperandValueArrayRef Args = BI->getArguments();
593+
auto *LHS = dyn_cast<IntegerLiteralInst>(Args[0]);
594+
auto *RHS = dyn_cast<IntegerLiteralInst>(Args[1]);
595+
if (!RHS || !LHS)
596+
return nullptr;
597+
APInt LHSI = LHS->getValue();
598+
APInt RHSI = RHS->getValue();
599+
600+
switch (ID) {
601+
default: llvm_unreachable("Not all cases are covered!");
602+
case BuiltinValueKind::Add:
603+
LHSI += RHSI;
604+
break;
605+
case BuiltinValueKind::Mul:
606+
LHSI *= RHSI;
607+
break;
608+
case BuiltinValueKind::Sub:
609+
LHSI -= RHSI;
610+
break;
611+
}
612+
613+
SILBuilderWithScope B(BI);
614+
return B.createIntegerLiteral(BI->getLoc(), BI->getType(), LHSI);
615+
}
593616

594617
case BuiltinValueKind::And:
595618
case BuiltinValueKind::AShr:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-frontend -emit-ir %s -enable-experimental-feature VariadicGenerics -disable-availability-checking
2+
3+
// Because of -enable-experimental-feature VariadicGenerics
4+
// REQUIRES: asserts
5+
6+
// FIXME: Add more tests
7+
8+
public protocol P {}
9+
10+
public struct G<each T>: P {}
11+
12+
public func returnsG<each T>(_ t: repeat each T) -> some P {
13+
return G<repeat each T>()
14+
}

test/SILOptimizer/constant_propagation.sil

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,25 @@ bb0:
102102
// CHECK-NEXT: return [[RES]] : $Builtin.Int64
103103
}
104104

105+
// Compute an expression using a chain of arithmetic with non-overflowing instructions: 2 * (2 + 3) - 3
106+
sil @fold_arithmetic_without_overflow : $@convention(thin) () -> Builtin.Int64 {
107+
bb0:
108+
%0 = integer_literal $Builtin.Int64, 2
109+
%110 = integer_literal $Builtin.Int64, 3
110+
%19 = builtin "add_Int64"(%0 : $Builtin.Int64, %110 : $Builtin.Int64) : $Builtin.Int64
111+
%21 = builtin "mul_Int64"(%0 : $Builtin.Int64, %19 : $Builtin.Int64) : $Builtin.Int64
112+
%23 = builtin "sub_Int64"(%21 : $Builtin.Int64, %110 : $Builtin.Int64) : $Builtin.Int64
113+
return %23 : $Builtin.Int64
114+
115+
// CHECK-LABEL: sil @fold_arithmetic_without_overflow
116+
// CHECK-NOT: integer_literal $Builtin.Int64, 2
117+
// CHECK-NOT: integer_literal $Builtin.Int64, 3
118+
// CHECK-NOT: integer_literal $Builtin.Int64, 0
119+
// CHECK-NOT: builtin
120+
// CHECK: [[RES:%.*]] = integer_literal $Builtin.Int64, 7
121+
// CHECK-NEXT: return [[RES]] : $Builtin.Int64
122+
}
123+
105124
// Fold casts. (This test assumes that DCE does not run, otherwise the unreachable blocks will get removed.)
106125
sil @fold_trunc : $@convention(thin) () -> Builtin.Int64 {
107126
bb0:

0 commit comments

Comments
 (0)