Isolate the Yul Compiler as a Standalone Backend

Many developers (myself included) are working on new domain-specific languages (DSLs) or alternative smart contract languages that target the EVM. Yul is a great intermediate representation, but today the Yul backend is deeply coupled with the Solidity compiler frontend.

This coupling makes it hard to reuse the Yul optimizer, codegen, and verification pipeline without pulling in all of solc.

In practice, this limits Yul’s ecosystem growth and discourages experimentation with new frontends.

I just finished reading the “Road to Core Solidity” post — it’s great to see renewed focus on the compiler internals and long-standing backend issues.

(Still patiently waiting for a reply to my earlier post :sweat_smile:)

I’m currently building Ora, a compile-time–verified smart contract language written in Zig, targeting the EVM through Yul. Ora currently integrates with solc and Yul directly, but the current structure makes it difficult to reuse the optimizer, codegen, and verification pipeline without depending on the full Solidity compiler stack.

From my perspective, one of Yul’s greatest strengths is its simplicity and verifiability — it’s a clean, transparent IR that’s close to the EVM. I’m a bit concerned that moving Yul’s backend toward SSA/CFG form could make it harder for external tools and experimental compilers to interoperate with it, especially if the transformation pipeline becomes more opaque or tightly coupled to Solidity’s internals. What would really help the broader ecosystem is a standalone Yul backend — essentially a reusable libyul or libsolc-backend that exposes the optimizer and codegen as a clean API. That would allow other languages (like Zig-based Ora, or future Rust/TypeScript compilers) to emit canonical Yul IR and leverage Solidity’s optimization and verification infrastructure without having to pull in the entire solc pipeline.

I’d be happy to help prototype or collaborate on such a module from a language developer’s perspective. It feels like a natural evolution for Yul — turning it from an internal IR into a shared backend standard for the EVM ecosystem.

Hello! Great to hear about a new language in the ecosystem. I guess it’s https://www.oralang.org?

Modularization of the compiler is definitely one of the things we’d like to do at some point. The compiler is already largely structured as independent components and we’ve long been thinking about introducing stronger interfaces between them. Especially now with Core Solidity, SSA-CFG and attempts from other Solidity compilers to plug into our pipeline, having a generic pipeline with well-defined extension points would make combining things much easier. It would also allow us to have some of these components written in languages other than C++ without rewriting the whole compiler.

My current idea for that is to have a set of self-contained components with something akin to Standard JSON on the boundaries between them. Each one would take sources in some form (Solidity, Yul, AST, evmasm, CFG) plus compilation settings and produce a series of outputs. Inputs/outputs would be typed, allowing you to connect them differently, as long as the types match. E.g. you could swap out the codegen by having a different implementation that still consumes Solidity AST and produces Yul. Or replace the frontend with a new one as long as both produce Yul (which is what we’d like to do with Core Solidity). Or plug in a new optimizer like GASOL taking and returning evmasm on both ends. Or even swap out Yul completely for a different IR (say, Sonatina or LLVM IR) by having a component that accepts the AST and produces evmasm.

Now, when that will materialize is still an open question. Personally, I think this may be the direction we’ll go to integrate Core Solidity into solc, but it still needs design work before we can say for sure.

In the shorter-term, the question is what are the biggest obstacles for you the way things work now? The Solidity/Yul boundary is the most defined one in the compiler right now. Yul is exposed in the CLI/Standard JSON as a distinct language and the interface is stable, unlike some others (AST, evmasm). Internally it’s pretty well encapsulated by YulStack. We also always tried to ensure that there is no strong coupling between Yul and Solidity frontend - i.e. you should always be able to take out the IR produced by the codegen, and use solc --strict-assembly on it to get the same exact result you’d get doing it in one step.

You’re saying you don’t want the pipeline to become more opaque. Why? I’d say that actually opaqueness - the kind you get by just putting in Yul sources+settings and not caring about exact steps - should be helpful in isolating you from changes in the compiler. Based on what you wrote, is it simply that you cannot take advantage of this because YulStack is not an interface you can easily use from Zig and you’re instead forced to run all the Yul compilation steps manually? Would a stateless C-level interface that wraps YulStack be a viable solution for you?

1 Like

Thanks a lot for the detailed reply.

You’re right that the Yul boundary is the most stable and well-defined part of the pipeline right now, and that’s exactly why I’ve been building on top of it.

Ora already makes heavy use of the Yul pipeline — the current architecture follows:

Ora → MLIR → Yul → Bytecode

This means Yul plays a central role in my compiler backend. Ora lowers its own MLIR into canonical Yul, and then depends on Solidity’s Yul optimizer and codegen to produce verified EVM bytecode. It’s a tight integration, but it also means we need consistent, reliable access to the Yul pipeline.

Even so, for Ora to use Yul as a backend today, I still need to compile and bundle the entire solc stack. That’s workable, but quite heavy for a language project — especially since Zig already provides a powerful build system and seamless C interop. Other languages don’t necessarily have that infrastructure, which makes integration even harder.

A stateless C-level interface around the Yul pipeline (or YulStack) would be a great step forward. It would allow compilers written in Zig, C++, or even Go to directly emit and optimize Yul without depending on the full Solidity build or CLI process. That would dramatically lower the barrier for experimental frontends.

This is the classic case of “don’t reinvent the wheel” — C should be the default interface layer for compiler interoperability.

My main concern isn’t just about opacity; it’s about ecosystem scope. Yul has organically become the de facto IR for the EVM, and I think it would benefit everyone if it stayed that way. If the compiler split between Classic and Core Solidity deepens, I worry Yul might become increasingly enshrined within Solidity itself, making it harder for external projects to reuse or contribute to it as a shared, language-agnostic component.

From my perspective, Yul should evolve as a stable, verifiable, and shared IR across the EVM ecosystem — one that Solidity, Vyper (even if they don’t currently use it), Ora, and future compilers can all target with confidence.

I’d be happy to collaborate or help test a C-level interface proposal when the time comes. The approach you described with typed component boundaries and swappable interfaces aligns very closely with how Ora’s pipeline is structured internally, so integration or prototyping would be straightforward on my end.

For this discussion, I’m using the terms Yul and IR interchangeably.

Yes, but is docs, so you know how easy is to get out of sync with the compiler itself :face_with_bags_under_eyes::face_with_bags_under_eyes::face_with_bags_under_eyes:

If the compiler split between Classic and Core Solidity deepens, I worry Yul might become increasingly enshrined within Solidity itself, making it harder for external projects to reuse or contribute to it as a shared, language-agnostic component.

Well, things are going in the opposite direction. Yul is a common interoperability layer and we want to keep it that way. We intentionally keep it separate to allow other projects to build on top of it. Projects like Fe or Yolc already depend on it, so it’s not going to suddenly change.

Still, we have no plans at the moment to make the separation stronger than just defining better interfaces around it. Definitely not to the point of extracting it into a separate repo. It’s not that it’s necessarily a bad idea, but it won’t work in a vacuum. While it’s being developed by the same team as solc and primarily used by Solidity, it’s just overhead that will only slow down development. It has to be the other way around - when there’s enough interest across multiple compilers to form a cross-compiler team to maintain it separately from Solidity, then we can start thinking about it.

And Yul is actually a substantial part of our backend and the place where the most of Solidity development is happening right now. When we finally deprecate the legacy evmasm codegen, it will become pretty much the only part aside from bytecode generation.

I’d be happy to collaborate or help test a C-level interface proposal when the time comes. The approach you described with typed component boundaries and swappable interfaces aligns very closely with how Ora’s pipeline is structured internally, so integration or prototyping would be straightforward on my end.

Good to hear that. I will let you know when we get to that task. We won’t be doing that immediately, because there’s a bunch of other things on our roadmap, but I’d like to address it before we get to the point where Core Solidity is ready for a production implementation. My plan is to start a design doc describing how we’ll define the interfaces and modularize the pipeline and then ask for some feedback. When I have something to share, I’ll cross-post it here.

1 Like