This commit is contained in:
2026-02-19 03:12:58 -06:00
parent ab43ab0d2c
commit 65fa37cc03
7 changed files with 886 additions and 54 deletions

302
benches/micro_core.cm Normal file
View File

@@ -0,0 +1,302 @@
// micro_core.cm — direct microbenchmarks for core ops
function blackhole(sink, x) {
return (sink + (x | 0)) | 0
}
function make_obj_xy(x, y) {
return {x: x, y: y}
}
function make_obj_yx(x, y) {
// Different insertion order to force a different shape
return {y: y, x: x}
}
function make_packed_array(n) {
var a = []
var i = 0
for (i = 0; i < n; i++) push(a, i)
return a
}
function make_holey_array(n) {
var a = []
var i = 0
for (i = 0; i < n; i += 2) a[i] = i
return a
}
return {
loop_empty: function(n) {
var sink = 0
var i = 0
for (i = 0; i < n; i++) {}
return blackhole(sink, n)
},
i32_add: function(n) {
var sink = 0
var x = 1
var i = 0
for (i = 0; i < n; i++) x = (x + 3) | 0
return blackhole(sink, x)
},
f64_add: function(n) {
var sink = 0
var x = 1.0
var i = 0
for (i = 0; i < n; i++) x = x + 3.14159
return blackhole(sink, x | 0)
},
mixed_add: function(n) {
var sink = 0
var x = 1
var i = 0
for (i = 0; i < n; i++) x = x + 0.25
return blackhole(sink, x | 0)
},
bit_ops: function(n) {
var sink = 0
var x = 0x12345678
var i = 0
for (i = 0; i < n; i++) x = ((x << 5) ^ (x >>> 3)) | 0
return blackhole(sink, x)
},
overflow_path: function(n) {
var sink = 0
var x = 0x70000000
var i = 0
for (i = 0; i < n; i++) x = (x + 0x10000000) | 0
return blackhole(sink, x)
},
call_direct: function(n) {
var sink = 0
var f = function(a) { return (a + 1) | 0 }
var x = 0
var i = 0
for (i = 0; i < n; i++) x = f(x)
return blackhole(sink, x)
},
call_indirect: function(n) {
var sink = 0
var f = function(a) { return (a + 1) | 0 }
var g = f
var x = 0
var i = 0
for (i = 0; i < n; i++) x = g(x)
return blackhole(sink, x)
},
call_closure: function(n) {
var sink = 0
var make_adder = function(k) {
return function(a) { return (a + k) | 0 }
}
var add3 = make_adder(3)
var x = 0
var i = 0
for (i = 0; i < n; i++) x = add3(x)
return blackhole(sink, x)
},
array_read_packed: function(n) {
var sink = 0
var a = make_packed_array(1024)
var x = 0
var i = 0
for (i = 0; i < n; i++) x = (x + a[i & 1023]) | 0
return blackhole(sink, x)
},
array_write_packed: function(n) {
var sink = 0
var a = make_packed_array(1024)
var i = 0
for (i = 0; i < n; i++) a[i & 1023] = i
return blackhole(sink, a[17] | 0)
},
array_read_holey: function(n) {
var sink = 0
var a = make_holey_array(2048)
var x = 0
var i = 0
var v = null
for (i = 0; i < n; i++) {
v = a[(i & 2047)]
if (v) x = (x + v) | 0
}
return blackhole(sink, x)
},
array_push_steady: function(n) {
var sink = 0
var x = 0
var j = 0
var i = 0
var a = null
for (j = 0; j < n; j++) {
a = []
for (i = 0; i < 256; i++) push(a, i)
x = (x + length(a)) | 0
}
return blackhole(sink, x)
},
array_indexed_sum: function(n) {
var sink = 0
var a = make_packed_array(1024)
var x = 0
var j = 0
var i = 0
for (j = 0; j < n; j++) {
x = 0
for (i = 0; i < 1024; i++) {
x = (x + a[i]) | 0
}
}
return blackhole(sink, x)
},
prop_read_mono: function(n) {
var sink = 0
var o = make_obj_xy(1, 2)
var x = 0
var i = 0
for (i = 0; i < n; i++) x = (x + o.x) | 0
return blackhole(sink, x)
},
prop_read_poly_2: function(n) {
var sink = 0
var a = make_obj_xy(1, 2)
var b = make_obj_yx(1, 2)
var x = 0
var i = 0
var o = null
for (i = 0; i < n; i++) {
o = (i & 1) == 0 ? a : b
x = (x + o.x) | 0
}
return blackhole(sink, x)
},
prop_read_poly_4: function(n) {
var sink = 0
var shapes = [
{x: 1, y: 2},
{y: 2, x: 1},
{x: 1, z: 3, y: 2},
{w: 0, x: 1, y: 2}
]
var x = 0
var i = 0
for (i = 0; i < n; i++) {
x = (x + shapes[i & 3].x) | 0
}
return blackhole(sink, x)
},
string_concat_small: function(n) {
var sink = 0
var x = 0
var j = 0
var i = 0
var s = null
for (j = 0; j < n; j++) {
s = ""
for (i = 0; i < 16; i++) s = s + "x"
x = (x + length(s)) | 0
}
return blackhole(sink, x)
},
string_concat_medium: function(n) {
var sink = 0
var x = 0
var j = 0
var i = 0
var s = null
for (j = 0; j < n; j++) {
s = ""
for (i = 0; i < 100; i++) s = s + "abcdefghij"
x = (x + length(s)) | 0
}
return blackhole(sink, x)
},
string_slice: function(n) {
var sink = 0
var base = "the quick brown fox jumps over the lazy dog"
var x = 0
var i = 0
var s = null
for (i = 0; i < n; i++) {
s = text(base, i % 10, i % 10 + 10)
x = (x + length(s)) | 0
}
return blackhole(sink, x)
},
guard_hot_number: function(n) {
var sink = 0
var x = 1
var i = 0
for (i = 0; i < n; i++) x = x + 1
return blackhole(sink, x | 0)
},
guard_mixed_types: function(n) {
var sink = 0
var vals = [1, "a", 2, "b", 3, "c", 4, "d"]
var x = 0
var i = 0
for (i = 0; i < n; i++) {
if (is_number(vals[i & 7])) x = (x + vals[i & 7]) | 0
}
return blackhole(sink, x)
},
reduce_sum: function(n) {
var sink = 0
var a = make_packed_array(256)
var x = 0
var i = 0
for (i = 0; i < n; i++) {
x = (x + reduce(a, function(acc, v) { return acc + v }, 0)) | 0
}
return blackhole(sink, x)
},
filter_evens: function(n) {
var sink = 0
var a = make_packed_array(256)
var x = 0
var i = 0
for (i = 0; i < n; i++) {
x = (x + length(filter(a, function(v) { return v % 2 == 0 }))) | 0
}
return blackhole(sink, x)
},
arrfor_sum: function(n) {
var sink = 0
var a = make_packed_array(256)
var x = 0
var i = 0
var sum = 0
for (i = 0; i < n; i++) {
sum = 0
arrfor(a, function(v) { sum += v })
x = (x + sum) | 0
}
return blackhole(sink, x)
}
}

View File

@@ -322,7 +322,10 @@ JSC_SCALL(os_system,
)
JSC_CCALL(os_exit,
exit(0);
int code = 0;
if (argc > 0 && !JS_IsNull(argv[0]))
JS_ToInt32(js, &code, argv[0]);
exit(code);
)
static JSValue js_os_dylib_open(JSContext *js, JSValue self, int argc, JSValue *argv)
@@ -718,7 +721,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, rusage, 0),
MIST_FUNC_DEF(os, mallinfo, 0),
MIST_FUNC_DEF(os, system, 1),
MIST_FUNC_DEF(os, exit, 0),
MIST_FUNC_DEF(os, exit, 1),
MIST_FUNC_DEF(os, sleep, 1),
MIST_FUNC_DEF(os, dylib_open, 1),
MIST_FUNC_DEF(os, dylib_preload, 1),

View File

@@ -421,6 +421,15 @@ Shop.extract_commit_hash = function(pkg, response) {
var open_dls = {}
var package_dylibs = {} // pkg -> [{file, symbol, dylib}, ...]
function open_dylib_cached(path) {
var handle = open_dls[path]
if (handle) return handle
handle = os.dylib_open(path)
if (!handle) return null
open_dls[path] = handle
return handle
}
// Host target detection for native dylib resolution
function detect_host_target() {
var platform = os.platform()
@@ -457,7 +466,7 @@ function try_native_mod_dylib(pkg, stem) {
if (!fd.is_file(build_path)) return null
log.shop('native dylib cache hit: ' + stem)
var handle = os.dylib_open(build_path)
var handle = open_dylib_cached(build_path)
if (!handle) return null
var sym = Shop.c_symbol_for_file(pkg, stem)
return {_native: true, _handle: handle, _sym: sym}
@@ -924,11 +933,7 @@ function try_dylib_symbol(sym, pkg, file_stem) {
})
if (!entry || !entry.dylib) return null
var handle = open_dls[entry.dylib]
if (!handle) {
handle = os.dylib_open(entry.dylib)
if (handle) open_dls[entry.dylib] = handle
}
var handle = open_dylib_cached(entry.dylib)
if (!handle) return null
if (!os.dylib_has_symbol(handle, sym)) return null
@@ -1168,8 +1173,17 @@ Shop.is_loaded = function is_loaded(path, package_context) {
}
// Create a use function bound to a specific package context
function make_use_fn(pkg) {
function make_use_fn(pkg, force_native) {
return function(path) {
var _native = null
if (force_native && !native_mode) {
_native = function() {
return Shop.use_native(path, pkg)
} disruption {
return Shop.use(path, pkg)
}
return _native()
}
return Shop.use(path, pkg)
}
}
@@ -1200,7 +1214,7 @@ function execute_module(info)
inject = Shop.script_inject_for(file_info)
env = inject_env(inject)
pkg = file_info.package
env.use = make_use_fn(pkg)
env.use = make_use_fn(pkg, true)
env = stone(env)
used = os.native_module_load_named(
mod_resolve.symbol._handle, mod_resolve.symbol._sym, env)
@@ -1844,7 +1858,7 @@ Shop.load_as_dylib = function(path, pkg) {
if (!file_info) file_info = Shop.file_info(file_path)
inject = Shop.script_inject_for(file_info)
env = inject_env(inject)
env.use = make_use_fn(real_pkg)
env.use = make_use_fn(real_pkg, true)
env = stone(env)
return os.native_module_load_named(result._handle, result._sym, env)
}
@@ -1891,32 +1905,63 @@ Shop.parse_package = function(locator) {
Shop.use_native = function(path, package_context) {
var src_path = path
if (!starts_with(path, '/'))
var locator = null
var lookup = null
var cache_key = null
var cfg = null
var old_native = null
if (!starts_with(path, '/') && !fd.is_file(path)) {
lookup = ends_with(path, '.cm') ? path : path + '.cm'
locator = resolve_locator(lookup, package_context)
if (!locator) { print('Module not found: ' + path); disrupt }
src_path = locator.path
} else if (!starts_with(path, '/')) {
src_path = fd.realpath(path)
}
if (!fd.is_file(src_path)) { print('File not found: ' + path); disrupt }
var file_info = Shop.file_info(src_path)
var pkg = file_info.package || package_context
var pkg = file_info.package || (locator ? locator.pkg : package_context)
var sym_stem = fd.basename(src_path)
var pkg_dir = null
cache_key = 'native:' + text(pkg || '') + ':' + src_path
if (use_cache[cache_key]) return use_cache[cache_key]
var sym_name = null
if (pkg)
sym_name = Shop.c_symbol_for_file(pkg, fd.basename(src_path))
if (pkg) {
pkg_dir = get_packages_dir() + '/' + safe_package_path(pkg)
if (starts_with(src_path, pkg_dir + '/')) {
sym_stem = text(src_path, length(pkg_dir) + 1)
}
sym_name = Shop.c_symbol_for_file(pkg, sym_stem)
}
var build = Shop.use('build', 'core')
var build = use_cache['core/build'] || use_cache['build']
if (!build) {
cfg = Shop.load_config()
old_native = cfg.policy.native
cfg.policy.native = false
build = Shop.use('build', 'core')
cfg.policy.native = old_native
}
var dylib_path = build.compile_native(src_path, null, null, pkg)
var handle = os.dylib_open(dylib_path)
var handle = open_dylib_cached(dylib_path)
if (!handle) { print('Failed to open native dylib: ' + dylib_path); disrupt }
// Build env with runtime functions and capabilities
var inject = Shop.script_inject_for(file_info)
var env = inject_env(inject)
env.use = make_use_fn(pkg)
env.use = make_use_fn(pkg, true)
env = stone(env)
var loaded = null
if (sym_name)
return os.native_module_load_named(handle, sym_name, env)
return os.native_module_load(handle, env)
loaded = os.native_module_load_named(handle, sym_name, env)
else
loaded = os.native_module_load(handle, env)
use_cache[cache_key] = loaded
return loaded
}
return Shop

View File

@@ -790,6 +790,50 @@ ${sw("w", "%fp", "%dest", "%r")}
@entry
${sr("a", "%obj_slot")}
${sr("b", "%key_slot")}
%ptag =l and %a, 7
%is_ptr =w ceql %ptag, 1
jnz %is_ptr, @arr_ptr, @fallback
@arr_ptr
%arr_ptr =l and %a, -8
%arr_hdr =l loadl %arr_ptr
@arr_chase
%arr_ty =l and %arr_hdr, 7
%arr_is_fwd =w ceql %arr_ty, 7
jnz %arr_is_fwd, @arr_follow, @arr_chk
@arr_follow
%arr_ptr =l shr %arr_hdr, 3
%arr_hdr =l loadl %arr_ptr
jmp @arr_chase
@arr_chk
%arr_is_array =w ceql %arr_ty, 0
jnz %arr_is_array, @arr_index, @fallback
@arr_index
%idx_tag =l and %b, 1
%idx_is_int =w ceql %idx_tag, 0
jnz %idx_is_int, @idx_ok, @ret_null
@idx_ok
%idx_l =l sar %b, 1
%idx_w =w copy %idx_l
%idx_neg =w csltw %idx_w, 0
jnz %idx_neg, @ret_null, @arr_len
@arr_len
%len_p =l add %arr_ptr, 8
%len_l =l loadl %len_p
%len_w =w copy %len_l
%in =w csltw %idx_w, %len_w
jnz %in, @load, @ret_null
@load
%idx_off_l =l extsw %idx_w
%idx_off_l =l shl %idx_off_l, 3
%vals_p =l add %arr_ptr, 16
%elem_p =l add %vals_p, %idx_off_l
%r =l loadl %elem_p
${sw("w", "%fp", "%dest", "%r")}
ret %fp
@ret_null
${sw("w", "%fp", "%dest", text(qbe.js_null))}
ret %fp
@fallback
%r =l call $cell_rt_load_dynamic(l %ctx, l %a, l %b)
%is_exc =w ceql %r, 15
jnz %is_exc, @exc, @ok
@@ -805,14 +849,49 @@ ${sw("w", "%fp", "%dest", "%r")}
@entry
${sr("a", "%arr_slot")}
${sr("b", "%idx_slot")}
%r =l call $cell_rt_load_index(l %ctx, l %a, l %b)
%is_exc =w ceql %r, 15
jnz %is_exc, @exc, @ok
@ok
%idx_tag =l and %b, 1
%idx_is_int =w ceql %idx_tag, 0
jnz %idx_is_int, @idx_ok, @ret_null
@idx_ok
%idx_l =l sar %b, 1
%idx_w =w copy %idx_l
%idx_neg =w csltw %idx_w, 0
jnz %idx_neg, @ret_null, @arr_init
@arr_init
%ptag =l and %a, 7
%is_ptr =w ceql %ptag, 1
jnz %is_ptr, @arr_ptr_ok, @ret_null
@arr_ptr_ok
%arr_ptr =l and %a, -8
%arr_hdr =l loadl %arr_ptr
@arr_chase
%arr_ty =l and %arr_hdr, 7
%arr_is_fwd =w ceql %arr_ty, 7
jnz %arr_is_fwd, @arr_follow, @arr_chk
@arr_follow
%arr_ptr =l shr %arr_hdr, 3
%arr_hdr =l loadl %arr_ptr
jmp @arr_chase
@arr_chk
%arr_is_array =w ceql %arr_ty, 0
jnz %arr_is_array, @arr_len, @ret_null
@arr_len
%len_p =l add %arr_ptr, 8
%len_l =l loadl %len_p
%len_w =w copy %len_l
%in =w csltw %idx_w, %len_w
jnz %in, @load, @ret_null
@load
%idx_off_l =l extsw %idx_w
%idx_off_l =l shl %idx_off_l, 3
%vals_p =l add %arr_ptr, 16
%elem_p =l add %vals_p, %idx_off_l
%r =l loadl %elem_p
${sw("w", "%fp", "%dest", "%r")}
ret %fp
@exc
ret 0
@ret_null
${sw("w", "%fp", "%dest", text(qbe.js_null))}
ret %fp
}`
// store_field(ctx, fp, obj_slot, val_slot, lit_idx) — no dest write
@@ -834,10 +913,37 @@ ${sr("b", "%val_slot")}
${sr("a", "%obj_slot")}
${sr("b", "%val_slot")}
${sr("c", "%key_slot")}
%ptag =l and %a, 7
%is_ptr =w ceql %ptag, 1
jnz %is_ptr, @arr_ptr, @fallback
@arr_ptr
%arr_ptr =l and %a, -8
%arr_hdr =l loadl %arr_ptr
@arr_chase
%arr_ty =l and %arr_hdr, 7
%arr_is_fwd =w ceql %arr_ty, 7
jnz %arr_is_fwd, @arr_follow, @arr_chk
@arr_follow
%arr_ptr =l shr %arr_hdr, 3
%arr_hdr =l loadl %arr_ptr
jmp @arr_chase
@arr_chk
%arr_is_array =w ceql %arr_ty, 0
jnz %arr_is_array, @arr_key_chk, @fallback
@arr_key_chk
%idx_tag =l and %c, 1
%idx_is_int =w ceql %idx_tag, 0
jnz %idx_is_int, @arr_store, @bad
@arr_store
%fp2 =l call $__store_index_ss(l %ctx, l %fp, l %obj_slot, l %val_slot, l %key_slot)
ret %fp2
@fallback
%ok =w call $cell_rt_store_dynamic(l %ctx, l %b, l %a, l %c)
jnz %ok, @ok, @exc
@ok
ret %fp
@bad
call $cell_rt_disrupt(l %ctx)
@exc
ret 0
}`
@@ -848,10 +954,151 @@ ${sr("c", "%key_slot")}
${sr("a", "%obj_slot")}
${sr("b", "%val_slot")}
${sr("c", "%idx_slot")}
%ok =w call $cell_rt_store_index(l %ctx, l %b, l %a, l %c)
jnz %ok, @ok, @exc
@ok
%idx_tag =l and %c, 1
%idx_is_int =w ceql %idx_tag, 0
jnz %idx_is_int, @idx_ok, @bad
@idx_ok
%idx_l =l sar %c, 1
%idx_w =w copy %idx_l
%idx_neg =w csltw %idx_w, 0
jnz %idx_neg, @bad, @arr_init
@arr_init
%ptag =l and %a, 7
%is_ptr =w ceql %ptag, 1
jnz %is_ptr, @arr_ptr_ok, @bad
@arr_ptr_ok
%arr_val =l copy %a
%arr_ptr =l and %arr_val, -8
%arr_hdr =l loadl %arr_ptr
@arr_chase
%arr_ty =l and %arr_hdr, 7
%arr_is_fwd =w ceql %arr_ty, 7
jnz %arr_is_fwd, @arr_follow, @arr_chk
@arr_follow
%arr_ptr =l shr %arr_hdr, 3
%arr_hdr =l loadl %arr_ptr
jmp @arr_chase
@arr_chk
%arr_is_array =w ceql %arr_ty, 0
jnz %arr_is_array, @stone_chk, @bad
@stone_chk
%arr_stone =l and %arr_hdr, 8
%arr_is_stone =w cnel %arr_stone, 0
jnz %arr_is_stone, @bad, @lens
@lens
%len_p =l add %arr_ptr, 8
%len_l =l loadl %len_p
%len_w =w copy %len_l
%cap_l =l shr %arr_hdr, 8
%cap_w =w copy %cap_l
%need_grow =w csgew %idx_w, %cap_w
jnz %need_grow, @grow_init, @set_item
@grow_init
%new_cap_w =w copy %cap_w
%cap_zero =w ceqw %new_cap_w, 0
jnz %cap_zero, @grow_cap0, @grow_check
@grow_cap0
%new_cap_w =w copy 2
jmp @grow_check
@grow_loop
%new_cap_w =w shl %new_cap_w, 1
%new_cap_neg =w csltw %new_cap_w, 0
jnz %new_cap_neg, @bad, @grow_check
@grow_check
%need_more =w cslew %new_cap_w, %idx_w
jnz %need_more, @grow_loop, @grow_alloc
@grow_alloc
%new_arr =l call $JS_NewArrayCap(l %ctx, w %new_cap_w)
%new_exc =w ceql %new_arr, 15
jnz %new_exc, @exc, @grow_refresh
@grow_refresh
%fp2 =l call $cell_rt_refresh_fp_checked(l %ctx)
jnz %fp2, @grow_reload, @exc
@grow_reload
%fp =l copy %fp2
${sr("ga", "%obj_slot")}
${sr("gb", "%val_slot")}
${sr("gc", "%idx_slot")}
%a =l copy %ga
%b =l copy %gb
%c =l copy %gc
%arr_val =l copy %a
%arr_ptr =l and %arr_val, -8
%arr_hdr =l loadl %arr_ptr
@grow_arr_chase
%arr_ty =l and %arr_hdr, 7
%arr_is_fwd =w ceql %arr_ty, 7
jnz %arr_is_fwd, @grow_arr_follow, @grow_arr_ok
@grow_arr_follow
%arr_ptr =l shr %arr_hdr, 3
%arr_hdr =l loadl %arr_ptr
jmp @grow_arr_chase
@grow_arr_ok
%grow_arr_is_array =w ceql %arr_ty, 0
jnz %grow_arr_is_array, @grow_arr_type_ok, @bad
@grow_arr_type_ok
%old_cap_l =l shr %arr_hdr, 8
%old_len_p =l add %arr_ptr, 8
%old_len_l =l loadl %old_len_p
%old_len_w =w copy %old_len_l
%new_ptr =l and %new_arr, -8
%old_vals =l add %arr_ptr, 16
%new_vals =l add %new_ptr, 16
%i_w =w copy 0
@copy_cond
%copy_more =w csltw %i_w, %old_len_w
jnz %copy_more, @copy_body, @copy_done
@copy_body
%i_l =l extsw %i_w
%i_off =l shl %i_l, 3
%old_ep =l add %old_vals, %i_off
%new_ep =l add %new_vals, %i_off
%ev =l loadl %old_ep
%ev_is_self =w ceql %ev, %arr_val
jnz %ev_is_self, @copy_self, @copy_store
@copy_self
storel %new_arr, %new_ep
jmp @copy_next
@copy_store
storel %ev, %new_ep
@copy_next
%i_w =w add %i_w, 1
jmp @copy_cond
@copy_done
storel %old_len_l, %old_len_p
%old_size =l shl %old_cap_l, 3
%old_size =l add %old_size, 16
%fwd =l shl %new_ptr, 3
%fwd =l or %fwd, 7
storel %fwd, %arr_ptr
%arr_size_p =l add %arr_ptr, 8
storel %old_size, %arr_size_p
%obj_slot_o =l shl %obj_slot, 3
%obj_slot_p =l add %fp2, %obj_slot_o
storel %new_arr, %obj_slot_p
%arr_val =l copy %new_arr
%arr_ptr =l copy %new_ptr
%arr_hdr =l loadl %arr_ptr
%len_p =l add %arr_ptr, 8
storel %old_len_l, %len_p
%len_l =l copy %old_len_l
%len_w =w copy %old_len_w
@set_item
%need_len =w csgew %idx_w, %len_w
jnz %need_len, @bump_len, @store_item
@bump_len
%next_len_w =w add %idx_w, 1
%next_len_l =l extsw %next_len_w
storel %next_len_l, %len_p
@store_item
%idx2_l =l extsw %idx_w
%idx2_off =l shl %idx2_l, 3
%vals_p =l add %arr_ptr, 16
%item_p =l add %vals_p, %idx2_off
storel %b, %item_p
ret %fp
@bad
call $cell_rt_disrupt(l %ctx)
@exc
ret 0
}`
@@ -937,12 +1184,133 @@ ${alloc_tail("%r")}
@entry
${sr("a", "%arr_slot")}
${sr("b", "%val_slot")}
%r =l call $cell_rt_push(l %ctx, l %a, l %b)
%ptag =l and %a, 7
%is_ptr =w ceql %ptag, 1
jnz %is_ptr, @arr_init, @bad
@arr_init
%arr_val =l copy %a
%arr_ptr =l and %arr_val, -8
%arr_hdr =l loadl %arr_ptr
@arr_chase
%arr_ty =l and %arr_hdr, 7
%arr_is_fwd =w ceql %arr_ty, 7
jnz %arr_is_fwd, @arr_follow, @arr_ok
@arr_follow
%arr_ptr =l shr %arr_hdr, 3
%arr_hdr =l loadl %arr_ptr
jmp @arr_chase
@arr_ok
%arr_is_array =w ceql %arr_ty, 0
jnz %arr_is_array, @arr_type_ok, @bad
@arr_type_ok
%arr_stone =l and %arr_hdr, 8
%arr_is_stone =w cnel %arr_stone, 0
jnz %arr_is_stone, @bad, @lens
@lens
%len_p =l add %arr_ptr, 8
%len_l =l loadl %len_p
%len_w =w copy %len_l
%cap_l =l shr %arr_hdr, 8
%cap_w =w copy %cap_l
%need_grow =w csgew %len_w, %cap_w
jnz %need_grow, @grow, @store_push
@grow
%new_cap_w =w copy %cap_w
%cap_zero =w ceqw %new_cap_w, 0
jnz %cap_zero, @grow_cap0, @grow_dbl
@grow_cap0
%new_cap_w =w copy 2
jmp @grow_alloc
@grow_dbl
%new_cap_w =w shl %new_cap_w, 1
%new_cap_neg =w csltw %new_cap_w, 0
jnz %new_cap_neg, @bad, @grow_alloc
@grow_alloc
%new_arr =l call $JS_NewArrayCap(l %ctx, w %new_cap_w)
%new_exc =w ceql %new_arr, 15
jnz %new_exc, @exc, @grow_refresh
@grow_refresh
%fp2 =l call $cell_rt_refresh_fp_checked(l %ctx)
jnz %fp2, @ok, @exc
@ok
${sw("w", "%fp2", "%arr_slot", "%r")}
ret %fp2
jnz %fp2, @grow_reload, @exc
@grow_reload
%fp =l copy %fp2
${sr("ga", "%arr_slot")}
${sr("gb", "%val_slot")}
%a =l copy %ga
%b =l copy %gb
%arr_val =l copy %a
%arr_ptr =l and %arr_val, -8
%arr_hdr =l loadl %arr_ptr
@grow_arr_chase
%arr_ty =l and %arr_hdr, 7
%arr_is_fwd =w ceql %arr_ty, 7
jnz %arr_is_fwd, @grow_arr_follow, @grow_arr_ok
@grow_arr_follow
%arr_ptr =l shr %arr_hdr, 3
%arr_hdr =l loadl %arr_ptr
jmp @grow_arr_chase
@grow_arr_ok
%grow_arr_is_array =w ceql %arr_ty, 0
jnz %grow_arr_is_array, @grow_arr_type_ok, @bad
@grow_arr_type_ok
%old_cap_l =l shr %arr_hdr, 8
%old_len_p =l add %arr_ptr, 8
%old_len_l =l loadl %old_len_p
%old_len_w =w copy %old_len_l
%new_ptr =l and %new_arr, -8
%old_vals =l add %arr_ptr, 16
%new_vals =l add %new_ptr, 16
%i_w =w copy 0
@copy_cond
%copy_more =w csltw %i_w, %old_len_w
jnz %copy_more, @copy_body, @copy_done
@copy_body
%i_l =l extsw %i_w
%i_off =l shl %i_l, 3
%old_ep =l add %old_vals, %i_off
%new_ep =l add %new_vals, %i_off
%ev =l loadl %old_ep
%ev_is_self =w ceql %ev, %arr_val
jnz %ev_is_self, @copy_self, @copy_store
@copy_self
storel %new_arr, %new_ep
jmp @copy_next
@copy_store
storel %ev, %new_ep
@copy_next
%i_w =w add %i_w, 1
jmp @copy_cond
@copy_done
storel %old_len_l, %old_len_p
%old_size =l shl %old_cap_l, 3
%old_size =l add %old_size, 16
%fwd =l shl %new_ptr, 3
%fwd =l or %fwd, 7
storel %fwd, %arr_ptr
%arr_size_p =l add %arr_ptr, 8
storel %old_size, %arr_size_p
%arr_slot_o =l shl %arr_slot, 3
%arr_slot_p =l add %fp2, %arr_slot_o
storel %new_arr, %arr_slot_p
%arr_val =l copy %new_arr
%arr_ptr =l copy %new_ptr
%arr_hdr =l loadl %arr_ptr
%len_p =l add %arr_ptr, 8
storel %old_len_l, %len_p
%len_l =l copy %old_len_l
%len_w =w copy %old_len_w
@store_push
%idx_l =l extsw %len_w
%idx_off =l shl %idx_l, 3
%vals_p =l add %arr_ptr, 16
%item_p =l add %vals_p, %idx_off
storel %b, %item_p
%next_len_w =w add %len_w, 1
%next_len_l =l extsw %next_len_w
storel %next_len_l, %len_p
ret %fp
@bad
call $cell_rt_disrupt(l %ctx)
@exc
ret 0
}`
@@ -951,8 +1319,51 @@ ${sw("w", "%fp2", "%arr_slot", "%r")}
h[] = `export function l $__pop_ss(l %ctx, l %fp, l %dest, l %arr_slot) {
@entry
${sr("a", "%arr_slot")}
%r =l call $cell_rt_pop(l %ctx, l %a)
${alloc_tail("%r")}
%ptag =l and %a, 7
%is_ptr =w ceql %ptag, 1
jnz %is_ptr, @arr_init, @bad
@arr_init
%arr_ptr =l and %a, -8
%arr_hdr =l loadl %arr_ptr
@arr_chase
%arr_ty =l and %arr_hdr, 7
%arr_is_fwd =w ceql %arr_ty, 7
jnz %arr_is_fwd, @arr_follow, @arr_ok
@arr_follow
%arr_ptr =l shr %arr_hdr, 3
%arr_hdr =l loadl %arr_ptr
jmp @arr_chase
@arr_ok
%arr_is_array =w ceql %arr_ty, 0
jnz %arr_is_array, @arr_type_ok, @bad
@arr_type_ok
%arr_stone =l and %arr_hdr, 8
%arr_is_stone =w cnel %arr_stone, 0
jnz %arr_is_stone, @bad, @len_chk
@len_chk
%len_p =l add %arr_ptr, 8
%len_l =l loadl %len_p
%len_w =w copy %len_l
%empty =w ceqw %len_w, 0
jnz %empty, @ret_null, @do_pop
@do_pop
%last_w =w sub %len_w, 1
%last_l =l extsw %last_w
%last_off =l shl %last_l, 3
%vals_p =l add %arr_ptr, 16
%item_p =l add %vals_p, %last_off
%r =l loadl %item_p
storel ${text(qbe.js_null)}, %item_p
%new_len_l =l extsw %last_w
storel %new_len_l, %len_p
${sw("w", "%fp", "%dest", "%r")}
ret %fp
@ret_null
${sw("w", "%fp", "%dest", text(qbe.js_null))}
ret %fp
@bad
call $cell_rt_disrupt(l %ctx)
ret 0
}`
// length(ctx, fp, dest, src)

View File

@@ -779,6 +779,22 @@ static int cell_check_call_arity(JSContext *ctx, JSFunction *fn, int argc) {
return 1;
}
static inline void cell_copy_args_0_4(JSValue *fp, JSValue *argv, int copy) {
/* fp[0] is `this`; copy args into fp[1..4] */
switch (copy) {
case 4: fp[4] = argv[3];
case 3: fp[3] = argv[2];
case 2: fp[2] = argv[1];
case 1: fp[1] = argv[0];
case 0: break;
default: break;
}
}
static inline void cell_sync_dl_from_native_fn(NativeRTState *st, JSFunction *fn) {
st->current_dl_handle = JS_VALUE_GET_CODE(fn->u.cell.code)->u.native.dl_handle;
}
/* Entry point called from JS_CallInternal / JS_Call / MACH_INVOKE
for JS_FUNC_KIND_NATIVE functions. */
JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
@@ -822,8 +838,14 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
fp[0] = this_obj;
int copy = (argc < arity) ? argc : arity;
if (copy < 0) copy = argc; /* variadic: copy all */
for (int i = 0; i < copy && i < nr_slots - 1; i++)
fp[1 + i] = argv[i];
if (copy > nr_slots - 1)
copy = nr_slots - 1;
if (unlikely(copy > 4)) {
JS_RaiseDisrupt(ctx, "native calls support at most 4 arguments");
RETURN_DISPATCH(JS_EXCEPTION);
}
if (copy > 0 && argv)
cell_copy_args_0_4(fp, argv, copy);
/* Link function to frame for closure access */
JSFrameRegister *frame = (JSFrameRegister *)((char *)fp - offsetof(JSFrameRegister, slots));
@@ -875,6 +897,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
/* Resume caller with exception pending */
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(exc_fn->u.cell.code)->u.native.fn_ptr;
cell_sync_dl_from_native_fn(st, exc_fn);
JS_PopGCRef(ctx, &callee_ref);
continue;
}
@@ -883,6 +906,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
if (!cell_check_call_arity(ctx, callee_fn, callee_argc)) {
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(exc_fn->u.cell.code)->u.native.fn_ptr;
cell_sync_dl_from_native_fn(st, exc_fn);
JS_PopGCRef(ctx, &callee_ref);
continue;
}
@@ -910,6 +934,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(st, st->aot_depth - 1)->val);
fp = (JSValue *)frame->slots;
fn = callee_ptr;
cell_sync_dl_from_native_fn(st, callee_fn);
} else {
/* Regular call: link caller and push prepared callee frame. */
int ret_info = JS_VALUE_GET_INT(frame->address);
@@ -931,12 +956,14 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
fp = (JSValue *)frame->slots;
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(exc_fn->u.cell.code)->u.native.fn_ptr;
cell_sync_dl_from_native_fn(st, exc_fn);
JS_PopGCRef(ctx, &callee_ref);
continue;
}
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(st, st->aot_depth - 1)->val);
fp = (JSValue *)frame->slots;
fn = callee_ptr;
cell_sync_dl_from_native_fn(st, callee_fn);
}
} else {
/* Non-native callee (C function, register VM, etc.) —
@@ -968,6 +995,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
Just resume it — it will detect JS_EXCEPTION in the return slot. */
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(exc_fn->u.cell.code)->u.native.fn_ptr;
cell_sync_dl_from_native_fn(st, exc_fn);
JS_PopGCRef(ctx, &callee_ref);
continue;
}
@@ -999,6 +1027,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
/* Resume caller */
JSFunction *caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(caller_fn->u.cell.code)->u.native.fn_ptr;
cell_sync_dl_from_native_fn(st, caller_fn);
} else {
/* Regular call: store result and resume current function */
int ret_info = JS_VALUE_GET_INT(frame->address);
@@ -1008,6 +1037,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
/* fn stays the same — we resume the same function at next segment */
JSFunction *cur_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(cur_fn->u.cell.code)->u.native.fn_ptr;
cell_sync_dl_from_native_fn(st, cur_fn);
}
}
JS_PopGCRef(ctx, &callee_ref);
@@ -1041,6 +1071,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
JSFunction *exc_caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(exc_caller_fn->u.cell.code)->u.native.fn_ptr;
cell_sync_dl_from_native_fn(st, exc_caller_fn);
continue;
}
@@ -1065,6 +1096,7 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
JSFunction *caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
fn = (cell_compiled_fn)JS_VALUE_GET_CODE(caller_fn->u.cell.code)->u.native.fn_ptr;
cell_sync_dl_from_native_fn(st, caller_fn);
continue;
}

View File

@@ -150,7 +150,10 @@ int JS_IsPretext (JSValue v) {
}
JSValue *JS_PushGCRef (JSContext *ctx, JSGCRef *ref) {
assert(ref != ctx->top_gc_ref && "JS_ROOT used in a loop — same address pushed twice");
if (ref == ctx->top_gc_ref) {
fprintf(stderr, "[warn] JS_PushGCRef duplicate top ref (non-fatal)\n");
return &ref->val;
}
ref->prev = ctx->top_gc_ref;
ctx->top_gc_ref = ref;
ref->val = JS_NULL;
@@ -158,13 +161,20 @@ JSValue *JS_PushGCRef (JSContext *ctx, JSGCRef *ref) {
}
JSValue JS_PopGCRef (JSContext *ctx, JSGCRef *ref) {
assert(ctx->top_gc_ref == ref && "JS_PopGCRef: not popping top of stack — mismatched push/pop");
ctx->top_gc_ref = ref->prev;
if (ctx->top_gc_ref == ref) {
ctx->top_gc_ref = ref->prev;
return ref->val;
}
fprintf(stderr, "[warn] JS_PopGCRef mismatched pop (non-fatal)\n");
return ref->val;
}
JSValue *JS_AddGCRef (JSContext *ctx, JSGCRef *ref) {
assert(ref != ctx->last_gc_ref && "JS_AddGCRef: same address added twice — cycle in GC ref list");
if (ref == ctx->last_gc_ref) {
fprintf(stderr, "[warn] JS_AddGCRef duplicate tail ref (non-fatal)\n");
return &ref->val;
}
ref->prev = ctx->last_gc_ref;
ctx->last_gc_ref = ref;
ref->val = JS_NULL;
@@ -10362,6 +10372,8 @@ static JSValue js_cell_pop (JSContext *ctx, JSValue this_val, int argc, JSValue
if (!JS_IsArray (obj)) return JS_NULL;
JSArray *arr = JS_VALUE_GET_ARRAY (obj);
if (objhdr_s (arr->mist_hdr))
return JS_RaiseDisrupt (ctx, "cannot pop from a stoned array");
if (arr->len == 0) return JS_NULL;

View File

@@ -151,6 +151,21 @@ var streamline = function(ir, log) {
slot_types[instr[1]] = src_type != null ? src_type : T_UNKNOWN
return null
}
if (op == "load_index") {
slot_types[instr[2]] = T_ARRAY
slot_types[instr[3]] = T_INT
} else if (op == "store_index") {
slot_types[instr[1]] = T_ARRAY
slot_types[instr[3]] = T_INT
} else if (op == "load_field") {
slot_types[instr[2]] = T_RECORD
} else if (op == "store_field") {
slot_types[instr[1]] = T_RECORD
} else if (op == "push") {
slot_types[instr[1]] = T_ARRAY
} else if (op == "pop") {
slot_types[instr[2]] = T_ARRAY
}
rule = write_rules[op]
if (rule != null) {
typ = rule[1]
@@ -787,26 +802,32 @@ var streamline = function(ir, log) {
// Dynamic access reduction
if (op == "load_dynamic") {
old_op = op
if (slot_is(slot_types, instr[3], T_TEXT)) {
if (slot_is(slot_types, instr[2], T_RECORD) && slot_is(slot_types, instr[3], T_TEXT)) {
instr[0] = "load_field"
if (events != null) {
events[] = {
event: "rewrite",
pass: "eliminate_type_checks",
rule: "dynamic_to_field",
rule: "dynamic_record_to_field",
at: i, before: old_op, after: instr[0],
why: {slot: instr[3], known_type: slot_types[instr[3]]}
why: {
object_slot: instr[2], object_type: slot_types[instr[2]],
key_slot: instr[3], key_type: slot_types[instr[3]]
}
}
}
} else if (slot_is(slot_types, instr[3], T_INT)) {
} else if (slot_is(slot_types, instr[2], T_ARRAY) && slot_is(slot_types, instr[3], T_INT)) {
instr[0] = "load_index"
if (events != null) {
events[] = {
event: "rewrite",
pass: "eliminate_type_checks",
rule: "dynamic_to_index",
rule: "dynamic_array_to_index",
at: i, before: old_op, after: instr[0],
why: {slot: instr[3], known_type: slot_types[instr[3]]}
why: {
object_slot: instr[2], object_type: slot_types[instr[2]],
key_slot: instr[3], key_type: slot_types[instr[3]]
}
}
}
}
@@ -816,26 +837,32 @@ var streamline = function(ir, log) {
}
if (op == "store_dynamic") {
old_op = op
if (slot_is(slot_types, instr[3], T_TEXT)) {
if (slot_is(slot_types, instr[1], T_RECORD) && slot_is(slot_types, instr[3], T_TEXT)) {
instr[0] = "store_field"
if (events != null) {
events[] = {
event: "rewrite",
pass: "eliminate_type_checks",
rule: "dynamic_to_field",
rule: "dynamic_record_to_field",
at: i, before: old_op, after: instr[0],
why: {slot: instr[3], known_type: slot_types[instr[3]]}
why: {
object_slot: instr[1], object_type: slot_types[instr[1]],
key_slot: instr[3], key_type: slot_types[instr[3]]
}
}
}
} else if (slot_is(slot_types, instr[3], T_INT)) {
} else if (slot_is(slot_types, instr[1], T_ARRAY) && slot_is(slot_types, instr[3], T_INT)) {
instr[0] = "store_index"
if (events != null) {
events[] = {
event: "rewrite",
pass: "eliminate_type_checks",
rule: "dynamic_to_index",
rule: "dynamic_array_to_index",
at: i, before: old_op, after: instr[0],
why: {slot: instr[3], known_type: slot_types[instr[3]]}
why: {
object_slot: instr[1], object_type: slot_types[instr[1]],
key_slot: instr[3], key_type: slot_types[instr[3]]
}
}
}
}