native boot

This commit is contained in:
2026-02-22 20:47:26 -06:00
parent 7ac5ac63d2
commit 4edc4b7cc5
19 changed files with 59808 additions and 22461 deletions

View File

@@ -52,6 +52,38 @@ function ensure_build_dir() {
return dir
}
// --- 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'
}
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))
}
var _engine_host_target = null
var _engine_cc = null
var _engine_is_darwin = false
var _qbe_mod = null
var _qbe_emit_mod = null
// Load a boot seed module (for compiling pipeline modules on cache miss)
function boot_load(name) {
var mcode_path = core_path + '/boot/' + name + '.cm.mcode'
@@ -85,8 +117,22 @@ function load_pipeline_module(name, env) {
var ast = null
var compiled = null
var mcode_json = null
if (fd.is_file(source_path)) {
var native_path = null
var native_handle = null
// Native mode: check native cache first
if (native_mode && _engine_host_target && fd.is_file(source_path)) {
source_blob = fd.slurp(source_path)
src = text(source_blob)
native_path = native_dylib_cache_path(src, _engine_host_target)
if (native_path && fd.is_file(native_path)) {
native_handle = os.dylib_open(native_path)
return os.native_module_load_named(native_handle, 'cell_main', env)
}
}
if (fd.is_file(source_path)) {
if (!source_blob) source_blob = fd.slurp(source_path)
hash = content_hash(source_blob)
cached = cache_path(hash)
if (cached && fd.is_file(cached))
@@ -99,7 +145,7 @@ function load_pipeline_module(name, env) {
boot_par = boot_load("parse")
boot_fld = boot_load("fold")
boot_mc = boot_load("mcode")
src = text(source_blob)
if (!src) src = text(source_blob)
tok_result = boot_tok(src, source_path)
ast = boot_par(tok_result.tokens, src, source_path, boot_tok)
if (ast.errors != null && length(ast.errors) > 0) {
@@ -112,7 +158,7 @@ function load_pipeline_module(name, env) {
compiled = boot_sl(compiled)
mcode_json = json.encode(compiled)
mach_blob = mach_compile_mcode_bin(name, mcode_json)
if (cached) {
if (!native_mode && cached) {
ensure_build_dir()
fd.slurpwrite(cached, mach_blob)
}
@@ -130,6 +176,13 @@ function load_pipeline_module(name, env) {
disrupt
}
// Initialize native compilation state before pipeline loading
if (native_mode) {
_engine_host_target = detect_host_target()
_engine_cc = detect_cc()
_engine_is_darwin = os.platform() == 'macOS'
}
// Load compilation pipeline
var pipeline_env = stone({use: use_embed})
var tokenize_mod = load_pipeline_module('tokenize', pipeline_env)
@@ -146,6 +199,16 @@ use_cache['core/mcode'] = mcode_mod
use_cache['streamline'] = streamline_mod
use_cache['core/streamline'] = streamline_mod
// Load QBE modules when native mode
if (native_mode) {
_qbe_mod = load_pipeline_module('qbe', pipeline_env)
_qbe_emit_mod = load_pipeline_module('qbe_emit', pipeline_env)
use_cache['qbe'] = _qbe_mod
use_cache['core/qbe'] = _qbe_mod
use_cache['qbe_emit'] = _qbe_emit_mod
use_cache['core/qbe_emit'] = _qbe_emit_mod
}
// analyze: tokenize + parse + fold, check for errors
function analyze(src, filename) {
var tok_result = tokenize_mod(src, filename)
@@ -440,6 +503,70 @@ use_cache['core/internal/os'] = os
// Extra env properties added as engine initializes (log, runtime fns, etc.)
var core_extras = {}
// Compile a core module to a native dylib, return the dylib path
function compile_core_native(name, source_path) {
var source_blob = fd.slurp(source_path)
var src = text(source_blob)
var dylib_path = native_dylib_cache_path(src, _engine_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))
return dylib_path
ast = analyze(src, source_path)
compiled = streamline_mod(mcode_mod(ast))
il_parts = _qbe_emit_mod(compiled, _qbe_mod, null)
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
asm_text = os.qbe(full_il)
tmp = '/tmp/cell_engine_' + replace(name, '/', '_')
fd.slurpwrite(tmp + '.s', stone(blob(asm_text)))
rc = os.system(_engine_cc + ' -c ' + tmp + '.s -o ' + tmp + '.o')
if (rc != 0) {
os.print("error: assembly failed for " + name + "\n")
disrupt
}
rt_o = '/tmp/cell_qbe_rt.o'
if (!fd.is_file(rt_o)) {
qbe_rt_path = core_path + '/src/qbe_rt.c'
rc = os.system(_engine_cc + ' -c ' + qbe_rt_path + ' -o ' + rt_o + ' -fPIC')
if (rc != 0) {
os.print("error: qbe_rt compilation failed\n")
disrupt
}
}
ensure_build_dir()
link_cmd = _engine_cc + ' -shared -fPIC'
if (_engine_is_darwin)
link_cmd = link_cmd + ' -undefined dynamic_lookup'
link_cmd = link_cmd + ' ' + tmp + '.o ' + rt_o + ' -o ' + dylib_path
rc = os.system(link_cmd)
if (rc != 0) {
os.print("error: linking failed for " + name + "\n")
disrupt
}
return dylib_path
}
// Load a core module from the file system
function use_core(path) {
var cache_key = 'core/' + path
@@ -452,6 +579,7 @@ function use_core(path) {
var script = null
var ast = null
var _load_mod = null
var _try_native = null
// Build env: merge core_extras
env = {use: use_core}
@@ -467,8 +595,34 @@ function use_core(path) {
// Compile from source .cm file
file_path = core_path + '/' + path + MOD_EXT
if (fd.is_file(file_path)) {
// Native path: try native cache or compile natively
if (native_mode && _qbe_mod && _qbe_emit_mod) {
_try_native = function() {
var src = null
var native_path = null
var native_handle = null
source_blob = fd.slurp(file_path)
src = text(source_blob)
native_path = native_dylib_cache_path(src, _engine_host_target)
if (native_path && fd.is_file(native_path)) {
native_handle = os.dylib_open(native_path)
result = os.native_module_load_named(native_handle, 'cell_main', env)
return
}
native_path = compile_core_native('core:' + path, file_path)
native_handle = os.dylib_open(native_path)
result = os.native_module_load_named(native_handle, 'cell_main', env)
} disruption {}
_try_native()
if (result != null) {
use_cache[cache_key] = result
return result
}
}
// Bytecode path (fallback or non-native mode)
_load_mod = function() {
source_blob = fd.slurp(file_path)
if (!source_blob) source_blob = fd.slurp(file_path)
hash = content_hash(source_blob)
cached_path = cache_path(hash)
if (cached_path && fd.is_file(cached_path)) {
@@ -477,7 +631,7 @@ function use_core(path) {
script = text(source_blob)
ast = analyze(script, file_path)
mach_blob = compile_to_blob('core:' + path, ast)
if (cached_path) {
if (!native_mode && cached_path) {
ensure_build_dir()
fd.slurpwrite(cached_path, mach_blob)
}