// compile.ce — compile a .cm module to native .dylib via QBE // // Usage: // cell --core . compile.ce // // Produces .dylib in the current directory. var fd = use('fd') var os = use('os') if (length(args) < 1) { print('usage: cell --core . compile.ce ') return } var file = args[0] var base = file if (ends_with(base, '.cm')) { base = text(base, 0, length(base) - 3) } var safe = replace(replace(base, '/', '_'), '-', '_') var symbol = 'js_' + safe + '_use' var tmp = '/tmp/qbe_' + safe var ssa_path = tmp + '.ssa' var s_path = tmp + '.s' var o_path = tmp + '.o' var rt_o_path = '/tmp/qbe_rt.o' var dylib_path = base + '.dylib' var cwd = fd.getcwd() var rc = 0 // Step 1: emit QBE IL print('emit qbe...') rc = os.system('cd ' + cwd + ' && ./cell --core . --emit-qbe ' + file + ' > ' + ssa_path) if (rc != 0) { print('failed to emit qbe il') return } // Step 2: post-process — insert dead labels after ret/jmp, append wrapper // Use awk via shell to avoid blob/slurpwrite issues with long strings print('post-process...') var awk_cmd = `awk ' need_label && /^[[:space:]]*[^@}]/ && NF > 0 { print "@_dead_" dead_id; dead_id++; need_label=0 } /^@/ || /^}/ || NF==0 { need_label=0 } /^[[:space:]]*ret / || /^[[:space:]]*jmp / { need_label=1; print; next } { print } ' ` + ssa_path + ` > ` + tmp + `_fixed.ssa` rc = os.system(awk_cmd) if (rc != 0) { print('post-process failed') return } // Append wrapper function — called as symbol(ctx) by os.dylib_symbol. // Delegates to cell_rt_module_entry which heap-allocates a frame // (so closures survive) and calls cell_main. var wrapper_cmd = `printf '\nexport function l $` + symbol + `(l %%ctx) {\n@entry\n %%result =l call $cell_rt_module_entry(l %%ctx)\n ret %%result\n}\n' >> ` + tmp + `_fixed.ssa` rc = os.system(wrapper_cmd) if (rc != 0) { print('wrapper append failed') return } // Step 3: compile QBE IL to assembly print('qbe compile...') rc = os.system('~/.local/bin/qbe -o ' + s_path + ' ' + tmp + '_fixed.ssa') if (rc != 0) { print('qbe compilation failed') return } // Step 4: assemble print('assemble...') rc = os.system('cc -c ' + s_path + ' -o ' + o_path) if (rc != 0) { print('assembly failed') return } // Step 5: compile runtime stubs (cached — skip if already built) if (!fd.is_file(rt_o_path)) { print('compile runtime stubs...') rc = os.system('cc -c ' + cwd + '/qbe_rt.c -o ' + rt_o_path + ' -fPIC') if (rc != 0) { print('runtime stubs compilation failed') return } } // Step 6: link dylib print('link...') rc = os.system('cc -shared -fPIC -undefined dynamic_lookup ' + o_path + ' ' + rt_o_path + ' -o ' + cwd + '/' + dylib_path) if (rc != 0) { print('linking failed') return } print('built: ' + dylib_path)