Spec-ID: x07.spec.internal.x07-c-backend@0.1.0 Status: draft Applies-to: toolchain >= v0.0.95 Related schemas: []
C Backend (Native Execution)
X07’s execution pipeline is:
- The language core (compiler built-ins + embedded stdlib modules) defines the surface (no macro/rewrite layer).
- Program (UTF-8 x07AST JSON, usually
*.x07.json) is compiled to C source. - The runner compiles the C source to a native executable (“solver artifact”).
- The runner executes the artifact under deterministic limits and collects output + metrics.
Notes:
- The canonical LLM-facing surface is x07AST JSON (
spec/x07ast.schema.json), with expressions encoded as JSON S-expressions (json-sexpr). - The compiler accepts only x07AST JSON (tool contracts are JSON-first).
Solver I/O ABI (bytes → bytes)
The solver artifact is a native executable that:
- Reads from stdin:
u32_le input_lentheninput_lenbytes. - Writes to stdout:
u32_le output_lenthenoutput_lenbytes. - Writes metrics to stderr as a single JSON line at exit, including deterministic memory stats.
- Example shape:
{"fuel_used":123,"heap_used":456,"fs_read_file_calls":7,"rr_open_calls":1,"rr_close_calls":1,"rr_stats_calls":0,"rr_next_calls":3,"rr_next_miss_calls":0,"rr_append_calls":0,"kv_get_calls":2,"kv_set_calls":0,"mem_stats":{"alloc_calls":1,"realloc_calls":2,"free_calls":1,"bytes_alloc_total":64,"bytes_freed_total":64,"live_bytes":0,"peak_live_bytes":64,"live_allocs":0,"peak_live_allocs":3,"memcpy_bytes":64},"sched_stats":{"tasks_spawned":3,"spawn_calls":3,"join_calls":3,"yield_calls":0,"sleep_calls":3,"chan_send_calls":0,"chan_recv_calls":0,"ctx_switches":10,"wake_events":3,"blocked_waits":0,"virtual_time_end":55,"sched_trace_hash":"0x..."},"debug_stats":{"borrow_violations":0}}
- Example shape:
Notes:
- The compiled program sees
inputas abytes_view(borrowed) and must return ownedbytes. mem_statsare reset after reading the input payload, so input bytes are excluded from memory gates/scoring.- In debug-borrow builds,
debug_stats.borrow_violationsis emitted and can be gated by benchmark suites. - ABI v2 (C-facing value layouts) is specified under
docs/spec/abi/andcrates/x07c/include/x07_abi_v2.h.
Worlds (capability profiles)
solve-pure: pure compute only.solve-fs: deterministic read-only fixture filesystem mounted as current directory (.).["fs.read", path_bytes]reads a fixture file with a safe relative-path policy.- Phase G2 streaming adapters:
["fs.open_read", path_bytes]→ifacereader["io.read", reader_iface, max_i32]→ next chunk (bytes)
solve-rr: deterministic replay/record backed by cassette files under./.x07_rr/.- Cassette files are
*.rrbinand contain u32-le framed RRentry_v1records (DataModel docs). - Core builtins:
["rr.open_v1", cfg_bytes_view]→result_i32(ok = handle)["rr.close_v1", handle_i32]→i32["rr.stats_v1", handle_i32]→bytes(JSON)["rr.next_v1", handle_i32, kind_bytes_view, op_bytes_view, key_bytes_view]→result_bytes(ok = entry bytes)["rr.append_v1", handle_i32, entry_bytes_view]→result_i32
- Entry helpers:
["rr.entry_resp_v1", entry_bytes_view]→bytes["rr.entry_err_v1", entry_bytes_view]→i32
- Programs typically use
std.rr.with_policy_v1/std.rr.with_v1to managerr.open_v1/rr.close_v1as a structured scope.
- Cassette files are
solve-kv: deterministic key/value store (seeded per case; reset per case).["kv.get", key_bytes]returns the value bytes or empty if missing.["kv.get_async", key_bytes]returns the value bytes or empty if missing.["kv.get_stream", key_bytes]returns anifacereader for streaming reads.["kv.set", key_bytes, val_bytes]sets a value and returns 1 if inserted, 0 if updated.- The seed is loaded from
./.x07_kv/seed.evkvat process start. - Phase G2 latency index: runner may also materialize
./.x07_kv/latency.evkvlat(used to advance virtual time forkv.get).
solve-full: includes fs + rr + kv.
Standalone-only (not used in deterministic suites; run via x07-os-runner):
run-os: real OS access (non-deterministic by design).run-os-sandboxed: same surface, but restricted by a policy file (seeschemas/run-os-policy.schema.json).
Phase H3 adds standalone-only OS builtins (compile-time gated to run-os*):
os.fs.read_file(path: bytes) -> bytesos.fs.write_file(path: bytes, data: bytes) -> i32(0 on success; errno-like code on failure)os.env.get(key: bytes) -> bytes(empty if missing)os.time.now_unix_ms() -> i32(low 32 bits of unix ms)os.time.now_instant_v1() -> bytes(InstantDocV1; encoded like DurationDocV1)os.time.sleep_ms_v1(ms: i32) -> i32(1 on success; 0 on failure/policy denied)os.time.local_tzid_v1() -> bytes(LocalTzidResultV1 doc; seedocs/time/os-time-v1.md)os.process.exit(code: i32) -> neveros.net.http_request(req: bytes) -> bytes(currently traps; reserved for later)- OS-world networking today: use
ext-net(packages/ext/x07-ext-net/0.1.5/) viastd.net.*(seedocs/guides/networking.mdanddocs/net/net-v1.md).
- OS-world networking today: use
Pinned tzdb builtins (deterministic; used by ext.time.tzdb, see docs/time/tzdb-v1.md):
os.time.tzdb_is_valid_tzid_v1(tzid: bytes_view) -> i32os.time.tzdb_offset_duration_v1(tzid: bytes_view, unix_s_lo: i32, unix_s_hi: i32) -> bytesos.time.tzdb_snapshot_id_v1() -> bytes
Phase H4 adds standalone-only unsafe + FFI (compile-time gated to run-os*):
unsafeblocks (["unsafe", ...]) and raw pointer types (ptr_const_u8,ptr_mut_u8,ptr_const_void,ptr_mut_void,ptr_const_i32,ptr_mut_i32).- Pointer + memory intrinsics (
ptr.*,addr_of*,memcpy/memmove/memset) andexternC function declarations/calls (unsafe +fficapability).
Determinism & limits
Determinism is enforced by:
- A deterministic fuel counter consumed by generated code (
rt_fuel(ctx, 1)per AST node evaluation). - A fixed-capacity deterministic heap allocator capped by
X07_MEM_CAP(compile-time macro), with explicitfree(used bybytes/vec_u8drops). - A fixed environment: no inherited args/env; the process runs in an isolated temp working directory.
- Unix resource limits (
setrlimit) as kill-switches (CPU time, file size, fd count, core dumps). - Bounded stdout/stderr capture in the runner to prevent output-spam from exhausting host memory.
Toolchain & caching
The runner compiles C using cc (override via X07_CC) and caches artifacts under:
target/x07-native-cache/<sha256>/solver
Cache keys include the C source, compiler version string, and runner compile-time configuration.
To inject extra C toolchain arguments (used for sanitizer gates), set:
X07_CC_ARGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g -O1"
To build smaller native artifacts (useful when linking native extension archives), use:
x07-host-runner --cc-profile size ...(orx07-os-runner --cc-profile size ...)
This appends a platform-specific set of size-focused flags to X07_CC_ARGS (for example: -Os plus linker dead-stripping on macOS, or --gc-sections on Linux). X07_CC_ARGS remains the escape hatch for custom toolchain flags.
For standalone OS runs that use external FFI packages, prefer x07-os-runner --auto-ffi so the runner compiles ffi/*.c sources and links x07-package.json meta.ffi_libs automatically.
To keep the generated C source for inspection, set X07_KEEP_C=1 and the runner will write:
target/x07-native-cache/<sha256>/solver.c
Debug borrow checks
To enable debug-only borrow/lifetime instrumentation in the C backend (Phase G1), run the host runner with:
--debug-borrow-checks