Skip to content

[6.1 cherry-pick] Allow string-interpolating fatalError, assert, assertionFailure, precondition, preconditionFailure in Embedded Swift #79757

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
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
5 changes: 4 additions & 1 deletion include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ class IRGenOptions {
/// Emit names of struct stored properties and enum cases.
unsigned EnableReflectionNames : 1;

unsigned DisableLLVMMergeFunctions : 1;

/// Emit mangled names of anonymous context descriptors.
unsigned EnableAnonymousContextMangledNames : 1;

Expand Down Expand Up @@ -568,7 +570,8 @@ class IRGenOptions {
SwiftAsyncFramePointer(SwiftAsyncFramePointerKind::Auto),
HasValueNamesSetting(false), ValueNames(false),
ReflectionMetadata(ReflectionMetadataMode::Runtime),
EnableReflectionNames(true), EnableAnonymousContextMangledNames(false),
EnableReflectionNames(true), DisableLLVMMergeFunctions(false),
EnableAnonymousContextMangledNames(false),
ForcePublicLinkage(false), LazyInitializeClassMetadata(false),
LazyInitializeProtocolConformances(false),
IndirectAsyncFunctionPointer(false),
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,9 @@ def disable_reflection_names : Flag<["-"], "disable-reflection-names">,
HelpText<"Disable emission of names of stored properties and enum cases in"
"reflection metadata">;

def disable_llvm_merge_functions_pass : Flag<["-"], "disable-llvm-merge-functions-pass">,
HelpText<"Disable the MergeFunctionPass LLVM IR pass">;

def function_sections: Flag<["-"], "function-sections">,
Flags<[FrontendOption, NoInteractiveOption]>,
HelpText<"Emit functions to separate sections.">;
Expand Down
4 changes: 4 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3344,6 +3344,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
Opts.EnableReflectionNames = false;
}

if (Args.hasArg(OPT_disable_llvm_merge_functions_pass)) {
Opts.DisableLLVMMergeFunctions = true;
}

if (Args.hasArg(OPT_force_public_linkage)) {
Opts.ForcePublicLinkage = true;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ void swift::performLLVMOptimizations(const IRGenOptions &Opts,
PTO.LoopInterleaving = true;
PTO.LoopVectorization = true;
PTO.SLPVectorization = true;
PTO.MergeFunctions = true;
PTO.MergeFunctions = !Opts.DisableLLVMMergeFunctions;
// Splitting trades code size to enhance memory locality, avoid in -Osize.
DoHotColdSplit = Opts.EnableHotColdSplit && !Opts.optimizeForSize();
level = llvm::OptimizationLevel::Os;
Expand Down Expand Up @@ -361,7 +361,8 @@ void swift::performLLVMOptimizations(const IRGenOptions &Opts,
allowlistFiles, ignorelistFiles));
});
}
if (RunSwiftSpecificLLVMOptzns) {

if (RunSwiftSpecificLLVMOptzns && !Opts.DisableLLVMMergeFunctions) {
PB.registerOptimizerLastEPCallback(
[&](ModulePassManager &MPM, OptimizationLevel Level) {
if (Level != OptimizationLevel::O0) {
Expand Down
16 changes: 11 additions & 5 deletions stdlib/public/core/Assert.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
/// fails. The default is the line number where `assert(_:_:file:line:)`
/// is called.
@_transparent
@_unavailableInEmbedded
#if $Embedded
@_disfavoredOverload
#endif
Expand Down Expand Up @@ -101,7 +100,6 @@ public func assert(
/// fails. The default is the line number where
/// `precondition(_:_:file:line:)` is called.
@_transparent
@_unavailableInEmbedded
#if $Embedded
@_disfavoredOverload
#endif
Expand Down Expand Up @@ -167,7 +165,6 @@ public func precondition(
/// line number where `assertionFailure(_:file:line:)` is called.
@inlinable
@inline(__always)
@_unavailableInEmbedded
#if $Embedded
@_disfavoredOverload
#endif
Expand Down Expand Up @@ -229,7 +226,6 @@ public func assertionFailure(
/// - line: The line number to print along with `message`. The default is the
/// line number where `preconditionFailure(_:file:line:)` is called.
@_transparent
@_unavailableInEmbedded
#if $Embedded
@_disfavoredOverload
#endif
Expand Down Expand Up @@ -275,16 +271,26 @@ public func preconditionFailure(
/// - line: The line number to print along with `message`. The default is the
/// line number where `fatalError(_:file:line:)` is called.
@_transparent
@_unavailableInEmbedded
#if $Embedded
@_disfavoredOverload
#endif
public func fatalError(
_ message: @autoclosure () -> String = String(),
file: StaticString = #file, line: UInt = #line
) -> Never {
#if !$Embedded
_assertionFailure("Fatal error", message(), file: file, line: line,
flags: _fatalErrorFlags())
#else
if _isDebugAssertConfiguration() {
_assertionFailure("Fatal error", message(), file: file, line: line,
flags: _fatalErrorFlags())
} else {
Builtin.condfail_message(true._value,
StaticString("fatal error").unsafeRawPointer)
Builtin.unreachable()
}
#endif
}

#if $Embedded
Expand Down
11 changes: 10 additions & 1 deletion stdlib/public/core/AssertCommon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,16 @@ internal func _assertionFailure(
#if !$Embedded
@inline(never)
#else
@_disfavoredOverload
@inline(__always)
#endif
@_semantics("programtermination_point")
@_unavailableInEmbedded
internal func _assertionFailure(
_ prefix: StaticString, _ message: String,
file: StaticString, line: UInt,
flags: UInt32
) -> Never {
#if !$Embedded
prefix.withUTF8Buffer {
(prefix) -> Void in
var message = message
Expand All @@ -158,6 +159,14 @@ internal func _assertionFailure(
}
}
}
#else
if _isDebugAssertConfiguration() {
var message = message
message.withUTF8 { (messageUTF8) -> Void in
_embeddedReportFatalErrorInFile(prefix: prefix, message: messageUTF8, file: file, line: line)
}
}
#endif

Builtin.int_trap()
}
Expand Down
11 changes: 11 additions & 0 deletions stdlib/public/core/EmbeddedPrint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ public func print(_ object: some CustomStringConvertible, terminator: StaticStri
}
}

func print(_ buf: UnsafeBufferPointer<UInt8>, terminator: StaticString = "\n") {
for c in buf {
putchar(CInt(c))
}
var p = terminator.utf8Start
while p.pointee != 0 {
putchar(CInt(p.pointee))
p += 1
}
}

func printCharacters(_ buf: UnsafeRawBufferPointer) {
for c in buf {
putchar(CInt(c))
Expand Down
12 changes: 12 additions & 0 deletions stdlib/public/core/EmbeddedRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -515,17 +515,29 @@ public func swift_clearSensitive(buf: UnsafeMutableRawPointer, nbytes: Int) {
}

@usableFromInline
@inline(never)
func _embeddedReportFatalError(prefix: StaticString, message: StaticString) {
print(prefix, terminator: "")
if message.utf8CodeUnitCount > 0 { print(": ", terminator: "") }
print(message)
}

@usableFromInline
@inline(never)
func _embeddedReportFatalErrorInFile(prefix: StaticString, message: StaticString, file: StaticString, line: UInt) {
print(file, terminator: ":")
print(line, terminator: ": ")
print(prefix, terminator: "")
if message.utf8CodeUnitCount > 0 { print(": ", terminator: "") }
print(message)
}

@usableFromInline
@inline(never)
func _embeddedReportFatalErrorInFile(prefix: StaticString, message: UnsafeBufferPointer<UInt8>, file: StaticString, line: UInt) {
print(file, terminator: ":")
print(line, terminator: ": ")
print(prefix, terminator: "")
if message.count > 0 { print(": ", terminator: "") }
print(message)
}
33 changes: 33 additions & 0 deletions test/embedded/traps-fatalerror-exec2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// RUN: %empty-directory(%t)
// RUN: %target-clang -x c -c %S/Inputs/unbuffered-putchar.c -o %t/unbuffered-putchar.o

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-MESSAGE

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out -O
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-NOMESSAGE

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out -Osize
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-NOMESSAGE

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out -O -assert-config Debug
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-MESSAGE

// RUN: %target-build-swift -enable-experimental-feature Embedded -wmo -runtime-compatibility-version none %s -Xlinker %t/unbuffered-putchar.o -o %t/a.out -Osize -assert-config Debug
// RUN: not --crash %t/a.out 2>&1 | %FileCheck %s --check-prefix=CHECK-MESSAGE

// REQUIRES: swift_in_compiler
// REQUIRES: executable_test
// REQUIRES: optimized_stdlib
// REQUIRES: OS=macosx || OS=linux-gnu
// REQUIRES: swift_test_mode_optimize_none
// REQUIRES: swift_feature_Embedded

func testWithInterpolation(i: Int) {
fatalError("task failed successfully \(i)")
// CHECK-MESSAGE: {{.*}}/traps-fatalerror-exec2.swift:[[@LINE-1]]: Fatal error: task failed successfully 42
// CHECK-NOMESSAGE-NOT: Fatal error
// CHECK-NOMESSAGE-NOT: task failed successfully 42
}

testWithInterpolation(i: 42)
20 changes: 20 additions & 0 deletions test/embedded/traps-fatalerror-ir.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,23 @@ public func test() {
// CHECK-NOMESSAGE-NEXT: tail call void @llvm.trap()
// CHECK-NOMESSAGE-NEXT: unreachable
// CHECK-NOMESSAGE-NEXT: }

public func testWithInterpolation(i: Int) {
fatalError("task failed successfully \(i)")
}

// CHECK-MESSAGE: define {{.*}}void @"$s4main21testWithInterpolation1iySi_tF"(i64 %0){{.*}} {
// CHECK-MESSAGE: entry:
// CHECK-MESSAGE: task failed successfully
// CHECK-MESSAGE: {{.*}}call {{.*}}void @"${{(ss17_assertionFailure__|ss31_embeddedReportFatalErrorInFile6prefix7message4file4lineys12StaticStringV_SRys5UInt8VGAGSutF)}}
// CHECK-MESSAGE-SAME: Fatal error
// CHECK-MESSAGE-SAME: traps-fatalerror-ir.swift
// CHECK-MESSAGE: unreachable
// CHECK-MESSAGE: }

// CHECK-NOMESSAGE: define {{.*}}void @"$s4main21testWithInterpolation1iySi_tF"(i64 %0){{.*}} {
// CHECK-NOMESSAGE-NEXT: entry:
// CHECK-NOMESSAGE-NEXT: tail call void asm sideeffect "", "n"(i32 0)
// CHECK-NOMESSAGE-NEXT: tail call void @llvm.trap()
// CHECK-NOMESSAGE-NEXT: unreachable
// CHECK-NOMESSAGE-NEXT: }
65 changes: 65 additions & 0 deletions test/embedded/traps-string-interpolations.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -enable-experimental-feature Embedded -emit-ir -Osize -disable-llvm-merge-functions-pass | %FileCheck %s

// REQUIRES: swift_in_compiler
// REQUIRES: optimized_stdlib
// REQUIRES: OS=macosx || OS=linux-gnu
// REQUIRES: swift_feature_Embedded

public func test1(i: Int) {
fatalError("\(i) is not 42")
}

public func test2(i: Int) {
assert(i == 42, "\(i) is not 42")
}

public func test3(i: Int) {
precondition(i == 42, "\(i) is not 42")
}

public func test4(i: Int) {
assertionFailure("\(i) is not 42")
}

public func test5(i: Int) {
preconditionFailure("\(i) is not 42")
}

// CHECK: define {{.*}}@"$s4main5test11iySi_tF"
// CHECK-NEXT: entry:
// CHECK-NEXT: tail call void asm sideeffect ""
// CHECK-NEXT: tail call void @llvm.trap()
// CHECK-NEXT: unreachable
// CHECK-NEXT: }

// CHECK: define {{.*}}@"$s4main5test21iySi_tF"
// CHECK-NEXT: entry:
// CHECK-NEXT: ret void
// CHECK-NEXT: }

// CHECK: define {{.*}}@"$s4main5test31iySi_tF"
// CHECK-NEXT: entry:
// CHECK-NEXT: %.not = icmp eq i64 %0, 42
// CHECK-NEXT: br i1 %.not, label %1, label %2
// CHECK-EMPTY:
// CHECK-NEXT: 1:
// CHECK-NEXT: ret void
// CHECK-EMPTY:
// CHECK-NEXT: 2:
// CHECK-NEXT: tail call void asm sideeffect ""
// CHECK-NEXT: tail call void @llvm.trap()
// CHECK-NEXT: unreachable
// CHECK-NEXT: }

// CHECK: define {{.*}}@"$s4main5test41iySi_tF"
// CHECK-NEXT: entry:
// CHECK-NEXT: ret void
// CHECK-NEXT: }

// CHECK: define {{.*}}@"$s4main5test51iySi_tF"
// CHECK-NEXT: entry:
// CHECK-NEXT: tail call void asm sideeffect ""
// CHECK-NEXT: tail call void @llvm.trap()
// CHECK-NEXT: unreachable
// CHECK-NEXT: }