Skip to content

Commit 88a74a2

Browse files
committed
StringOptimization: optimize interpolated C strings.
Optimize code like: puts("\(String.self)") Optimizing string interpolation and optimizing C-strings are both done in StringOptimization. A second run of the StringOptimization is needed in the pipeline to optimize such code, because the result of the interpolation-optimization must be cleaned up so that the C-String optimization can kick in. Also, StringOptimization must handle struct_extract(struct(literal)), where the struct_extract may be in a called function. To solve a phase ordering problem with inlining String semantics and inlining the `String(stringInterpolation: DefaultStringInterpolation)` constructor, we do a simple analysis of the callee. Doing this simple "interprocedural" analysis avoids relying on inlining that String constructor. rdar://74941849
1 parent 286d22b commit 88a74a2

File tree

4 files changed

+109
-1
lines changed

4 files changed

+109
-1
lines changed

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,12 @@ void addFunctionPasses(SILPassPipelinePlan &P,
347347
P.addCOWArrayOpts();
348348
P.addDCE();
349349
P.addSwiftArrayPropertyOpt();
350+
351+
// This string optimization can catch additional opportunities, which are
352+
// exposed once optimized String interpolations (from the high-level string
353+
// optimization) are cleaned up. But before the mid-level inliner inlines
354+
// semantic calls.
355+
P.addStringOptimization();
350356
}
351357

352358
// Run the devirtualizer, specializer, and inliner. If any of these

lib/SILOptimizer/Transforms/StringOptimization.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,11 +471,60 @@ void StringOptimization::invalidateModifiedObjects(SILInstruction *inst,
471471
}
472472
}
473473

474+
/// If \p value is a struct_extract, return its operand and field.
475+
static std::pair<SILValue, VarDecl *> skipStructExtract(SILValue value) {
476+
if (auto *sei = dyn_cast<StructExtractInst>(value))
477+
return {sei->getOperand(), sei->getField()};
478+
479+
// Look through function calls, which do the struct_extract in the callee.
480+
// This specifically targets
481+
// String(stringInterpolation: DefaultStringInterpolation)
482+
// which is not inlined in the high level pipeline (due to the specified
483+
// effects).
484+
auto *apply = dyn_cast<ApplyInst>(value);
485+
if (!apply)
486+
return {value, nullptr};
487+
488+
SILFunction *callee = apply->getReferencedFunctionOrNull();
489+
if (!callee || !callee->isDefinition())
490+
return {value, nullptr};
491+
492+
// `String(stringInterpolation: DefaultStringInterpolation)` has only a single
493+
// basic block. Avoid the effort of searching all blocks for a `return`.
494+
auto *ret = dyn_cast<ReturnInst>(callee->getEntryBlock()->getTerminator());
495+
if (!ret)
496+
return {value, nullptr};
497+
498+
auto *sei = dyn_cast<StructExtractInst>(ret->getOperand());
499+
if (!sei)
500+
return {value, nullptr};
501+
502+
auto *arg = dyn_cast<SILFunctionArgument>(sei->getOperand());
503+
if (!arg)
504+
return {value, nullptr};
505+
506+
value = apply->getArgument(arg->getIndex());
507+
return {value, sei->getField()};
508+
}
509+
474510
/// Returns information about value if it's a constant string.
475511
StringOptimization::StringInfo StringOptimization::getStringInfo(SILValue value) {
476512
if (!value)
477513
return StringInfo::unknown();
478514

515+
// Look through struct_extract(struct(value)).
516+
// This specifically targets calls to
517+
// String(stringInterpolation: DefaultStringInterpolation)
518+
// which are not inlined in the high level pipeline.
519+
VarDecl *field = nullptr;
520+
std::tie(value, field) = skipStructExtract(value);
521+
if (field) {
522+
auto *si = dyn_cast<StructInst>(value);
523+
if (!si)
524+
return StringInfo::unknown();
525+
value = si->getFieldValue(field);
526+
}
527+
479528
auto *apply = dyn_cast<ApplyInst>(value);
480529
if (!apply) {
481530
return getStringFromStaticLet(value);

test/SILOptimizer/c_string_optimization.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,25 @@ public func testStringConstantForCFunction() {
3232
puts("Hello " + "world!")
3333
}
3434

35+
// CHECK-LABEL: sil [noinline] @$s4test0A17TypeInterpolationyyF
36+
// CHECK-NOT: apply
37+
// CHECK: [[L:%[0-9]+]] = string_literal utf8 "String"
38+
// CHECK-NOT: apply
39+
// CHECK: [[P:%[0-9]+]] = struct $UnsafePointer<Int8> ([[L]] : $Builtin.RawPointer)
40+
// CHECK-NOT: apply
41+
// CHECK: [[O:%[0-9]+]] = enum $Optional<UnsafePointer<Int8>>, #Optional.some!enumelt, [[P]]
42+
// CHECK-NOT: apply
43+
// CHECK: [[F:%[0-9]+]] = function_ref @puts
44+
// CHECK: apply [[F]]([[O]])
45+
// CHECK: } // end sil function '$s4test0A17TypeInterpolationyyF'
46+
@inline(never)
47+
public func testTypeInterpolation() {
48+
puts("\(String.self)")
49+
}
50+
3551
// CHECK-OUTPUT: Hello world!
3652
testStringConstantForCFunction()
53+
54+
// CHECK-OUTPUT: String
55+
testTypeInterpolation()
56+

test/SILOptimizer/string_optimization.sil

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,45 @@ bb0:
245245
%6 = metatype $@thin String.Type
246246
%7 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String
247247
%8 = apply %7(%3, %4, %5, %6) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String
248+
%d = struct $DefaultStringInterpolation (%8 : $String)
249+
%e = struct_extract %d : $DefaultStringInterpolation, #DefaultStringInterpolation._storage
248250
%9 = function_ref @string_getUTF8CString : $@convention(method) (@guaranteed String) -> @owned ContiguousArray<Int8>
249-
%10 = apply %9(%8) : $@convention(method) (@guaranteed String) -> @owned ContiguousArray<Int8>
251+
%10 = apply %9(%e) : $@convention(method) (@guaranteed String) -> @owned ContiguousArray<Int8>
250252
%11 = struct_extract %10 : $ContiguousArray<Int8>, #ContiguousArray._buffer
251253
%12 = struct_extract %11 : $_ContiguousArrayBuffer<Int8>, #_ContiguousArrayBuffer._storage
252254
%13 = ref_tail_addr %12 : $__ContiguousArrayStorageBase, $Int8
253255
%14 = address_to_pointer %13 : $*Int8 to $Builtin.RawPointer
254256
return %14 : $Builtin.RawPointer
255257
}
256258

259+
// CHECK-LABEL: sil @test_interpolated_cstring
260+
// CHECK: apply
261+
// CHECK: [[S:%[0-9]+]] = string_literal utf8 "a"
262+
// CHECK: return [[S]]
263+
// CHECK: } // end sil function 'test_interpolated_cstring'
264+
sil @test_interpolated_cstring : $@convention(thin) () -> Builtin.RawPointer {
265+
bb0:
266+
%3 = string_literal utf8 "a"
267+
%4 = integer_literal $Builtin.Word, 1
268+
%5 = integer_literal $Builtin.Int1, -1
269+
%6 = metatype $@thin String.Type
270+
%7 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String
271+
%8 = apply %7(%3, %4, %5, %6) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String
272+
%d = struct $DefaultStringInterpolation (%8 : $String)
273+
%f = function_ref @$sSS19stringInterpolationSSs013DefaultStringB0V_tcfC : $@convention(method) (@owned DefaultStringInterpolation, @thin String.Type) -> @owned String
274+
%a = apply %f(%d, %6) : $@convention(method) (@owned DefaultStringInterpolation, @thin String.Type) -> @owned String
275+
%9 = function_ref @string_getUTF8CString : $@convention(method) (@guaranteed String) -> @owned ContiguousArray<Int8>
276+
%10 = apply %9(%a) : $@convention(method) (@guaranteed String) -> @owned ContiguousArray<Int8>
277+
%11 = struct_extract %10 : $ContiguousArray<Int8>, #ContiguousArray._buffer
278+
%12 = struct_extract %11 : $_ContiguousArrayBuffer<Int8>, #_ContiguousArrayBuffer._storage
279+
%13 = ref_tail_addr %12 : $__ContiguousArrayStorageBase, $Int8
280+
%14 = address_to_pointer %13 : $*Int8 to $Builtin.RawPointer
281+
return %14 : $Builtin.RawPointer
282+
}
283+
284+
sil public_external [readonly] @$sSS19stringInterpolationSSs013DefaultStringB0V_tcfC : $@convention(method) (@owned DefaultStringInterpolation, @thin String.Type) -> @owned String {
285+
bb0(%0 : $DefaultStringInterpolation, %1 : $@thin String.Type):
286+
%2 = struct_extract %0 : $DefaultStringInterpolation, #DefaultStringInterpolation._storage
287+
return %2 : $String
288+
}
289+

0 commit comments

Comments
 (0)