diff --git a/include/swift/AST/IRGenOptions.h b/include/swift/AST/IRGenOptions.h index 1f102d78204cb..636fdc2db1c2b 100644 --- a/include/swift/AST/IRGenOptions.h +++ b/include/swift/AST/IRGenOptions.h @@ -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; @@ -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), diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index e8bb97ada5e00..9ae29ffc6e1b1 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -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.">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 7cbc2df7ea748..e4136f132ba43 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -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; } diff --git a/lib/IRGen/IRGen.cpp b/lib/IRGen/IRGen.cpp index ea9492d172531..d9e99dc32e4dc 100644 --- a/lib/IRGen/IRGen.cpp +++ b/lib/IRGen/IRGen.cpp @@ -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; @@ -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) { diff --git a/stdlib/public/core/Assert.swift b/stdlib/public/core/Assert.swift index 647ac82c4b396..325f1d1dfdc6b 100644 --- a/stdlib/public/core/Assert.swift +++ b/stdlib/public/core/Assert.swift @@ -38,7 +38,6 @@ /// fails. The default is the line number where `assert(_:_:file:line:)` /// is called. @_transparent -@_unavailableInEmbedded #if $Embedded @_disfavoredOverload #endif @@ -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 @@ -167,7 +165,6 @@ public func precondition( /// line number where `assertionFailure(_:file:line:)` is called. @inlinable @inline(__always) -@_unavailableInEmbedded #if $Embedded @_disfavoredOverload #endif @@ -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 @@ -275,7 +271,6 @@ 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 @@ -283,8 +278,19 @@ 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 diff --git a/stdlib/public/core/AssertCommon.swift b/stdlib/public/core/AssertCommon.swift index c9eb7540a1df9..5c5b7b5cc963a 100644 --- a/stdlib/public/core/AssertCommon.swift +++ b/stdlib/public/core/AssertCommon.swift @@ -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 @@ -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() } diff --git a/stdlib/public/core/EmbeddedPrint.swift b/stdlib/public/core/EmbeddedPrint.swift index d1163d264b94b..7c107f1154e67 100644 --- a/stdlib/public/core/EmbeddedPrint.swift +++ b/stdlib/public/core/EmbeddedPrint.swift @@ -63,6 +63,17 @@ public func print(_ object: some CustomStringConvertible, terminator: StaticStri } } +func print(_ buf: UnsafeBufferPointer, 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)) diff --git a/stdlib/public/core/EmbeddedRuntime.swift b/stdlib/public/core/EmbeddedRuntime.swift index 187e9ab55be95..347d45751c878 100644 --- a/stdlib/public/core/EmbeddedRuntime.swift +++ b/stdlib/public/core/EmbeddedRuntime.swift @@ -515,6 +515,7 @@ 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: "") } @@ -522,6 +523,7 @@ func _embeddedReportFatalError(prefix: StaticString, message: StaticString) { } @usableFromInline +@inline(never) func _embeddedReportFatalErrorInFile(prefix: StaticString, message: StaticString, file: StaticString, line: UInt) { print(file, terminator: ":") print(line, terminator: ": ") @@ -529,3 +531,13 @@ func _embeddedReportFatalErrorInFile(prefix: StaticString, message: StaticString if message.utf8CodeUnitCount > 0 { print(": ", terminator: "") } print(message) } + +@usableFromInline +@inline(never) +func _embeddedReportFatalErrorInFile(prefix: StaticString, message: UnsafeBufferPointer, file: StaticString, line: UInt) { + print(file, terminator: ":") + print(line, terminator: ": ") + print(prefix, terminator: "") + if message.count > 0 { print(": ", terminator: "") } + print(message) +} diff --git a/test/embedded/traps-fatalerror-exec2.swift b/test/embedded/traps-fatalerror-exec2.swift new file mode 100644 index 0000000000000..8ac36de64968b --- /dev/null +++ b/test/embedded/traps-fatalerror-exec2.swift @@ -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) diff --git a/test/embedded/traps-fatalerror-ir.swift b/test/embedded/traps-fatalerror-ir.swift index 56108054387eb..cf64221786b5c 100644 --- a/test/embedded/traps-fatalerror-ir.swift +++ b/test/embedded/traps-fatalerror-ir.swift @@ -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: } diff --git a/test/embedded/traps-string-interpolations.swift b/test/embedded/traps-string-interpolations.swift new file mode 100644 index 0000000000000..9fbc711255df3 --- /dev/null +++ b/test/embedded/traps-string-interpolations.swift @@ -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: }