Generating SIL from another Frontend

Hello everyone,

We are writing a compiler for a dialect of Swift, in Swift. If this sounds like we're trying to bootstrap Swift, it's because we kind of are, but without the ambition to support everything Swift currently does. Although we decided not to reuse most of the frontend, to have more control over the syntax and the sema, the plan is to eventually converge to Swift's toolchain, first to reinvent only part of the wheel, and second to hopefully maximize compatibility.

Without getting too much into details, our plan is for our language to support some constructions to enforce immutability, reference uniqueness (a kind of featherweight version of Rust't type-system) and behavioral specifications (think Eiffel-like contracts).

In an ideal world, it would be possible to call Swift functions or use Swift types from our language and vice versa. I see three options to reach that objective:

  1. Transpile our dialect to regular Swift, in a similar way as a most Javascript dialects do (e.g. typescript, coffeescript, ...). This is probably the easiest option and a good solution for a POC. However it's unclear how we could trace debug info back to our original source without getting lost in glue code.
  2. Generate LLVM IR that could be linked against Swift. This is probably the least promising choice, both in terms of difficulty and restrictions. In particular, I expect that seamlessly exchanging high-level data types would be an intractable task.
  3. Generate SIL after having translated all non-swift features into compatible constructions. This might be equally difficult as emitting IR, but without the same restrictions on high-level data types. I also expect that it would free us from writing very clunky interfaces to shim our language's features, as we could handle assignment semantics at the SIL level for a smoother integration.

It follows that I'm currently looking how feasible the third option actually is. To be precise, the goal would be to wrap the SILGen phase so that we could drive it from Swift. It is my understanding that SILGenModule is an AST visitor, and that is is deeply coupled to an AST context (to build types, etc.). Hence it doesn't seem like we could use it directly with our own AST structure. Instead, I guess we could try to write SIL files directly and feed it to the compiler (it is my understanding that its textual form can be parsed).

I would like to collect opinions about the overall feasibility of this endeavor, in particular compared to the first option that consists of translating high-level Swift code directly.

1 Like

Of the target choices you lay out (Swift code, SIL, and LLVM IR), only Swift has any source stability guarantees, so targeting SIL or LLVM IR will impose some additional maintenance burden on you as the infrastructure shifts. Targeting LLVM IR would also obligate you to reimplement most of Swift's ABI, which I don't think would be worth the effort if your goal is to only implement a dialect of Swift. Either SIL or LLVM IR is also easiest to generate and manipulate using the C++ APIs, and their support for textual representation is somewhat secondary. If you want to write your tool in Swift, that will be an additional barrier. Swift source code supports #sourceLocation directives in source code, similar to #line in C, so that code generators can map generated code back to source locations in the generator's language.

As another option, the things you describe (effect control, ownership, and behavioral specification) are all relatively near-term interests for Swift itself. You might want to consider building these features into the Swift compiler itself, and working with the community to make them features everyone can use, instead of trying to make a whole new language that targets Swift.

11 Likes