Skip to content

StringOptimization: bug fixes #33201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ std::string ASTMangler::mangleTypeForDebugger(Type Ty, const DeclContext *DC) {
}

std::string ASTMangler::mangleTypeForTypeName(Type type) {
beginMangling();
beginManglingWithoutPrefix();
appendType(type);
return finalize();
}
Expand Down
47 changes: 41 additions & 6 deletions lib/SILOptimizer/Transforms/StringOptimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,32 @@ bool StringOptimization::optimizeStringAppend(ApplyInst *appendCall,
return false;
}

/// Checks if the demangling tree contains any node which prevents constant
/// folding of the type name.
static bool containsProblematicNode(Demangle::Node *node, bool qualified) {
switch (node->getKind()) {
case Demangle::Node::Kind::LocalDeclName:
// The printing of contexts for local types is completely different
// in the runtime. Don't constant fold if we need to print the context.
if (qualified)
return true;
break;
case Demangle::Node::Kind::Class:
// ObjC class names are not derived from the mangling but from the
// ObjC runtime. We cannot constant fold this.
if (node->getChild(0)->getText() == "__C")
return true;
break;
default:
break;
}
for (Demangle::Node *child : *node) {
if (containsProblematicNode(child, qualified))
return true;
}
return false;
}

/// Try to replace a _typeName() call with a constant string if the type is
/// statically known.
bool StringOptimization::optimizeTypeName(ApplyInst *typeNameCall) {
Expand All @@ -217,20 +243,29 @@ bool StringOptimization::optimizeTypeName(ApplyInst *typeNameCall) {
return false;

// Usually the "qualified" parameter of _typeName() is a constant boolean.
Optional<int> isQualified = getIntConstant(typeNameCall->getArgument(1));
if (!isQualified)
Optional<int> isQualifiedOpt = getIntConstant(typeNameCall->getArgument(1));
if (!isQualifiedOpt)
return false;
bool isQualified = isQualifiedOpt.getValue();

// Create the constant type string by mangling + demangling.
Mangle::ASTMangler mangler;
std::string mangledTypeName = mangler.mangleTypeForTypeName(ty);

Demangle::DemangleOptions options;
options.PrintForTypeName = true;
options.QualifyEntities = (isQualified.getValue() != 0);
std::string typeStr = Demangle::demangleSymbolAsString(mangledTypeName,
options);

options.DisplayLocalNameContexts = false;
options.QualifyEntities = isQualified;

Demangle::Context ctx;
Demangle::NodePointer root = ctx.demangleTypeAsNode(mangledTypeName);
if (!root || containsProblematicNode(root, isQualified))
return false;

std::string typeStr = nodeToString(root, options);
if (typeStr.empty())
return false;

ApplyInst *stringInit = createStringInit(typeStr, typeNameCall);
if (!stringInit)
return false;
Expand Down
47 changes: 47 additions & 0 deletions test/SILOptimizer/string_optimization.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

// REQUIRES: executable_test,swift_stdlib_no_asserts

#if _runtime(_ObjC)
import Foundation
#endif

struct Outer {
struct Inner { }
}
Expand Down Expand Up @@ -50,17 +54,60 @@ public func testQualifiedTypeName() -> String {
return _typeName(Outer.Inner.self, qualified: true)
}

// CHECK-LABEL: sil [noinline] @$s4test0A20UnqualifiedLocalTypeSSyF
// CHECK-NOT: apply
// CHECK-NOT: bb1
// CHECK: } // end sil function '$s4test0A20UnqualifiedLocalTypeSSyF'
@inline(never)
public func testUnqualifiedLocalType() -> String {
struct LocalStruct { }
return _typeName(LocalStruct.self, qualified: false)
}

// CHECK-LABEL: sil [noinline] @$s4test0A18QualifiedLocalTypeSSyF
// CHECK: [[F:%[0-9]+]] = function_ref @$ss9_typeName_9qualifiedSSypXp_SbtF
// CHECK: apply [[F]]
// CHECK: } // end sil function '$s4test0A18QualifiedLocalTypeSSyF'
@inline(never)
public func testQualifiedLocalType() -> String {
struct LocalStruct { }
return _typeName(LocalStruct.self, qualified: true)
}

#if _runtime(_ObjC)
@inline(never)
public func testObjcClassName(qualified: Bool) -> String {
return _typeName(NSObject.self, qualified: qualified)
}
#endif

@inline(never)
func printEmbeeded(_ s: String) {
print("<\(s)>")
}

// CHECK-OUTPUT: <-Inner+>
printEmbeeded(testTypeNameInterpolation())

// CHECK-OUTPUT: <-Array<Int> is cool+>
printEmbeeded(testFoldCompleteInterpolation())

// CHECK-OUTPUT: <Inner>
printEmbeeded(testUnqualifiedTypeName())

// CHECK-OUTPUT: <test.Outer.Inner>
printEmbeeded(testQualifiedTypeName())

// CHECK-OUTPUT: <LocalStruct>
printEmbeeded(testUnqualifiedLocalType())

// CHECK-OUTPUT: <test.(unknown context at {{.*}}).LocalStruct>
printEmbeeded(testQualifiedLocalType())

#if _runtime(_ObjC)
// CHECK-OUTPUT: <NSObject>
printEmbeeded(testObjcClassName(qualified: false))
// CHECK-OUTPUT: <NSObject>
printEmbeeded(testObjcClassName(qualified: true))
#endif

2 changes: 2 additions & 0 deletions test/stdlib/TypeName.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -O -module-name=main %s -o %t/O.out
// RUN: %target-codesign %t/O.out
// RUN: %target-run %t/O.out
// RUN: %target-build-swift -Onone -module-name=main %s -o %t/Onone.out
// RUN: %target-codesign %t/Onone.out
// RUN: %target-run %t/Onone.out

// REQUIRES: executable_test
Expand Down
2 changes: 2 additions & 0 deletions test/stdlib/TypeNameInterpolation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

// RUN: %empty-directory(%t)
// RUN: %target-build-swift -O -module-name=test %s -o %t/O.out
// RUN: %target-codesign %t/O.out
// RUN: %target-run %t/O.out
// RUN: %target-build-swift -Onone -module-name=test %s -o %t/Onone.out
// RUN: %target-codesign %t/Onone.out
// RUN: %target-run %t/Onone.out

// REQUIRES: executable_test
Expand Down
7 changes: 6 additions & 1 deletion validation-test/stdlib/StringUTF8.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ extension String {
var isFastUTF8: Bool {
return withFastUTF8IfAvailable({ _ in return 0 }) != nil
}
mutating func makeNative() { self += "" }

// Prevent that the optimizer removes 'self += ""' in makeNative()
@inline(never)
static func emptyString() -> String { return "" }

mutating func makeNative() { self += String.emptyString() }

var isASCII: Bool { return utf8.allSatisfy { $0 < 0x7f } }
}
Expand Down