// Minimal bootstrap — seeds the content-addressed cache // Only runs on cold start (C runtime couldn't find engine in cache) // Hidden vars: os, core_path, shop_path, native_mode (optional) var load_internal = os.load_internal function use_embed(name) { return load_internal("js_core_" + name + "_use") } var fd = use_embed('internal_fd') var json_mod = use_embed('json') var crypto = use_embed('internal_crypto') function content_hash(content) { var data = content if (!is_blob(data)) data = stone(blob(text(data))) return text(crypto.blake2(data), 'h') } function cache_path(hash) { if (!shop_path) return null return shop_path + '/build/' + hash } function ensure_build_dir() { if (!shop_path) return null var dir = shop_path + '/build' if (!fd.is_dir(dir)) fd.mkdir(dir) return dir } // Load seed pipeline from boot/ function boot_load(name) { var mcode_path = core_path + '/boot/' + name + '.cm.mcode' var mcode_blob = null var mach_blob = null if (!fd.is_file(mcode_path)) { os.print("error: missing seed: " + name + "\n") disrupt } mcode_blob = fd.slurp(mcode_path) mach_blob = mach_compile_mcode_bin(name, text(mcode_blob)) return mach_load(mach_blob, stone({use: use_embed})) } var tokenize_mod = boot_load("tokenize") var parse_mod = boot_load("parse") var fold_mod = boot_load("fold") var mcode_mod = boot_load("mcode") var streamline_mod = boot_load("streamline") function analyze(src, filename) { var tok_result = tokenize_mod(src, filename) var ast = parse_mod(tok_result.tokens, src, filename, tokenize_mod) var _i = 0 var e = null var msg = null var has_errors = ast.errors != null && length(ast.errors) > 0 if (has_errors) { while (_i < length(ast.errors)) { e = ast.errors[_i] msg = e.message if (e.line != null && e.column != null) os.print(`${filename}:${text(e.line)}:${text(e.column)}: error: ${msg}\n`) else os.print(`${filename}: error: ${msg}\n`) _i = _i + 1 } disrupt } return fold_mod(ast) } function compile_and_cache(name, source_path) { var source_blob = fd.slurp(source_path) var hash = content_hash(source_blob) var cached = cache_path(hash) var ast = null var compiled = null var mcode_json = null var mach_blob = null if (cached && fd.is_file(cached)) return ast = analyze(text(source_blob), source_path) compiled = streamline_mod(mcode_mod(ast)) mcode_json = json_mod.encode(compiled) mach_blob = mach_compile_mcode_bin(name, mcode_json) if (cached) { ensure_build_dir() fd.slurpwrite(cached, mach_blob) } } // --- Native compilation support --- function detect_host_target() { var platform = os.platform() var arch = os.arch ? os.arch() : 'arm64' if (platform == 'macOS' || platform == 'darwin') return arch == 'x86_64' ? 'macos_x86_64' : 'macos_arm64' if (platform == 'Linux' || platform == 'linux') return arch == 'x86_64' ? 'linux' : 'linux_arm64' if (platform == 'Windows' || platform == 'windows') return 'windows' return null } function detect_cc() { var platform = os.platform() if (platform == 'macOS') return 'clang' return 'cc' } // Compute native dylib cache path matching build.cm's scheme: // cache_path(native_cache_content(src, target, ''), SALT_NATIVE) function native_dylib_cache_path(src, target) { var native_key = src + '\n' + target + '\nnative\n' var full_key = native_key + '\nnative' return cache_path(content_hash(full_key)) } // Compile a module to a native dylib and cache it var _qbe_mod = null var _qbe_emit_mod = null var _host_target = null var _cc = null var _is_darwin = false var _rt_compiled = false function compile_native_cached(name, source_path) { var source_blob = fd.slurp(source_path) var src = text(source_blob) var dylib_path = native_dylib_cache_path(src, _host_target) var ast = null var compiled = null var il_parts = null var helpers_il = null var all_fns = null var full_il = null var asm_text = null var tmp = null var rc = null var rt_o = null var qbe_rt_path = null var link_cmd = null if (dylib_path && fd.is_file(dylib_path)) { os.print("bootstrap: native cache hit: " + name + "\n") return } var t0 = null var t1 = null os.print("bootstrap: compiling native: " + name + "\n") t0 = os.now() ast = analyze(src, source_path) compiled = streamline_mod(mcode_mod(ast)) t1 = os.now() os.print(" [" + name + "] pipeline (tok+parse+fold+mcode+streamline): " + text((t1 - t0) / 1000000) + "ms\n") t0 = os.now() il_parts = _qbe_emit_mod(compiled, _qbe_mod, null) t1 = os.now() os.print(" [" + name + "] qbe_emit: " + text((t1 - t0) / 1000000) + "ms\n") helpers_il = (il_parts.helpers && length(il_parts.helpers) > 0) ? text(il_parts.helpers, "\n") : "" all_fns = text(il_parts.functions, "\n") full_il = il_parts.data + "\n\n" + helpers_il + "\n\n" + all_fns t0 = os.now() asm_text = os.qbe(full_il) t1 = os.now() os.print(" [" + name + "] os.qbe (QBE compile): " + text((t1 - t0) / 1000000) + "ms\n") tmp = '/tmp/cell_boot_' + name fd.slurpwrite(tmp + '.s', stone(blob(asm_text))) t0 = os.now() rc = os.system(_cc + ' -c ' + tmp + '.s -o ' + tmp + '.o') t1 = os.now() os.print(" [" + name + "] clang -c: " + text((t1 - t0) / 1000000) + "ms\n") if (rc != 0) { os.print("error: assembly failed for " + name + "\n") disrupt } // Compile QBE runtime stubs (once) rt_o = '/tmp/cell_qbe_rt.o' if (!_rt_compiled && !fd.is_file(rt_o)) { qbe_rt_path = core_path + '/src/qbe_rt.c' rc = os.system(_cc + ' -c ' + qbe_rt_path + ' -o ' + rt_o + ' -fPIC') if (rc != 0) { os.print("error: qbe_rt compilation failed\n") disrupt } _rt_compiled = true } // Link dylib ensure_build_dir() link_cmd = _cc + ' -shared -fPIC' if (_is_darwin) link_cmd = link_cmd + ' -undefined dynamic_lookup' link_cmd = link_cmd + ' ' + tmp + '.o ' + rt_o + ' -o ' + dylib_path t0 = os.now() rc = os.system(link_cmd) t1 = os.now() os.print(" [" + name + "] clang -shared (link): " + text((t1 - t0) / 1000000) + "ms\n") if (rc != 0) { os.print("error: linking failed for " + name + "\n") disrupt } } // --- Main bootstrap logic --- // Check if native_mode was passed from C runtime var _native = false var _check_nm = function() { if (native_mode) _native = true } disruption {} _check_nm() var _targets = null var _ti = 0 var _te = null if (_native) { // Native path: compile everything to native dylibs _qbe_mod = boot_load("qbe") _qbe_emit_mod = boot_load("qbe_emit") _host_target = detect_host_target() _cc = detect_cc() _is_darwin = os.platform() == 'macOS' if (!_host_target) { os.print("error: could not detect host target for native compilation\n") disrupt } // Also seed bytecode cache for engine (so non-native path still works) compile_and_cache("engine", core_path + '/internal/engine.cm') // Compile pipeline modules + qbe/qbe_emit + engine to native dylibs _targets = [ {name: "tokenize", path: "tokenize.cm"}, {name: "parse", path: "parse.cm"}, {name: "fold", path: "fold.cm"}, {name: "mcode", path: "mcode.cm"}, {name: "streamline", path: "streamline.cm"}, {name: "qbe", path: "qbe.cm"}, {name: "qbe_emit", path: "qbe_emit.cm"}, {name: "engine", path: "internal/engine.cm"} ] _ti = 0 while (_ti < length(_targets)) { _te = _targets[_ti] compile_native_cached(_te.name, core_path + '/' + _te.path) _ti = _ti + 1 } os.print("bootstrap: native cache seeded\n") } else { // Bytecode path: seed cache with everything engine needs _targets = [ {name: "tokenize", path: "tokenize.cm"}, {name: "parse", path: "parse.cm"}, {name: "fold", path: "fold.cm"}, {name: "mcode", path: "mcode.cm"}, {name: "streamline", path: "streamline.cm"}, {name: "engine", path: "internal/engine.cm"} ] _ti = 0 while (_ti < length(_targets)) { _te = _targets[_ti] compile_and_cache(_te.name, core_path + '/' + _te.path) _ti = _ti + 1 } os.print("bootstrap: cache seeded\n") }