Yul: IR for Solidity and EVM Bytecode
- Yul is a structured intermediate representation bridging high-level Solidity source and low-level EVM bytecode with explicit control flows and lexical scopes.
- It serves as the primary layer for implementing source-to-source transformations, enabling precise backend optimizations and formal verification.
- Empirical datasets and mechanized formal proofs of Yul facilitate advanced security analyses and support IR-centric research in smart contract development.
Yul is the intermediate representation used by the Solidity compiler on the path from high-level Solidity source code to Ethereum Virtual Machine bytecode. In the compilation path described in recent work, Solidity source is translated into Yul and only then lowered to EVM bytecode, making Yul the principal layer at which backend optimization, low-level reasoning, and a substantial portion of formal analysis naturally concentrate. It is a deliberately small, structured, dialect-parametric language: simpler than Solidity, more structured than raw bytecode, and close enough to the execution model of Ethereum to support compiler research, verification, and security tooling (Koutavas et al., 2024, Fonal, 23 Jun 2025, Coglio et al., 25 Jul 2025).
1. Position in the Solidity–EVM compilation path
Yul occupies the middle layer between Solidity and deployed EVM bytecode. The relationship is explicitly described as:
Solidity source Yul IR EVM bytecode
This placement is not incidental. Recent work characterizes Yul as the layer where most of the Solidity compiler’s optimization passes are performed, while the front-end translation from Solidity to Yul and the back-end lowering from optimized Yul to EVM bytecode are comparatively simple. That architecture concentrates compilation complexity into an IR that remains structured enough for analyses and transformations, yet reflects code-generation choices relevant to the EVM (Fonal, 23 Jun 2025, Coglio et al., 25 Jul 2025).
A recurring misconception is that Yul is merely a textual form of bytecode. The literature rejects that characterization. Yul is described instead as a structured IR with blocks, local variables, functions, loops, conditionals, and switches, avoiding the stack-level and jump-oriented opacity of raw EVM code. For the same reason, it should not be reduced to the role of “inline assembly syntax,” even though Solidity uses Yul for inline assembly and some developers write performance-critical contracts directly in Yul to obtain finer control over generated bytecode.
Yul is also dialect-parametric. The core language does not itself fix concrete value types or primitive operations; these are supplied by a dialect, the currently important instance being the EVM dialect. In the ACL2 formalization, the core is statically typed and block-structured, while the currently used EVM dialect supplies essentially one type, u256, and built-ins corresponding to EVM instructions such as add (Coglio et al., 25 Jul 2025). This suggests that Yul is best understood as a reusable IR design whose present practical significance comes from its EVM instantiation.
2. Core language design and operational behavior
The abstract syntax presented in the operational-semantics literature treats Yul as a block-structured imperative language with blocks of statements, function definitions with argument and return variables, variable declaration and assignment including multi-value forms, if and switch, and for loops with initialization, condition, post-step, and body. Control effects are explicit through break, continue, and leave. Expressions consist of function calls, dialect-specific opcode calls, variable identifiers, and constants (Koutavas et al., 2024).
A central semantic organization uses configurations of the form . Here, is a global environment external to core Yul semantics, is the local state mapping variables to values, is the function namespace, and is the statement or term being evaluated. The function namespace is described by mappings of the form
where a function name maps to its input variables, output variables, and body. This decomposition is important because it separates ordinary variable state from the lexically scoped function environment.
The operational semantics distinguishes ordinary completion from control outcomes. The mode space includes regular completion, break, continue, leave, and dialect-specific external irregular modes. In the big-step presentation, the semantics is defined only for successful computations and excludes external irregular modes; the small-step presentation includes them explicitly. This separation makes precise how Yul sequences are interrupted, how loop control is caught, and how function exit differs from loop-local transfer.
Several details are semantically significant. Blocks collect function definitions visible in the block, evaluate statements sequentially, and restore outer variable scope on exit by pruning variables introduced inside the block. Function and opcode arguments are evaluated right-to-left. Function calls do not use substitution; they evaluate arguments, initialize a fresh callee-local state, initialize return variables to zero, execute the body, and then read back the output variables as the return result. One of the most important clarifications supplied by the operational semantics concerns void function calls: the prior informal specification was undefined in this case, and the formal account resolves the issue by making a void function call return regular completion, so sequencing in statement position remains well-defined (Koutavas et al., 2024).
3. Optimization IR and transformation sequencing
Yul matters to compiler research because it is the principal target of source-to-source transformations in the Solidity optimizer. One formalization states that “a few tens of Yul transformations” exist, with some dialect-independent and others specific to the EVM dialect. These transformations are applied in customizable sequences; the compiler has a default optimization pipeline, but users can override it. Sequencing is therefore a first-class concern, because some passes establish normal forms required by later passes, and some pass subsequences may be iterated until a fixpoint or iteration bound (Coglio et al., 25 Jul 2025).
This is also the setting in which the phase-ordering problem arises. Because optimization passes over Yul are applied in a hardcoded sequence, a real-world corpus of Yul programs makes it possible to study whether alternative orderings improve bytecode size, execution performance, or gas efficiency. A plausible implication is that Yul is not only an implementation convenience for solc, but also the natural abstraction level for empirical optimizer research (Fonal, 23 Jun 2025).
Recent mechanized work formalizes representative Yul transformations. ForLoopInitRewriter rewrites
0
into
1
so that subsequent passes need not handle the special scoping rule of nonempty for initializers. DeadCodeEliminator removes code in a block following unconditional break, continue, or leave. Disambiguator renames variables and function names so that all names become globally unique. Notably, Disambiguator is formalized relationally rather than as a deterministic function, because many renamings are semantically valid and the aim is to validate the compiler’s renaming rather than reproduce a particular naming heuristic (Coglio et al., 25 Jul 2025).
An important subtlety is that Yul’s function visibility rules make dead-code elimination less trivial than it appears. A function definition can be textually after a break yet remain statically visible earlier in the block. Accordingly, preservation results for dead-code elimination are proved under explicit side conditions excluding function definitions. This is a characteristic example of how Yul’s structured scoping supports optimization while still imposing nontrivial proof obligations.
4. Formal semantics and mechanized correctness results
The official Yul documentation has been characterized as primarily informal, giving grammar, scoping rules, and pseudocode. To address the resulting ambiguity, one line of work presents both big-step and small-step operational semantics for Yul in standard programming-languages notation and proves the equivalence of the two presentations (Koutavas et al., 2024). The big-step semantics provides a concise account of successful executions; the small-step semantics is designed for interpreters and symbolic execution and introduces runtime-only forms such as block-scoping frames, break-catching and continue-catching frames, and function-call frames.
The equivalence theorem states, in essence, that for source-level statements , big-step evaluation to successful completion and multi-step small-step reduction to the same successful result are equivalent. This matters because it connects a human-readable semantic account to an implementation-oriented execution model. It also underwrites the claim that Yul is mature enough to serve as a specification target for verification and symbolic execution tools rather than merely as a compiler-internal artifact (Koutavas et al., 2024).
A second line of work develops a machine-checked ACL2 formalization of Yul’s syntax and semantics together with verified code transformations (Coglio et al., 25 Jul 2025). The formalization includes abstract syntax, a concrete ABNF grammar, executable static semantic checkers, and a defensive big-step interpreter. The static semantics checks arities, name-scoping constraints, and restrictions on the occurrence of break, continue, and leave. It also computes possible termination modes: regular termination, break, continue, and leave.
The dynamic semantics is formulated over a computation state currently containing only local variables, wrapped in a structure intended to admit a later global-state extension. Function environments are represented as a stack of scopes recording function interfaces and bodies. The main metatheoretic result is static soundness: if static checking succeeds, then dynamic execution cannot fail with a safety error except possibly by exhausting the artificial recursion limit used as ACL2 fuel. Further theorems establish static and dynamic preservation for dead-code elimination and variable renaming. These results do not amount to a full verification of the Solidity compiler, but they provide a rigorous basis for translation validation and pass-by-pass optimizer assurance (Coglio et al., 25 Jul 2025).
5. Interpreters, game semantics, and security analysis
Formal work on Yul has produced executable artifacts as well as semantics. The small-step operational semantics has been implemented in an OCaml interpreter, YulTracer, structured in a CEK-machine style and parametric over dialects using functors. The implemented dialect is a partial model of the Shanghai EVM execution specifications, covering arithmetic, bitwise, comparison, control-flow, memory, stack, storage, and a partial model of gas consumption. The implementation includes semantics-preserving optimizations such as block dropping and break dropping, justified by lemmas on redundant frames, and was tested on custom programs and a compatible subset of Solidity compiler Yul interpreter tests (Koutavas et al., 2024).
Yul has also become a direct target for precise vulnerability analysis. YulToolkit is a bounded game-semantics checker for Yul that models smart-contract execution as an interaction between a contract and its environment. In this framework, the analyzed contract is the Proponent and the environment is the Opponent; configurations are triples of the form
where the stack records nested Proponent and Opponent frames, the address component partitions Proponent-controlled and Opponent-controlled addresses, and o-domains stores the Opponent’s known-value sets (Koutavas et al., 27 Dec 2025).
The move system includes deploy, o-call, po-call, pp-call, create, create2, o-ret, po-ret, pp-ret, internal moves, and o-wait. This makes external control transfer explicit, which is especially important for reentrancy. Rather than summarizing unknown external contracts abstractly, YulToolkit systematically enumerates feasible interactions within bounds. The tool’s precision claim is qualified but strong: it is described as having “no false positives up to our EVM-Yul interpreter,” and as bounded-complete in the sense that if no vulnerability trace is reported, then no exploit exists within the user-specified bounds (Koutavas et al., 27 Dec 2025).
Instrumentation is written in Solidity and propagated to Yul by preprocessing. Hooks become custom opcodes such as ASSERT, IMPERSONATECALL, REVEAL_UINT, REVEAL_ADDR, EXT_FUND, and PRINT. This allows auditors to specify safety properties as assertion reachability queries rather than relying only on built-in bug patterns. The tool was evaluated on The DAO, PredyPool, Lendf.Me, and benchmark contracts, detecting the known vulnerabilities and, after fixes, reporting no further violations within bounds. This suggests that Yul is already functioning as a practical semantic layer for precise smart-contract security analysis, especially for interaction-heavy vulnerabilities such as reentrancy.
6. Corpora, metadata, and research infrastructure
Empirical research on Yul has recently been enabled by YulCode, a dataset specifically designed to support work on the Yul layer of the Solidity toolchain. YulCode contains 348,840 Yul-based smart contract instances, comprising approximately 135,013 unique contracts after filtering common libraries such as Ownable, SafeMath, and ERC20. The contracts were generated by recompiling Solidity source files deployed on the Ethereum mainnet, using the corresponding solc versions extracted from the sources, and only considering compiler versions 0.8.10 or later, since 0.8.10 is the first Solidity version supporting generation of Yul intermediate representation in the intended way (Fonal, 23 Jun 2025).
The provenance of the corpus is central to its significance. The source contracts come from the DISL dataset, and the collection process traverses each Solidity contract, extracts its compiler version, reconstructs dependencies, and recompiles with the matching solc version. For each successfully compiled Solidity contract, the pipeline obtains at least constructor or initialization Yul code and runtime Yul code; additional Yul fragments may also be generated for dependencies. Duplicates are retained in the released dataset because the same source logic may yield different Yul under different compiler versions, which is not semantically trivial for compiler research.
Each dataset entry records contract_name, contract_address, solc_version, sol_filepath, yul_filepath, and source_code. This schema links deployed contract addresses, Solidity source files, compiler versions, and generated Yul IR, making the dataset useful for cross-version consistency testing, reverse engineering, and compiler validation. The paper explicitly presents the dataset as supporting machine learning applications, formal verification, optimization analysis, decompilation, low-level smart contract generation, and software engineering tool evaluation. It also claims that YulCode is, to the best of the author’s knowledge at the time of writing, the first and only publicly available dataset focused specifically on Yul (Fonal, 23 Jun 2025).
The dataset literature is also clear about limitations. Compilation settings are underspecified in several respects, including optimization flags, optimization run counts, target EVM version, and exact failure rates. Deduplication is analytical rather than fully operationalized, and no fine-grained diversity analysis is provided. These caveats matter because Yul output can vary significantly across solc versions and compilation environments. Even so, the availability of a large corpus of real-world deployed Yul contracts materially changes the research landscape: it creates a benchmark substrate for phase-ordering studies, IR-level vulnerability analysis, backend benchmarking, symbolic and static tool evaluation, and neural modeling over a representation closer to execution semantics than Solidity source.
7. Scope, misconceptions, and emerging directions
Across recent work, Yul emerges as more than a compiler implementation detail. It is presented as the optimization IR at the center of Solidity’s preferred compilation pipeline, the natural layer for formal semantics, and a practical target for both security analysis and dataset-driven research. This does not mean that Yul is a full replacement for source-level or bytecode-level reasoning. Solidity remains the dominant authoring language, and EVM bytecode remains the final executable artifact. Yul instead occupies the analytically productive middle ground: high enough to preserve structure, low enough to reflect backend transformations and deployed behavior (Koutavas et al., 2024, Fonal, 23 Jun 2025, Coglio et al., 25 Jul 2025).
Several limitations are shared across the literature. Formal semantics papers either exclude or only partially model the full EVM dialect and Yul object notation. The ACL2 formalization currently covers the Yul core and only a small portion of the EVM dialect, with global state not yet modeled. The operational-semantics interpreter supports only a subset of Yul or EVM instructions. Security-analysis claims are explicitly relative to a trusted EVM-Yul interpreter. Dataset work leaves important compilation parameters unspecified and begins version coverage only at 0.8.10. These constraints do not negate the utility of Yul-centered research, but they delimit the current state of formal and empirical coverage (Koutavas et al., 2024, Coglio et al., 25 Jul 2025, Koutavas et al., 27 Dec 2025, Fonal, 23 Jun 2025).
The overall direction is clear. A formal operational semantics supplies the language-theoretic basis for verification and symbolic execution. Mechanized ACL2 proofs begin to establish transformation correctness and transformation sequencing. Game-semantics tooling shows that Yul can support precise, bounded-complete vulnerability analysis on real contracts. Large-scale corpora now make IR-level empirical research feasible. Taken together, these developments suggest that Yul has become the principal layer at which rigorous work on Solidity compiler correctness, low-level smart-contract analysis, and IR-centric machine learning can converge.