Skip to content
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
4 changes: 4 additions & 0 deletions include/swift/AST/SILOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ class SILOptions {
/// The format used for serializing remarks (default: YAML)
llvm::remarks::Format OptRecordFormat = llvm::remarks::Format::YAML;

/// Are there any options that indicate that functions should not be preserved
/// for the debugger?
bool ShouldFunctionsBePreservedToDebugger = true;

SILOptions() {}

/// Return a hash code of any components from these options that should
Expand Down
4 changes: 4 additions & 0 deletions include/swift/SIL/SILGlobalVariable.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ class SILGlobalVariable
/// might be referenced from outside the current compilation unit.
bool isPossiblyUsedExternally() const;

/// Returns true if this global variable should be preserved so it can
/// potentially be inspected by the debugger.
bool shouldBePreservedForDebugger() const;

/// Get this global variable's serialized attribute.
IsSerialized_t isSerialized() const;
void setSerialized(IsSerialized_t isSerialized);
Expand Down
40 changes: 28 additions & 12 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2242,6 +2242,22 @@ void parseExclusivityEnforcementOptions(const llvm::opt::Arg *A,
}
}

static std::optional<IRGenLLVMLTOKind>
ParseLLVMLTOKind(const ArgList &Args, DiagnosticEngine &Diags) {
std::optional<IRGenLLVMLTOKind> LLVMLTOKind;
if (const Arg *A = Args.getLastArg(options::OPT_lto)) {
LLVMLTOKind =
llvm::StringSwitch<std::optional<IRGenLLVMLTOKind>>(A->getValue())
.Case("llvm-thin", IRGenLLVMLTOKind::Thin)
.Case("llvm-full", IRGenLLVMLTOKind::Full)
.Default(std::nullopt);
if (!LLVMLTOKind)
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(Args), A->getValue());
}
return LLVMLTOKind;
}

static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
IRGenOptions &IRGenOpts, const FrontendOptions &FEOpts,
const TypeCheckerOptions &TCOpts,
Expand Down Expand Up @@ -2629,6 +2645,16 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,

Opts.NoAllocations = Args.hasArg(OPT_no_allocations);

// If these optimizations are enabled never preserve functions for the
// debugger.
Opts.ShouldFunctionsBePreservedToDebugger =
!Args.hasArg(OPT_enable_llvm_wme);
Opts.ShouldFunctionsBePreservedToDebugger &=
!Args.hasArg(OPT_enable_llvm_vfe);
if (auto LTOKind = ParseLLVMLTOKind(Args, Diags))
Opts.ShouldFunctionsBePreservedToDebugger &=
LTOKind.value() == IRGenLLVMLTOKind::None;

return false;
}

Expand Down Expand Up @@ -2975,18 +3001,8 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
}
}

if (const Arg *A = Args.getLastArg(options::OPT_lto)) {
auto LLVMLTOKind =
llvm::StringSwitch<std::optional<IRGenLLVMLTOKind>>(A->getValue())
.Case("llvm-thin", IRGenLLVMLTOKind::Thin)
.Case("llvm-full", IRGenLLVMLTOKind::Full)
.Default(std::nullopt);
if (LLVMLTOKind)
Opts.LLVMLTOKind = LLVMLTOKind.value();
else
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
A->getAsString(Args), A->getValue());
}
if (auto LTOKind = ParseLLVMLTOKind(Args, Diags))
Opts.LLVMLTOKind = LTOKind.value();

if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) {
Opts.SanitizeCoverage =
Expand Down
4 changes: 3 additions & 1 deletion lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2826,6 +2826,8 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
// Mark as llvm.used if @_used, set section if @_section
if (var->markedAsUsed())
addUsedGlobal(gvar);
else if (var->shouldBePreservedForDebugger() && forDefinition)
addUsedGlobal(gvar);
if (auto *sectionAttr = var->getSectionAttr())
gvar->setSection(sectionAttr->Name);
}
Expand Down Expand Up @@ -3695,7 +3697,7 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(

// Also mark as llvm.used any functions that should be kept for the debugger.
// Only definitions should be kept.
if (f->shouldBePreservedForDebugger() && !fn->isDeclaration())
if (f->shouldBePreservedForDebugger() && forDefinition)
addUsedGlobal(fn);

// If `hasCReferences` is true, then the function is either marked with
Expand Down
6 changes: 6 additions & 0 deletions lib/SIL/IR/SILFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,12 @@ bool SILFunction::shouldBePreservedForDebugger() const {
if (getEffectiveOptimizationMode() != OptimizationMode::NoOptimization)
return false;

if (!getModule().getOptions().ShouldFunctionsBePreservedToDebugger)
return false;

if (getModule().getASTContext().LangOpts.hasFeature(Feature::Embedded))
return false;

if (isAvailableExternally())
return false;

Expand Down
10 changes: 10 additions & 0 deletions lib/SIL/IR/SILGlobalVariable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,20 @@ SILGlobalVariable::~SILGlobalVariable() {
}

bool SILGlobalVariable::isPossiblyUsedExternally() const {
if (shouldBePreservedForDebugger())
return true;

SILLinkage linkage = getLinkage();
return swift::isPossiblyUsedExternally(linkage, getModule().isWholeModule());
}

bool SILGlobalVariable::shouldBePreservedForDebugger() const {
if (getModule().getOptions().OptMode != OptimizationMode::NoOptimization)
return false;
// Keep any language-level global variables for the debugger.
return VDecl != nullptr;
}

/// Get this global variable's fragile attribute.
IsSerialized_t SILGlobalVariable::isSerialized() const {
return Serialized ? IsSerialized : IsNotSerialized;
Expand Down
2 changes: 2 additions & 0 deletions test/IRGen/conditional-dead-strip-exec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
@inline(never) func func1_used() { print("func1_used") }

// (2) unused
@_semantics("no.preserve.debugger")
@inline(never) func func2_dead() { print("func2_dead") }

// (3) completely unused
Expand All @@ -35,6 +36,7 @@ protocol TheProtocol { }

// (5) unused class
class MyClass: TheProtocol {
@_semantics("no.preserve.debugger")
func unused_method() {}
}

Expand Down
1 change: 1 addition & 0 deletions test/IRGen/objc_pointers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Foundation
}

// CHECK-LABEL: s13objc_pointers14returnNSObject3objSo0D0CAE_tF
@_semantics("no.preserve.debugger")
func returnNSObject(obj: NSObject) -> NSObject {
// CHECK-NOT: return
// CHECK: @llvm.objc.retain
Expand Down
40 changes: 39 additions & 1 deletion test/IRGen/preserve_for_debugger.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// RUN: %target-swiftc_driver %s -g -Onone -emit-ir | %FileCheck %s


// Check that unused globals are preserved at Onone.

private let number = 42
// CHECK: distinct !DIGlobalVariable(name: "number",

// Check that unused functions are preserved at Onone.
func unused() {
}
// CHECK: !DISubprogram(name: "unused", linkageName: "$s21preserve_for_debugger6unusedyyF"

// Property wrappers generate transparent getters, which we would like to check still exist at Onone.
@propertyWrapper
Expand All @@ -24,5 +31,36 @@ public class User {
let c = User()
c.f()

// CHECK: !DISubprogram(name: "unused", linkageName: "$s21preserve_for_debugger6unusedyyF"
// CHECK: !DISubprogram(name: "number.get"

protocol Foo {}

@propertyWrapper
struct Bar<ObjectType: Foo> {
var storage: ObjectType

public init(wrappedValue: ObjectType) {
storage = wrappedValue
}

public var wrappedValue: ObjectType {
return storage
}

}

class Baz: Foo {
let x = 42
}

struct Qux {
@Bar(wrappedValue: Baz()) private var baz: Baz

func f() {
print(self.baz) // break here
}
}
let qux = Qux()
qux.f()

// CHECK: !DISubprogram(name: "baz.get"
12 changes: 6 additions & 6 deletions test/IRGen/section.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
@_section("__DATA,__mysection") public var g3: Bool = true
@_section("__DATA,__mysection") var g4: UnsafeMutablePointer<Int>? = nil
@_section("__DATA,__mysection") var g5: UnsafeMutablePointer<Int>? = UnsafeMutablePointer(bitPattern: 0x42424242)
@_section("__TEXT,__mysection") func foo() {}
@_section("__TEXT,__mysection") @_used func foo() {}

struct MyStruct {
@_section("__DATA,__mysection") static var static0: Int = 1
@_section("__TEXT,__mysection") func foo() {}
@_section("__TEXT,__mysection") @_used func foo() {}
}

// SIL: @_section("__DATA,__mysection") @_hasStorage @_hasInitialValue var g0: Int { get set }
Expand All @@ -22,10 +22,10 @@ struct MyStruct {
// SIL: @_section("__DATA,__mysection") @_hasStorage @_hasInitialValue public var g3: Bool { get set }
// SIL: @_section("__DATA,__mysection") @_hasStorage @_hasInitialValue var g4: UnsafeMutablePointer<Int>? { get set }
// SIL: @_section("__DATA,__mysection") @_hasStorage @_hasInitialValue var g5: UnsafeMutablePointer<Int>? { get set }
// SIL: @_section("__TEXT,__mysection") func foo()
// SIL: @_section("__TEXT,__mysection") @_used func foo()
// SIL: struct MyStruct {
// SIL: @_section("__DATA,__mysection") @_hasStorage @_hasInitialValue static var static0: Int { get set }
// SIL: @_section("__TEXT,__mysection") func foo()
// SIL: @_section("__TEXT,__mysection") @_used func foo()

// SIL: sil private [global_init_once_fn] [perf_constraint] @$s7section2g0_WZ : $@convention(c)
// SIL: sil hidden [global_init] @$s7section2g0Sivau : $@convention(thin)
Expand All @@ -39,10 +39,10 @@ struct MyStruct {
// SIL: sil hidden [global_init] @$s7section2g4SpySiGSgvau : $@convention(thin)
// SIL: sil private [global_init_once_fn] [perf_constraint] @$s7section2g5_WZ : $@convention(c)
// SIL: sil hidden [global_init] @$s7section2g5SpySiGSgvau : $@convention(thin)
// SIL: sil hidden [section "__TEXT,__mysection"] @$s7section3fooyyF : $@convention(thin)
// SIL: sil hidden [used] [section "__TEXT,__mysection"] @$s7section3fooyyF : $@convention(thin)
// SIL: sil private [global_init_once_fn] [perf_constraint] @$s7section8MyStructV7static0_WZ : $@convention(c)
// SIL: sil hidden [global_init] @$s7section8MyStructV7static0Sivau : $@convention(thin)
// SIL: sil hidden [section "__TEXT,__mysection"] @$s7section8MyStructV3fooyyF : $@convention(method)
// SIL: sil hidden [used] [section "__TEXT,__mysection"] @$s7section8MyStructV3fooyyF : $@convention(method)

// IR: @"$s7section2g0Sivp" = hidden global %TSi <{ {{(i64|i32)}} 1 }>, section "__DATA,__mysection"
// IR: @"$s7section2g1Si_Sitvp" = hidden global <{ %TSi, %TSi }> <{ %TSi <{ {{(i64|i32)}} 42 }>, %TSi <{ {{(i64|i32)}} 43 }> }>, section "__DATA,__mysection"
Expand Down
12 changes: 4 additions & 8 deletions test/IRGen/section_asm.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,22 @@
// CHECK-NOT: .section
// CHECK: $s7section8MyStructV7static0SivpZ:

// CHECKELF: .section{{.*}}"__TEXT,__mysection","ax"
// CHECKELF: .section{{.*}}"__TEXT,__mysection","axR"
// CHECKELF-NOT: .section
// CHECKELF: $s7section3fooyyF:

// CHECKELF: .section{{.*}}"__TEXT,__mysection","ax"
// CHECKELF: .section{{.*}}"__TEXT,__mysection","axR"
// CHECKELF-NOT: .section
// CHECKELF: $s7section8MyStructV3fooyyF:

// CHECKELF: .section{{.*}}"__DATA,__mysection","aw"
// CHECKELF: .section{{.*}}"__DATA,__mysection","awR"
// CHECKELF-NOT: .section
// CHECKELF: $s7section2g0Sivp:
// CHECKELF-NOT: .section
// CHECKELF: $s7section2g1Si_Sitvp:
// CHECKELF-NOT: .section
// CHECKELF: $s7section2g2Sbvp:
// CHECKELF: .section{{.*}}"__DATA,__mysection","awR"
// CHECKELF: $s7section2g3Sbvp:
// CHECKELF: .section{{.*}}"__DATA,__mysection","aw"
// CHECKELF: .section{{.*}}"__DATA,__mysection","awR"
// CHECKELF: $s7section2g4SpySiGSgvp:
// CHECKELF-NOT: .section
// CHECKELF: $s7section2g5SpySiGSgvp:
// CHECKELF-NOT: .section
// CHECKELF: $s7section8MyStructV7static0SivpZ:
3 changes: 3 additions & 0 deletions test/embedded/accessor-unavailable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ struct Foo {
}
}

let foo = Foo()
let _ = foo[5]

// CHECK: $s4main3FooVyS2icig
// CHECK-NOT: $s4main3FooVyS2icis
2 changes: 1 addition & 1 deletion test/embedded/value-type-deinits.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public struct Foo<Element> : ~Copyable {

import MyModule

func test() {
public func test() {
createFoo(x: 1)
}

Expand Down