Skip to content

Commit 0affaaf

Browse files
committed
[embedded] Expand on the Embedded Swift documentation, fill in several TODOs
1 parent 31f821a commit 0affaaf

File tree

3 files changed

+135
-29
lines changed

3 files changed

+135
-29
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Embedded Swift -- Status
2+
3+
**⚠️ Embedded Swift is experimental. This document might be out of date with latest development.**
4+
5+
**‼️ Use the latest downloadable 'Trunk Development' snapshot from swift.org to use Embedded Swift. Public releases of Swift do not yet support Embedded Swift.**
6+
7+
For an introduction and motivation into Embedded Swift, please see "[A Vision for Embedded Swift](https://github.com/apple/swift-evolution/blob/main/visions/embedded-swift.md)", a Swift Evolution document highlighting the main goals and approaches.
8+
9+
## Embedded Standard Library Breakdown
10+
11+
This status table describes which of the following standard library features can be used in Embedded Swift:
12+
13+
| **Swift Standard Library Feature** | **Currently Supported In Embedded Swift** |
14+
|---------------------------------------------|-----------------------------------------------------|
15+
| Array (dynamic heap-allocated container) | Yes |
16+
| Array slices | Yes |
17+
| assert, precondition, fatalError | Partial, only StaticStrings can be used as a failure message |
18+
| Bool, Integer types, Float types | Yes |
19+
| Codable, Encodable, Decodable | No |
20+
| Collection + related protocols | Yes |
21+
| Collection algorithms (sort, reverse) | Yes |
22+
| CustomStringConvertible, CustomDebugStringConvertible | No |
23+
| Dictionary (dynamic heap-allocated container) | Yes |
24+
| FixedWidthInteger + related protocols | Yes |
25+
| Hashable, Equatable, Comparable protocols | Yes |
26+
| InputStream, OutputStream | No |
27+
| Integer parsing | No |
28+
| KeyPaths | No |
29+
| Lazy collections | No |
30+
| Mirror | No, intentionally unsupported long-term |
31+
| Objective-C bridging | No, intentionally unsupported long-term |
32+
| Optional | Yes |
33+
| print / debugPrint | Partial (only StaticStrings and integers) |
34+
| Range, ClosedRange, Stride | Yes |
35+
| Result | Yes |
36+
| Set (dynamic heap-allocated container) | Yes |
37+
| SIMD types | Yes |
38+
| StaticString | Yes |
39+
| String (dynamic) | No (work in progress) |
40+
| String Interpolations | No (work in progress) |
41+
| Unicode | No |
42+
| Unsafe\[Mutable\]\[Raw\]\[Buffer\]Pointer | Yes |
43+
| VarArgs | No |
44+
45+
## Non-stdlib Features
46+
47+
This status table describes which of the following Swift features can be used in Embedded Swift:
48+
49+
| **Swift Feature** | **Currently Supported In Embedded Swift** |
50+
|---------------------------------------------|-----------------------------------------------------|
51+
| Swift Concurrency | Partial, experimental (basics of actors and tasks work in single-threaded concurrency mode) |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Embedded Swift -- Integrating with embedded SDKs
2+
3+
**⚠️ Embedded Swift is experimental. This document might be out of date with latest development.**
4+
5+
**‼️ Use the latest downloadable 'Trunk Development' snapshot from swift.org to use Embedded Swift. Public releases of Swift do not yet support Embedded Swift.**
6+
7+
For an introduction and motivation into Embedded Swift, please see "[A Vision for Embedded Swift](https://github.com/apple/swift-evolution/blob/main/visions/embedded-swift.md)", a Swift Evolution document highlighting the main goals and approaches.
8+
9+
The following document sketches how to integrate Swift code into some popular embedded platforms' SDKs and build systems.
10+
11+
## Integrating with Raspberry Pi Pico (W) build system:
12+
13+
*TODO, documentation under construction.*

docs/EmbeddedSwift/UserManual.md

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,42 +33,42 @@ Most of these steps are out of scope for this document, instead refer to the ven
3333
A basic way to build a set of Swift source files in Embedded Swift mode, is to simply give the compiler (1) a target triple, (2) the `-enable-experimental-feature Embedded` flag, (3) the set of source files that form the input module:
3434

3535
```bash
36-
$ swiftc -target <target triple> -enable-experimental-feature Embedded \
36+
$ swiftc -target <target triple> -enable-experimental-feature Embedded -wmo \
3737
input1.swift input2.swift ... -c -o output.o
3838
```
3939

40-
The target triple also decides whether whether the output object file will be an ELF file, or a Mach-O. For example:
40+
## Examples
41+
42+
### Building Swift firmware for an embedded target
43+
44+
To build Swift firmware (for now ingnoring integration with SDKs, libraries and other pre-existing C code), we can use the `-target` argument to specify the CPU architecture. The target triple also decides whether the output object file will be an ELF file, or a Mach-O. For example:
4145

4246
```bash
4347
# To build an ARMv7 Mach-O object file:
44-
$ swiftc -target armv7-apple-none-macho -enable-experimental-feature Embedded \
48+
$ swiftc -target armv7-apple-none-macho -enable-experimental-feature Embedded -wmo \
4549
input1.swift input2.swift ... -c -o output.o
4650

4751
# To build an ARMv7 ELF object file:
48-
$ swiftc -target armv7-unknown-none-eabi -enable-experimental-feature Embedded \
52+
$ swiftc -target armv7-unknown-none-eabi -enable-experimental-feature Embedded -wmo \
4953
input1.swift input2.swift ... -c -o output.o
5054
```
5155

52-
Additionally, you probably want to specify additional Clang and/or LLVM flags to get the compiler to produce code for the exact ISA and ABI you need for your target. For example, a Raspberry Pi Pico / Pico W expects the `-mfloat-abi=soft` Clang option, and if you want to match ABI with libraries built with the GNU toolchain, you might also need `-fshort-enums`. To pass those to Swift, use the `-Xcc` prefix:
56+
Additionally, you probably want to specify additional Clang and/or LLVM flags to get the compiler to produce code for the exact ISA and ABI you need for your target.
57+
58+
For example, a Raspberry Pi Pico / Pico W should target the ARMv6-M architecture via the `armv6m-*` target triple, but the `-mfloat-abi=soft` Clang option should also be used, and if you want to match ABI with libraries built with the GNU toolchain, you might also need `-fshort-enums`. To pass those to Swift, use the `-Xcc` prefix:
5359

5460
```bash
5561
# To build an ELF object file for ARMv6-M with soft float ABI (floating-point arguments passed in integer registers) and "short enums":
56-
$ swiftc -target armv6m-unknown-none-eabi -enable-experimental-feature Embedded \
62+
$ swiftc -target armv6m-unknown-none-eabi -enable-experimental-feature Embedded -wmo \
5763
-Xcc -mfloat-abi=soft -Xcc -fshort-enums \
5864
input1.swift input2.swift ... -c -o output.o
5965
```
6066

6167
This might not be obvious: `-Xcc` flags are typically only used to alter behavior of the Clang importer, but passing flags to Clang this way also works to specify LLVM target options like selecting a specific CPU architecture (`-march`, `-mcpu`, `-mmcu`), FPU unit availability (`-mfpu`), which registers are used to pass floating-point values (`-mfloat-abi`), and others.
6268

63-
## Examples
64-
65-
### Building a pure Swift firmware for an embedded target
66-
67-
TODO
68-
6969
### Integrating with embedded SDKs and build systems
7070

71-
TODO
71+
For details and concrete examples of how to integrate with existing SDKs, see [Embedded Swift -- Integrating with embedded SDKs](IntegratingWithSDKs.md).
7272

7373
### Building a macOS Embedded Swift program:
7474

@@ -109,30 +109,72 @@ Features that are not available:
109109
- **Not available**: Metatypes, e.g. `let t = SomeClass.Type` or `type(of: value)` are not allowed.
110110
- **Not available yet (under development)**: The print() function for types other than StaticString and integers.
111111
- **Not available yet (under development)**: String. (StaticString **is** available).
112-
- **Not available yet (under development)**: Set.
113-
- **Not available yet (under development)**: Dictionary.
112+
- **Not available yet (under development)**: Swift Concurrency.
113+
114+
For a more complete list of supported features in Embedded Swift, see [Embedded Swift -- Status](EmbeddedSwiftStatus.md).
114115

115116
## Libraries and modules in Embedded Swift
116117

117-
TODO
118+
Traditional library build and use model of Swift is that library code is compiled into a .swiftmodule, containing the interfaces, and a compiled library with binary code, either a .a static library or a .dylib/.so dynamic library. A client's build then uses the .swiftmodule at compile-time, and the static/dynamic library at link-time.
118119

119-
## Allocating and non-allocating Embedded Swift mode
120+
The library model in Embedded Swift works slightly differently: All Swift source code of a library is promoted into being inlineable and visible to client builds (this is necessary for generic code, and beneficial for optimizations for non-generic code), and ends up serialized into the .swiftmodule, the interface of the library. Therefore, the compiled code of a library is never needed, and doesn't even need to be produced. For example:
120121

121-
TODO
122+
```
123+
# Build the library, only as a .swiftmomodule. Notice that we never build the .o or .a for the library.
124+
$ swiftc -target <target> -enable-experimental-feature Embedded -wmo \
125+
a.swift b.swift -module-name MyLibrary -emit-module -emit-module-path ./MyLibrary.swiftmodule
122126
123-
## Runtime support for allocating Embedded Swift
127+
# Build the client, "-I ." add the current directory to the module search path list
128+
$ swiftc -target <target> -enable-experimental-feature Embedded -wmo \
129+
client.swift -I . -c -o client.o
130+
```
124131

125-
TODO
132+
The Embedded Swift standard library is distributed in the toolchain the same way: It's strictly a .swiftmodule without any compiled code present anywhere. All the compiling into machine code is performed as part of the client's build. This has the major benefit that the client's build can provide additional ABI and ISA defining flags, such as the above-mentioned `-mfloat-abi`, `-fshort-enums`, `-mcpu`, `-march` flags, and these flags in the client's build will apply to all the library code (including standard library code) as well.
126133

127-
## External dependencies
134+
## Allocating and non-allocating Embedded Swift mode
135+
136+
Embedded Swift does allow instantiating and using reference types (classes) which are refcounted objects allocated on the heap. A common case of needing those is for dynamic containers like arrays and sets (they use dynamically-sized heap-allocated class instances as their storage). Outside of creating class instances and explicitly calling allocation APIs (e.g. `UnsafeMutablePointer.allocate()`), Embedded Swift does not perform allocations or cause heap usage.
128137

129-
Embedded Swift minimizes external dependencies, but they still exist. In Embedded Swift compilation mode, the compiler only triggers external dependencies based on actual usage of the program under compilation. The following table lists which situations cause which external dependencies:
138+
Some embedded platforms don't have and/or don't want *any heap allocations whatsoever* and don't provide a heap at all. The `-no-allocations` compiler flag can be used to match that, which will cause the compiler to produce an error at compile time when creating class instances or calling allocation APIs.
130139

131-
| | external dependencies: |
132-
|---------------------------------------------------------------------|-------------------------------------------------------------------------|
133-
| *(basic dependencies, added by the compiler in various situations)* | `void *memset(void *, int, size_t);` |
134-
| instantiating a class, or using UnsafePointer.allocate() | `int posix_memalign(void **, size_t, size_t);`<br/>`void free(void *);` |
135-
| using print() | `int putchar(int);` |
136-
| using Hashable, Set, Dictionary, or random-number generating APIs | `void arc4random_buf(void *, size_t);` |
140+
```bash
141+
$ cat test.swift
142+
let p = UnsafeMutablePointer<UInt8>.allocate(capacity: 10)
143+
$ swiftc test.swift -enable-experimental-feature Embedded -wmo -no-allocations
144+
test.swift:1:37: error: cannot use allocating operation in -no-allocations mode
145+
```
146+
147+
## External dependencies
137148

138-
The user and/or the platform is expected to provide these well-known APIs.
149+
Embedded Swift minimizes external dependencies (i.e. functions that need to be available at link-time), but they still exist. There are generally two categories of dependencies: (1) functions that the Swift standard library or Embedded Swift runtime need to call, and (2) functions/symbols that are implicitly added by LLVM and the compiler pipeline.
150+
151+
For (1), external dependencies are only used based on actual usage of the program under compilation:
152+
153+
- instantiating a class, or using UnsafeMutablePointer.allocate()
154+
- dependency: `int posix_memalign(void **, size_t, size_t);`
155+
- dependency: `void free(void *);`
156+
- using print()
157+
- dependency: `int putchar(int);`
158+
- using Hashable, Set, Dictionary, or random-number generating APIs
159+
- dependency: `void arc4random_buf(void *, size_t);`
160+
161+
For (2), external dependencies are also triggered by specific code needing them, but they are somewhat lower-level patterns where it might not be obvious that such patterns should cause external dependencies:
162+
163+
- **basic memory copying and zeroing functions**
164+
- usage added for a variety of reasons (e.g. using structs on the stack)
165+
- dependency: `void *memset(void *, int, size_t);`
166+
- dependency: `void *memcpy(void *, const void *, size_t);`
167+
- **stack protectors** (aka stack cookies or stack canaries)
168+
- dependency: `void *__stack_chk_guard;`
169+
- dependency: `void __stack_chk_fail(void);`
170+
- stack protectors can be disabled with `-disable-stack-protector` swiftc flag
171+
- **atomics instrinsics**
172+
- on CPU architectures that don't have direct load-acquire/store-release support in the ISA, LLVM calls helper functions for atomic operations
173+
- needed by refcounting in the Embedded Swift runtime (so any class usage will trigger this dependency)
174+
- **multiplication/division/modulo instrinsics**
175+
- on CPU architectures that don't have direct support for the math operations in the ISA
176+
- dependency (on Mach-O): `__divti3`
177+
- dependency (on Mach-O): `__modti3`
178+
- dependency (with EABI): `__aeabi_ldivmod`
179+
180+
The user and/or the platform (via basic libraries like libc or compiler builtins) is expected to provide these well-known APIs.

0 commit comments

Comments
 (0)