Skip to content

Commit aa6e600

Browse files
authored
Merge pull request #33282 from eeckstein/string-optimization
SILOptimizer: move String concatination optimization from SILCombine/ConstantFolding to StringOptimization.
2 parents b2d2cf0 + 63c275c commit aa6e600

File tree

8 files changed

+132
-323
lines changed

8 files changed

+132
-323
lines changed

include/swift/SILOptimizer/Utils/ConstantFolding.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ class ConstantFolder {
7070
/// Called for each constant folded instruction.
7171
std::function<void (SILInstruction *)> Callback;
7272

73-
bool constantFoldStringConcatenation(ApplyInst *AI);
74-
7573
public:
7674
/// The constructor.
7775
///

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,6 @@ TermInst *addArgumentToBranch(SILValue val, SILBasicBlock *dest,
299299
/// the given linkage.
300300
SILLinkage getSpecializedLinkage(SILFunction *f, SILLinkage linkage);
301301

302-
/// Tries to optimize a given apply instruction if it is a concatenation of
303-
/// string literals. Returns a new instruction if optimization was possible.
304-
SingleValueInstruction *tryToConcatenateStrings(ApplyInst *ai,
305-
SILBuilder &builder);
306-
307302
/// Tries to perform jump-threading on all checked_cast_br instruction in
308303
/// function \p Fn.
309304
bool tryCheckedCastBrJumpThreading(

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -654,12 +654,6 @@ bool SILCombiner::eraseApply(FullApplySite FAS, const UserListTy &Users) {
654654
return true;
655655
}
656656

657-
SILInstruction *
658-
SILCombiner::optimizeConcatenationOfStringLiterals(ApplyInst *AI) {
659-
// String literals concatenation optimizer.
660-
return tryToConcatenateStrings(AI, Builder);
661-
}
662-
663657
/// This routine replaces the old witness method inst with a new one.
664658
void SILCombiner::replaceWitnessMethodInst(
665659
WitnessMethodInst *WMI, SILBuilderContext &BuilderCtx, CanType ConcreteType,
@@ -1464,12 +1458,6 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
14641458
}
14651459

14661460
if (SF) {
1467-
if (SF->getEffectsKind() < EffectsKind::ReleaseNone) {
1468-
// Try to optimize string concatenation.
1469-
if (auto I = optimizeConcatenationOfStringLiterals(AI)) {
1470-
return I;
1471-
}
1472-
}
14731461
if (SF->hasSemanticsAttr(semantics::ARRAY_UNINITIALIZED)) {
14741462
UserListTy Users;
14751463
// If the uninitialized array is only written into then it can be removed.

lib/SILOptimizer/Transforms/StringOptimization.cpp

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class StringOptimization {
9090

9191
bool optimizeStringAppend(ApplyInst *appendCall,
9292
llvm::DenseMap<SILValue, SILValue> &storedStrings);
93-
93+
bool optimizeStringConcat(ApplyInst *concatCall);
9494
bool optimizeTypeName(ApplyInst *typeNameCall);
9595

9696
static ApplyInst *isSemanticCall(SILInstruction *inst, StringRef attr,
@@ -102,8 +102,8 @@ class StringOptimization {
102102
static StringInfo getStringFromStaticLet(SILValue value);
103103

104104
static Optional<int> getIntConstant(SILValue value);
105-
static void replaceAppendWith(ApplyInst *appendCall, SILValue newValue,
106-
bool copyNewValue);
105+
static void replaceAppendWith(ApplyInst *appendCall, SILValue newValue);
106+
static SILValue copyValue(SILValue value, SILInstruction *before);
107107
ApplyInst *createStringInit(StringRef str, SILInstruction *beforeInst);
108108
};
109109

@@ -144,6 +144,12 @@ bool StringOptimization::optimizeBlock(SILBasicBlock &block) {
144144
continue;
145145
}
146146
}
147+
if (ApplyInst *append = isSemanticCall(inst, semantics::STRING_CONCAT, 3)) {
148+
if (optimizeStringConcat(append)) {
149+
changed = true;
150+
continue;
151+
}
152+
}
147153
if (ApplyInst *typeName = isSemanticCall(inst, semantics::TYPENAME, 2)) {
148154
if (optimizeTypeName(typeName)) {
149155
changed = true;
@@ -184,7 +190,7 @@ bool StringOptimization::optimizeStringAppend(ApplyInst *appendCall,
184190

185191
// Replace lhs.append(rhs) with 'lhs = rhs' if lhs is empty.
186192
if (lhsString.isEmpty()) {
187-
replaceAppendWith(appendCall, rhs, /*copyNewValue*/ true);
193+
replaceAppendWith(appendCall, copyValue(rhs, appendCall));
188194
storedStrings[lhsAddr] = rhs;
189195
return true;
190196
}
@@ -195,7 +201,7 @@ bool StringOptimization::optimizeStringAppend(ApplyInst *appendCall,
195201
std::string concat = lhsString.str;
196202
concat += rhsString.str;
197203
if (ApplyInst *stringInit = createStringInit(concat, appendCall)) {
198-
replaceAppendWith(appendCall, stringInit, /*copyNewValue*/ false);
204+
replaceAppendWith(appendCall, stringInit);
199205
storedStrings[lhsAddr] = stringInit;
200206
return true;
201207
}
@@ -204,6 +210,43 @@ bool StringOptimization::optimizeStringAppend(ApplyInst *appendCall,
204210
return false;
205211
}
206212

213+
/// Optimize String.+ in case anything is known about the parameters.
214+
bool StringOptimization::optimizeStringConcat(ApplyInst *concatCall) {
215+
SILValue lhs = concatCall->getArgument(0);
216+
SILValue rhs = concatCall->getArgument(1);
217+
StringInfo rhsString = getStringInfo(rhs);
218+
219+
// Replace lhs + "" with lhs
220+
if (rhsString.isEmpty()) {
221+
lhs = copyValue(lhs, concatCall);
222+
concatCall->replaceAllUsesWith(lhs);
223+
concatCall->eraseFromParent();
224+
return true;
225+
}
226+
227+
// Replace "" + rhs with rhs
228+
StringInfo lhsString = getStringInfo(lhs);
229+
if (lhsString.isEmpty()) {
230+
rhs = copyValue(rhs, concatCall);
231+
concatCall->replaceAllUsesWith(rhs);
232+
concatCall->eraseFromParent();
233+
return true;
234+
}
235+
236+
// Replace lhs + rhs with "lhs + rhs" if both lhs and rhs are constant.
237+
if (lhsString.isConstant() && rhsString.isConstant()) {
238+
std::string concat = lhsString.str;
239+
concat += rhsString.str;
240+
if (ApplyInst *stringInit = createStringInit(concat, concatCall)) {
241+
concatCall->replaceAllUsesWith(stringInit);
242+
concatCall->eraseFromParent();
243+
return true;
244+
}
245+
}
246+
247+
return false;
248+
}
249+
207250
/// Checks if the demangling tree contains any node which prevents constant
208251
/// folding of the type name.
209252
static bool containsProblematicNode(Demangle::Node *node, bool qualified) {
@@ -499,25 +542,33 @@ Optional<int> StringOptimization::getIntConstant(SILValue value) {
499542

500543
/// Replace a String.append() with a store of \p newValue to the destination.
501544
void StringOptimization::replaceAppendWith(ApplyInst *appendCall,
502-
SILValue newValue, bool copyNewValue) {
545+
SILValue newValue) {
503546
SILBuilder builder(appendCall);
504547
SILLocation loc = appendCall->getLoc();
505548
SILValue destAddr = appendCall->getArgument(1);
506549
if (appendCall->getFunction()->hasOwnership()) {
507-
if (copyNewValue)
508-
newValue = builder.createCopyValue(loc, newValue);
509550
builder.createStore(loc, newValue, destAddr,
510551
StoreOwnershipQualifier::Assign);
511552
} else {
512-
if (copyNewValue)
513-
builder.createRetainValue(loc, newValue, builder.getDefaultAtomicity());
514553
builder.createDestroyAddr(loc, destAddr);
515554
builder.createStore(loc, newValue, destAddr,
516555
StoreOwnershipQualifier::Unqualified);
517556
}
518557
appendCall->eraseFromParent();
519558
}
520559

560+
/// Returns a copy of \p value. Depending if the function is in OSSA, insert
561+
/// either a copy_value or retain_value.
562+
SILValue StringOptimization::copyValue(SILValue value, SILInstruction *before) {
563+
SILBuilder builder(before);
564+
SILLocation loc = before->getLoc();
565+
if (before->getFunction()->hasOwnership())
566+
return builder.createCopyValue(loc, value);
567+
568+
builder.createRetainValue(loc, value, builder.getDefaultAtomicity());
569+
return value;
570+
}
571+
521572
/// Creates a call to a string initializer.
522573
ApplyInst *StringOptimization::createStringInit(StringRef str,
523574
SILInstruction *beforeInst) {

lib/SILOptimizer/Utils/ConstantFolding.cpp

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,46 +1509,6 @@ static bool isFoldable(SILInstruction *I) {
15091509
isa<StringLiteralInst>(I);
15101510
}
15111511

1512-
bool ConstantFolder::constantFoldStringConcatenation(ApplyInst *AI) {
1513-
SILBuilder B(AI);
1514-
// Try to apply the string literal concatenation optimization.
1515-
auto *Concatenated = tryToConcatenateStrings(AI, B);
1516-
// Bail if string literal concatenation could not be performed.
1517-
if (!Concatenated)
1518-
return false;
1519-
1520-
// Replace all uses of the old instruction by a new instruction.
1521-
AI->replaceAllUsesWith(Concatenated);
1522-
1523-
auto RemoveCallback = [&](SILInstruction *DeadI) { WorkList.remove(DeadI); };
1524-
// Remove operands that are not used anymore.
1525-
// Even if they are apply_inst, it is safe to
1526-
// do so, because they can only be applies
1527-
// of functions annotated as string.utf16
1528-
// or string.utf16.
1529-
for (auto &Op : AI->getAllOperands()) {
1530-
SILValue Val = Op.get();
1531-
Op.drop();
1532-
if (Val->use_empty()) {
1533-
auto *DeadI = Val->getDefiningInstruction();
1534-
assert(DeadI);
1535-
recursivelyDeleteTriviallyDeadInstructions(DeadI, /*force*/ true,
1536-
RemoveCallback);
1537-
}
1538-
}
1539-
// Schedule users of the new instruction for constant folding.
1540-
// We only need to schedule the string.concat invocations.
1541-
for (auto AIUse : Concatenated->getUses()) {
1542-
if (isApplyOfStringConcat(*AIUse->getUser())) {
1543-
WorkList.insert(AIUse->getUser());
1544-
}
1545-
}
1546-
// Delete the old apply instruction.
1547-
recursivelyDeleteTriviallyDeadInstructions(AI, /*force*/ true,
1548-
RemoveCallback);
1549-
return true;
1550-
}
1551-
15521512
/// Given a buitin instruction calling globalStringTablePointer, check whether
15531513
/// the string passed to the builtin is constructed from a literal and if so,
15541514
/// replace the uses of the builtin instruction with the string_literal inst.
@@ -1772,16 +1732,6 @@ ConstantFolder::processWorkList() {
17721732
}
17731733
}
17741734

1775-
if (auto *AI = dyn_cast<ApplyInst>(I)) {
1776-
// Apply may only come from a string.concat invocation.
1777-
if (constantFoldStringConcatenation(AI)) {
1778-
// Invalidate all analysis that's related to the call graph.
1779-
InvalidateInstructions = true;
1780-
}
1781-
1782-
continue;
1783-
}
1784-
17851735
// If we have a cast instruction, try to optimize it.
17861736
if (isa<CheckedCastBranchInst>(I) || isa<CheckedCastAddrBranchInst>(I) ||
17871737
isa<UnconditionalCheckedCastInst>(I) ||

0 commit comments

Comments
 (0)