SSA CFG JSON leaks instruction type to block
- Dominant language
- C++
- Stars
- 25.7k
- Forks
- 6.2k
- Avg merge
- 2d 19h
- Merged PRs (30d)
- 29
Description
## Description
The SSA CFG JSON exporter (`--yul-cfg-json`) leaks an instruction's kind into the enclosing block's JSON object. In `libyul/backends/evm/ssa/io/JSONExporter.cpp`, the operation serializer is passed the parent block JSON object (`_ret`) and writes `_ret["type"] = "BuiltinCall"` or `_ret["type"] = "FunctionCall"` on that parent instead of writing the kind into the per-instruction JSON.
As a result, only blocks that contain at least one operation gain a bogus top-level `"type": "BuiltinCall"` (or `"FunctionCall"`) field, while operation-free blocks omit it. Consumers cannot treat `type` as a real block schema property.
Relevant code:
```cpp
// libyul/backends/evm/ssa/io/JSONExporter.cpp:46
Json toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation, ControlFlowGraphs const& _controlFlow)
{
Json opJson = Json::object();
std::visit(solidity::util::GenericVisitor{
[&](SSACFG::Call const& _call) {
_ret["type"] = "FunctionCall";
opJson["op"] = _controlFlow.functionGraph(_call.graphID)->name;
},
[&](SSACFG::BuiltinCall const& _call) {
_ret["type"] = "BuiltinCall";
```
```cpp
// libyul/backends/evm/ssa/io/JSONExporter.cpp:116-117
for (auto const opId: block.operations)
blockJson["instructions"].push_back(toJson(blockJson, _cfg, _cfg.operation(opId), _controlFlow));
```
Expected: the operation kind should be written into the instruction JSON (`opJson["type"]`), not into the enclosing block object. Block-level fields should remain limited to the documented schema (`id`, `exit`, `instructions`, `liveness`).
## Environment
- Compiler version: 0.8.35-develop.2026.5.5+commit.47b9dedd.Linux.g++
- Operating system: Linux Ubuntu Jammy
## Steps to Reproduce
Compile the following Yul input with `--yul-cfg-json`:
```yul
{
let x := calldataload(0)
if x { pop(x) }
}
```
In the resulting JSON, the block containing the `calldataload` operation has a top-level `"type": "BuiltinCall"` field, while operation-free blocks do not. The instruction kind leaks onto the parent block object instead of staying on the instruction JSON.
Reproduction via the verification script:
```bash
$ bash research/scripts/verify_ssa_cfg_block_type_leak.sh \
research/compile_tests/Ebab680_ssa_cfg_block_type_leak.input.yul
OK: SSA CFG JSON leaks instruction kind into parent block type field.
```
Contributor guide
Assessment
This issue has not been assessed yet.