Skip to content

[embedded] Mark all functions as 'nounwind' in embedded Swift, add dependency tests #70344

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 7 commits into from
Dec 12, 2023
6 changes: 6 additions & 0 deletions lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,12 @@ void IRGenModule::constructInitialFnAttributes(
Attrs.addAttribute(llvm::Attribute::StackProtectReq);
Attrs.addAttribute("stack-protector-buffer-size", llvm::utostr(8));
}

// Mark as 'nounwind' to avoid referencing exception personality symbols, this
// is okay even with C++ interop on because the landinpads are trapping.
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
Attrs.addAttribute(llvm::Attribute::NoUnwind);
Copy link
Contributor

@hyp hyp Dec 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that applied only to Swift functions, and not the imported C++ functions? In this case you can enable nounwind for Swift functions even when C++ interop is enabled, as exceptions are always caught at the boundary between Swift and a call to C++.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right, this only applies to SILFunctions, not imported C++ functions. @eeckstein is that correct?

}
}

llvm::AttributeList IRGenModule::constructInitialAttributes() {
Expand Down
6 changes: 6 additions & 0 deletions lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1846,6 +1846,12 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f)
CurFn->addFnAttr(llvm::Attribute::NoInline);
}

// Mark as 'nounwind' to avoid referencing exception personality symbols, this
// is okay even with C++ interop on because the landinpads are trapping.
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
CurFn->addFnAttr(llvm::Attribute::NoUnwind);
}

auto optMode = f->getOptimizationMode();
if (optMode != OptimizationMode::NotSet &&
optMode != f->getModule().getOptions().OptMode) {
Expand Down
68 changes: 68 additions & 0 deletions test/embedded/dependencies.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -parse-as-library -enable-experimental-feature Embedded %s -c -o %t/a.o

// RUN: grep DEP\: %s | sed 's#// DEP\: ##' | sort > %t/allowed-dependencies.txt

// Linux/ELF doesn't use the "_" prefix in symbol mangling.
// RUN: if [ %target-os == "linux-gnu" ]; then sed -E -i -e 's/^_(.*)$/\1/' %t/allowed-dependencies.txt; fi

// RUN: %llvm-nm --undefined-only --format=just-symbols %t/a.o | sort | tee %t/actual-dependencies.txt

// Fail if there is any entry in actual-dependencies.txt that's not in allowed-dependencies.txt
// RUN: test -z "`comm -13 %t/allowed-dependencies.txt %t/actual-dependencies.txt`"

// DEP: ___stack_chk_fail
// DEP: ___stack_chk_guard
// DEP: _free
// DEP: _memset
// DEP: _putchar
// DEP: _posix_memalign

// RUN: %target-clang -x c -c %S/Inputs/print.c -o %t/print.o
// RUN: %target-clang %t/a.o %t/print.o -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s

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

@_silgen_name("putchar")
func putchar(_: UInt8)

public func print(_ s: StaticString, terminator: StaticString = "\n") {
var p = s.utf8Start
while p.pointee != 0 {
putchar(p.pointee)
p += 1
}
p = terminator.utf8Start
while p.pointee != 0 {
putchar(p.pointee)
p += 1
}
}

class MyClass {
func foo() { print("MyClass.foo") }
}

class MySubClass: MyClass {
override func foo() { print("MySubClass.foo") }
}

@main
struct Main {
static var objects: [MyClass] = []
static func main() {
print("Hello Embedded Swift!")
// CHECK: Hello Embedded Swift!
objects.append(MyClass())
objects.append(MySubClass())
for o in objects {
o.foo()
}
// CHECK: MyClass.foo
// CHECK: MySubClass.foo
}
}