16 bit support in swift compiler

As much as I'd like for option 1 (avoiding whole stdlib compiled) to work, my understanding from trying to add WebAssembly support is that this is hard to implement. A lot of basic Swift runtime stuff depends on stdlib as well as libc, libc++ and ICU, for example memory allocation, threading primitives for atomic ARC, unicode support in String and Character types etc.

I'm currently slowly proceeding with cross-compiling those libraries to WebAssembly, which is basically option 2. For AVR support, I'd recommend looking into AVR Libc Home Page and GitHub - jypma/AvrLib: Highly type-safe modern C++ library for AVR

Hey Max,

I already have working programs that don't use any stdlib. I demonstrated them at WWDC to the Apple engineers, who loved them. It requires that you must not use String, Character, ARC, structs, enums, classes and many other things but it works. And this is for an atmega328p, a device with 2048 bytes of RAM so most of those things are a bad idea anyway.

But it definitely can be done... because I've done it! At the moment I use a standard OSX swiftc to output LLVM IR then feed that into a custom build LLC.exe from LLVM for output to AVR machine code.

Carl

2 Likes

Very interesting, I'm wondering how is it possible to write code even without structs and enums? I'm not saying it's impossible, on the contrary, thinking if it would make sense to define some kind of restricted subset of Swift that doesn't rely on heap allocations that is usable on these restricted platforms with no standard library.

Ideally would be great to have something very similar to Rust's #![no_std] flag, which easily allows you to compile without standard library and runtime.

1 Like

It has been a challenge at times for sure. In the end the code is probably a bit more like rust... a sort of friendly, type safe version of C. :slight_smile:

1 Like

I stumbled over this folder swift/validation-test/stdlib/MicroStdlib at main ยท apple/swift ยท GitHub yesterday, so that might be a hint for what parts of the stdlib are absolutely required (even though they'd need more implementation than what is there, i guess).

@carlos4242 I would be very interested in what code without using the stdlib looks like. I tried to do the same but couldn't get anywhere. Could you share an example?

1 Like

@ahti, I should clarify... The code I compile in the Swift for Arduino project (www.swiftforarduino.com) does (sort of) use at least a tiny bit of the stdlib, because in being compiled to LLVM IR, it uses various Int and pointer types. But the salient point is none of that makes it into the LLVM IR, the IR has already been translated through SIL and optimised so that the resulting code ends up compiled to just use "native" LLVM IR fundamental types. It does not have any calls to stdlib functions left, everything is inlined by the time it reaches IR and you end up with something that looks like it could have come from Rust or C++. All of that is done within the swiftc front end, the final "code" output using --emit-llvm has no stdlib left in it, if that makes sense?

TBH, I naively didn't even realise the above process was occurring when I first made S4A. I just put in types like UInt8 and didn't know it was going via inlined stdlib code and Builtin.Int types. The compiler and optimiser elided all of that as it should, leaving sensible types in the bitcode.

Here's an example of a more complex swift file used by Swift for Arduino from the community site: https://github.com/swiftforarduino/community/blob/master/MPL31155A2.swift

It's the "driver" code for an MPL31155A2 sensor, to allow the developer to get the current altitude or temperature, etc. from the sensor. (Note, it calls into my AVR library for functions to communicate with I2C but you can still easily figure out how it works.) So this code works on an Arduino without and stdlib functions being linked in. The swiftc front end thinks it's writing code for x86_64 and uses that stdlib to output llvm IR, then the llc.exe back end creates object files from the IR but because I'm not using structs, enums, ARC, etc. it doesn't need to link any stdlib in, which is necessary because I don't have an AVR version of stdlib and probably never will (as described above). For now, because I don't need these extra features/functions, I'm trying to "trick" the toolchain into compiling without stdlib, which is how all this conversation started. I hope that makes sense?

2 Likes

Ah, so you're only using the parts of the stdlib that are @_transparent and don't need the runtime I guess?

Actually the stdlib might not be the most important part of the puzzle. So getting the runtime is probably a good first step to porting Swift to a new platform, as the stdlib (correct me if I'm wrong here) seems to be mostly abstractions over LLVM instructions and the runtime (plus icu?) anyway.

Sounds about right. I'm still coming to terms with what the runtime, stdlib etc. all are when it comes to swift (and in general really because I'm new to compiler internals). From what I saw, all the code I'm using was @_transparent, yes.

There shouldn't be any need to avoid structs and enums, as long as they don't transitively include any fields that require runtime support for reference counting or other features. All of the "primitive" types like Int and Bool are structs in Swift. If you were taking x86-64 IR and trying to compile it for AVR, you may have run into problems with compound types, but those should go away once you're properly targeting AVR in IRGen. You should be able to use generics as long as they get fully specialized by the optimizer too.

1 Like

Yeah, WebAssembly is a more fully-featured platform than AVR, so I think your best bet to get Swift targeting wasm is to port the runtime in order to support the "full" language. You may be able to at least subset out some of the Unicode processing parts of String to defer the ICU dependency.

Yes, it's a funny one! So I've found out by trial and error what is supported by this hacked together toolchain. Classes i don't expect to work without some library support due to reference counting but I did expect structs and enums to work.

It turns out the reason the LLVM IR produced by swiftc from structs and enums cannot be compiled to AVR machine code is all down to pointer sizes. The LLVM IR has a few places where fixups are created that refer to some struct metadata or something. The hacked together toolchain I'm using has a mac OSX (x86_64) version of swiftc that outputs pointer types that are i64*. The AVR backend doesn't know how to lower them properly to AVR machine code (AVR has 16 bit pointers) so although it does a pretty heroic job with other pointers (truncating them etc.) it breaks on the fixups.

This is why I started the whole chain. I can get structs and enums to work if I can just get swiftc to output LLVM IR with 16 bit pointers instead of 64 bit!

For a "no_std" build without any runtime support, it might be interesting to add a flag to disable metadata generation altogether, since for AVR you probably need the compiler to eliminate any runtime calls that would ever use the metadata, so it should be unnecessary.

1 Like

To add to that, you really want to have Optional since much of the syntax in the language relies on Optional for it to work. For instance, if you want for-in loops, you need Optional.

@Joe_Groff oh yes, that would be great! The metadata is just getting in the way of my build chain. I actually use a simple text processing script to strip some of it out at the moment, haha.

That's interesting. I haven't tried for...in loops yet. I have avoided optionals because they're enums which aren't yet working in my toolchain, I think due to pointer issues as described above.

Using Optional shouldn't have any metadata problems because the metadata is emitted as part of the standard library, so it wouldn't show up in your IR.

Sounds good. Is there a simple way to prevent metadata from being emitted from swiftc or will i need to make a patch?

Cheers,
Carl

There isn't a way to do it currently, but it should be straightforward to add a flag and have IRGen skip metadata emission when it's passed.

Cool. I'll look into it. :slight_smile: