Merge branch 'optimize_mcode' into fix_aot
This commit is contained in:
86
bench_arith.ce
Normal file
86
bench_arith.ce
Normal file
@@ -0,0 +1,86 @@
|
||||
// bench_arith.ce — arithmetic and number crunching benchmark
|
||||
// Tests: integer add/mul, float ops, loop counter overhead, conditionals
|
||||
|
||||
var time = use('time')
|
||||
|
||||
def iterations = 2000000
|
||||
|
||||
// 1. Integer sum in tight loop
|
||||
function bench_int_sum() {
|
||||
var i = 0
|
||||
var s = 0
|
||||
for (i = 0; i < iterations; i++) {
|
||||
s = s + i
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 2. Integer multiply + mod (sieve-like)
|
||||
function bench_int_mul_mod() {
|
||||
var i = 0
|
||||
var s = 0
|
||||
for (i = 1; i < iterations; i++) {
|
||||
s = s + (i * 7 % 1000)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 3. Float math — accumulate with division
|
||||
function bench_float_arith() {
|
||||
var i = 0
|
||||
var s = 0.5
|
||||
for (i = 1; i < iterations; i++) {
|
||||
s = s + 1.0 / i
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 4. Nested loop with branch (fizzbuzz-like counter)
|
||||
function bench_branch() {
|
||||
var i = 0
|
||||
var fizz = 0
|
||||
var buzz = 0
|
||||
var fizzbuzz = 0
|
||||
for (i = 1; i <= iterations; i++) {
|
||||
if (i % 15 == 0) {
|
||||
fizzbuzz = fizzbuzz + 1
|
||||
} else if (i % 3 == 0) {
|
||||
fizz = fizz + 1
|
||||
} else if (i % 5 == 0) {
|
||||
buzz = buzz + 1
|
||||
}
|
||||
}
|
||||
return fizz + buzz + fizzbuzz
|
||||
}
|
||||
|
||||
// 5. Nested loop (small inner)
|
||||
function bench_nested() {
|
||||
var i = 0
|
||||
var j = 0
|
||||
var s = 0
|
||||
def outer = 5000
|
||||
def inner = 5000
|
||||
for (i = 0; i < outer; i++) {
|
||||
for (j = 0; j < inner; j++) {
|
||||
s = s + 1
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Run each and print timing
|
||||
function run(name, fn) {
|
||||
var start = time.number()
|
||||
var result = fn()
|
||||
var elapsed = time.number() - start
|
||||
var ms = whole(elapsed * 100000) / 100
|
||||
log.console(` ${name}: ${ms} ms (result: ${result})`)
|
||||
}
|
||||
|
||||
log.console("=== Arithmetic Benchmark ===")
|
||||
log.console(` iterations: ${iterations}`)
|
||||
run("int_sum ", bench_int_sum)
|
||||
run("int_mul_mod ", bench_int_mul_mod)
|
||||
run("float_arith ", bench_float_arith)
|
||||
run("branch ", bench_branch)
|
||||
run("nested_loop ", bench_nested)
|
||||
67
bench_arith.js
Normal file
67
bench_arith.js
Normal file
@@ -0,0 +1,67 @@
|
||||
// bench_arith.js — arithmetic and number crunching benchmark (QuickJS)
|
||||
|
||||
const iterations = 2000000;
|
||||
|
||||
function bench_int_sum() {
|
||||
let s = 0;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
s = s + i;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function bench_int_mul_mod() {
|
||||
let s = 0;
|
||||
for (let i = 1; i < iterations; i++) {
|
||||
s = s + (i * 7 % 1000);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function bench_float_arith() {
|
||||
let s = 0.5;
|
||||
for (let i = 1; i < iterations; i++) {
|
||||
s = s + 1.0 / i;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function bench_branch() {
|
||||
let fizz = 0, buzz = 0, fizzbuzz = 0;
|
||||
for (let i = 1; i <= iterations; i++) {
|
||||
if (i % 15 === 0) {
|
||||
fizzbuzz = fizzbuzz + 1;
|
||||
} else if (i % 3 === 0) {
|
||||
fizz = fizz + 1;
|
||||
} else if (i % 5 === 0) {
|
||||
buzz = buzz + 1;
|
||||
}
|
||||
}
|
||||
return fizz + buzz + fizzbuzz;
|
||||
}
|
||||
|
||||
function bench_nested() {
|
||||
let s = 0;
|
||||
const outer = 5000, inner = 5000;
|
||||
for (let i = 0; i < outer; i++) {
|
||||
for (let j = 0; j < inner; j++) {
|
||||
s = s + 1;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function run(name, fn) {
|
||||
const start = performance.now();
|
||||
const result = fn();
|
||||
const elapsed = performance.now() - start;
|
||||
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
|
||||
}
|
||||
|
||||
console.log("=== Arithmetic Benchmark ===");
|
||||
console.log(` iterations: ${iterations}`);
|
||||
run("int_sum ", bench_int_sum);
|
||||
run("int_mul_mod ", bench_int_mul_mod);
|
||||
run("float_arith ", bench_float_arith);
|
||||
run("branch ", bench_branch);
|
||||
run("nested_loop ", bench_nested);
|
||||
68
bench_arith.lua
Normal file
68
bench_arith.lua
Normal file
@@ -0,0 +1,68 @@
|
||||
-- bench_arith.lua — arithmetic and number crunching benchmark (Lua)
|
||||
|
||||
local iterations = 2000000
|
||||
local clock = os.clock
|
||||
|
||||
local function bench_int_sum()
|
||||
local s = 0
|
||||
for i = 0, iterations - 1 do
|
||||
s = s + i
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function bench_int_mul_mod()
|
||||
local s = 0
|
||||
for i = 1, iterations - 1 do
|
||||
s = s + (i * 7 % 1000)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function bench_float_arith()
|
||||
local s = 0.5
|
||||
for i = 1, iterations - 1 do
|
||||
s = s + 1.0 / i
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function bench_branch()
|
||||
local fizz, buzz, fizzbuzz = 0, 0, 0
|
||||
for i = 1, iterations do
|
||||
if i % 15 == 0 then
|
||||
fizzbuzz = fizzbuzz + 1
|
||||
elseif i % 3 == 0 then
|
||||
fizz = fizz + 1
|
||||
elseif i % 5 == 0 then
|
||||
buzz = buzz + 1
|
||||
end
|
||||
end
|
||||
return fizz + buzz + fizzbuzz
|
||||
end
|
||||
|
||||
local function bench_nested()
|
||||
local s = 0
|
||||
local outer, inner = 5000, 5000
|
||||
for i = 0, outer - 1 do
|
||||
for j = 0, inner - 1 do
|
||||
s = s + 1
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function run(name, fn)
|
||||
local start = clock()
|
||||
local result = fn()
|
||||
local elapsed = (clock() - start) * 1000
|
||||
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
|
||||
end
|
||||
|
||||
print("=== Arithmetic Benchmark ===")
|
||||
print(string.format(" iterations: %d", iterations))
|
||||
run("int_sum ", bench_int_sum)
|
||||
run("int_mul_mod ", bench_int_mul_mod)
|
||||
run("float_arith ", bench_float_arith)
|
||||
run("branch ", bench_branch)
|
||||
run("nested_loop ", bench_nested)
|
||||
113
bench_array.ce
Normal file
113
bench_array.ce
Normal file
@@ -0,0 +1,113 @@
|
||||
// bench_array.ce — array operation benchmark
|
||||
// Tests: sequential access, push/build, index write, sum reduction, sort
|
||||
|
||||
var time = use('time')
|
||||
|
||||
def size = 100000
|
||||
|
||||
// 1. Build array with push
|
||||
function bench_push() {
|
||||
var a = []
|
||||
var i = 0
|
||||
for (i = 0; i < size; i++) {
|
||||
a[] = i
|
||||
}
|
||||
return length(a)
|
||||
}
|
||||
|
||||
// 2. Index write into preallocated array
|
||||
function bench_index_write() {
|
||||
var a = array(size, 0)
|
||||
var i = 0
|
||||
for (i = 0; i < size; i++) {
|
||||
a[i] = i
|
||||
}
|
||||
return a[size - 1]
|
||||
}
|
||||
|
||||
// 3. Sequential read and sum
|
||||
function bench_seq_read() {
|
||||
var a = array(size, 0)
|
||||
var i = 0
|
||||
for (i = 0; i < size; i++) {
|
||||
a[i] = i
|
||||
}
|
||||
var s = 0
|
||||
for (i = 0; i < size; i++) {
|
||||
s = s + a[i]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 4. Reverse array in-place
|
||||
function bench_reverse() {
|
||||
var a = array(size, 0)
|
||||
var i = 0
|
||||
for (i = 0; i < size; i++) {
|
||||
a[i] = i
|
||||
}
|
||||
var lo = 0
|
||||
var hi = size - 1
|
||||
var tmp = 0
|
||||
while (lo < hi) {
|
||||
tmp = a[lo]
|
||||
a[lo] = a[hi]
|
||||
a[hi] = tmp
|
||||
lo = lo + 1
|
||||
hi = hi - 1
|
||||
}
|
||||
return a[0]
|
||||
}
|
||||
|
||||
// 5. Nested array access (matrix-like, 300x300)
|
||||
function bench_matrix() {
|
||||
def n = 300
|
||||
var mat = array(n, null)
|
||||
var i = 0
|
||||
var j = 0
|
||||
for (i = 0; i < n; i++) {
|
||||
mat[i] = array(n, 0)
|
||||
for (j = 0; j < n; j++) {
|
||||
mat[i][j] = i * n + j
|
||||
}
|
||||
}
|
||||
// sum diagonal
|
||||
var s = 0
|
||||
for (i = 0; i < n; i++) {
|
||||
s = s + mat[i][i]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 6. filter-like: count evens
|
||||
function bench_filter_count() {
|
||||
var a = array(size, 0)
|
||||
var i = 0
|
||||
for (i = 0; i < size; i++) {
|
||||
a[i] = i
|
||||
}
|
||||
var count = 0
|
||||
for (i = 0; i < size; i++) {
|
||||
if (a[i] % 2 == 0) {
|
||||
count = count + 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
function run(name, fn) {
|
||||
var start = time.number()
|
||||
var result = fn()
|
||||
var elapsed = time.number() - start
|
||||
var ms = whole(elapsed * 100000) / 100
|
||||
log.console(` ${name}: ${ms} ms (result: ${result})`)
|
||||
}
|
||||
|
||||
log.console("=== Array Benchmark ===")
|
||||
log.console(` size: ${size}`)
|
||||
run("push ", bench_push)
|
||||
run("index_write ", bench_index_write)
|
||||
run("seq_read_sum ", bench_seq_read)
|
||||
run("reverse ", bench_reverse)
|
||||
run("matrix_300 ", bench_matrix)
|
||||
run("filter_count ", bench_filter_count)
|
||||
93
bench_array.js
Normal file
93
bench_array.js
Normal file
@@ -0,0 +1,93 @@
|
||||
// bench_array.js — array operation benchmark (QuickJS)
|
||||
|
||||
const size = 100000;
|
||||
|
||||
function bench_push() {
|
||||
let a = [];
|
||||
for (let i = 0; i < size; i++) {
|
||||
a.push(i);
|
||||
}
|
||||
return a.length;
|
||||
}
|
||||
|
||||
function bench_index_write() {
|
||||
let a = new Array(size).fill(0);
|
||||
for (let i = 0; i < size; i++) {
|
||||
a[i] = i;
|
||||
}
|
||||
return a[size - 1];
|
||||
}
|
||||
|
||||
function bench_seq_read() {
|
||||
let a = new Array(size).fill(0);
|
||||
for (let i = 0; i < size; i++) {
|
||||
a[i] = i;
|
||||
}
|
||||
let s = 0;
|
||||
for (let i = 0; i < size; i++) {
|
||||
s = s + a[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function bench_reverse() {
|
||||
let a = new Array(size).fill(0);
|
||||
for (let i = 0; i < size; i++) {
|
||||
a[i] = i;
|
||||
}
|
||||
let lo = 0, hi = size - 1, tmp;
|
||||
while (lo < hi) {
|
||||
tmp = a[lo];
|
||||
a[lo] = a[hi];
|
||||
a[hi] = tmp;
|
||||
lo = lo + 1;
|
||||
hi = hi - 1;
|
||||
}
|
||||
return a[0];
|
||||
}
|
||||
|
||||
function bench_matrix() {
|
||||
const n = 300;
|
||||
let mat = new Array(n);
|
||||
for (let i = 0; i < n; i++) {
|
||||
mat[i] = new Array(n).fill(0);
|
||||
for (let j = 0; j < n; j++) {
|
||||
mat[i][j] = i * n + j;
|
||||
}
|
||||
}
|
||||
let s = 0;
|
||||
for (let i = 0; i < n; i++) {
|
||||
s = s + mat[i][i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function bench_filter_count() {
|
||||
let a = new Array(size).fill(0);
|
||||
for (let i = 0; i < size; i++) {
|
||||
a[i] = i;
|
||||
}
|
||||
let count = 0;
|
||||
for (let i = 0; i < size; i++) {
|
||||
if (a[i] % 2 === 0) {
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function run(name, fn) {
|
||||
const start = performance.now();
|
||||
const result = fn();
|
||||
const elapsed = performance.now() - start;
|
||||
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
|
||||
}
|
||||
|
||||
console.log("=== Array Benchmark ===");
|
||||
console.log(` size: ${size}`);
|
||||
run("push ", bench_push);
|
||||
run("index_write ", bench_index_write);
|
||||
run("seq_read_sum ", bench_seq_read);
|
||||
run("reverse ", bench_reverse);
|
||||
run("matrix_300 ", bench_matrix);
|
||||
run("filter_count ", bench_filter_count);
|
||||
93
bench_array.lua
Normal file
93
bench_array.lua
Normal file
@@ -0,0 +1,93 @@
|
||||
-- bench_array.lua — array operation benchmark (Lua)
|
||||
|
||||
local size = 100000
|
||||
local clock = os.clock
|
||||
|
||||
local function bench_push()
|
||||
local a = {}
|
||||
for i = 0, size - 1 do
|
||||
a[#a + 1] = i
|
||||
end
|
||||
return #a
|
||||
end
|
||||
|
||||
local function bench_index_write()
|
||||
local a = {}
|
||||
for i = 1, size do a[i] = 0 end
|
||||
for i = 1, size do
|
||||
a[i] = i - 1
|
||||
end
|
||||
return a[size]
|
||||
end
|
||||
|
||||
local function bench_seq_read()
|
||||
local a = {}
|
||||
for i = 1, size do
|
||||
a[i] = i - 1
|
||||
end
|
||||
local s = 0
|
||||
for i = 1, size do
|
||||
s = s + a[i]
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function bench_reverse()
|
||||
local a = {}
|
||||
for i = 1, size do
|
||||
a[i] = i - 1
|
||||
end
|
||||
local lo, hi = 1, size
|
||||
while lo < hi do
|
||||
a[lo], a[hi] = a[hi], a[lo]
|
||||
lo = lo + 1
|
||||
hi = hi - 1
|
||||
end
|
||||
return a[1]
|
||||
end
|
||||
|
||||
local function bench_matrix()
|
||||
local n = 300
|
||||
local mat = {}
|
||||
for i = 1, n do
|
||||
mat[i] = {}
|
||||
for j = 1, n do
|
||||
mat[i][j] = (i - 1) * n + (j - 1)
|
||||
end
|
||||
end
|
||||
local s = 0
|
||||
for i = 1, n do
|
||||
s = s + mat[i][i]
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function bench_filter_count()
|
||||
local a = {}
|
||||
for i = 1, size do
|
||||
a[i] = i - 1
|
||||
end
|
||||
local count = 0
|
||||
for i = 1, size do
|
||||
if a[i] % 2 == 0 then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
local function run(name, fn)
|
||||
local start = clock()
|
||||
local result = fn()
|
||||
local elapsed = (clock() - start) * 1000
|
||||
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
|
||||
end
|
||||
|
||||
print("=== Array Benchmark ===")
|
||||
print(string.format(" size: %d", size))
|
||||
run("push ", bench_push)
|
||||
run("index_write ", bench_index_write)
|
||||
run("seq_read_sum ", bench_seq_read)
|
||||
run("reverse ", bench_reverse)
|
||||
run("matrix_300 ", bench_matrix)
|
||||
run("filter_count ", bench_filter_count)
|
||||
21
bench_fib.ce
Normal file
21
bench_fib.ce
Normal file
@@ -0,0 +1,21 @@
|
||||
var time = use('time')
|
||||
|
||||
function fib(n) {
|
||||
if (n < 2) {
|
||||
return n
|
||||
}
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
}
|
||||
|
||||
function run(name, fn) {
|
||||
var start = time.number()
|
||||
var result = fn()
|
||||
var elapsed = time.number() - start
|
||||
var ms = whole(elapsed * 100000) / 100
|
||||
log.console(` ${name}: ${ms} ms (result: ${result})`)
|
||||
}
|
||||
|
||||
log.console("=== Cell fib ===")
|
||||
run("fib(25)", function() { return fib(25) })
|
||||
run("fib(30)", function() { return fib(30) })
|
||||
run("fib(35)", function() { return fib(35) })
|
||||
118
bench_object.ce
Normal file
118
bench_object.ce
Normal file
@@ -0,0 +1,118 @@
|
||||
// bench_object.ce — object/record and string benchmark
|
||||
// Tests: property read/write, string concat, string interpolation, method-like dispatch
|
||||
|
||||
var time = use('time')
|
||||
|
||||
def iterations = 200000
|
||||
|
||||
// 1. Record create + property write
|
||||
function bench_record_create() {
|
||||
var i = 0
|
||||
var r = null
|
||||
for (i = 0; i < iterations; i++) {
|
||||
r = {x: i, y: i + 1, z: i + 2}
|
||||
}
|
||||
return r.z
|
||||
}
|
||||
|
||||
// 2. Property read in loop
|
||||
function bench_prop_read() {
|
||||
var obj = {x: 10, y: 20, z: 30, w: 40}
|
||||
var i = 0
|
||||
var s = 0
|
||||
for (i = 0; i < iterations; i++) {
|
||||
s = s + obj.x + obj.y + obj.z + obj.w
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 3. Dynamic property access (computed keys)
|
||||
function bench_dynamic_prop() {
|
||||
var obj = {a: 1, b: 2, c: 3, d: 4, e: 5}
|
||||
var keys = ["a", "b", "c", "d", "e"]
|
||||
var i = 0
|
||||
var j = 0
|
||||
var s = 0
|
||||
for (i = 0; i < iterations; i++) {
|
||||
for (j = 0; j < 5; j++) {
|
||||
s = s + obj[keys[j]]
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 4. String concatenation
|
||||
function bench_string_concat() {
|
||||
var i = 0
|
||||
var s = ""
|
||||
def n = 10000
|
||||
for (i = 0; i < n; i++) {
|
||||
s = s + "x"
|
||||
}
|
||||
return length(s)
|
||||
}
|
||||
|
||||
// 5. String interpolation
|
||||
function bench_interpolation() {
|
||||
var i = 0
|
||||
var s = ""
|
||||
def n = 50000
|
||||
for (i = 0; i < n; i++) {
|
||||
s = `item_${i}`
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 6. Prototype chain / method-like call
|
||||
function make_point(x, y) {
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
sum: function(self) {
|
||||
return self.x + self.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bench_method_call() {
|
||||
var p = make_point(3, 4)
|
||||
var i = 0
|
||||
var s = 0
|
||||
for (i = 0; i < iterations; i++) {
|
||||
s = s + p.sum(p)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 7. Function call overhead (simple recursion depth)
|
||||
function fib(n) {
|
||||
if (n <= 1) return n
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
}
|
||||
|
||||
function bench_fncall() {
|
||||
var i = 0
|
||||
var s = 0
|
||||
for (i = 0; i < 20; i++) {
|
||||
s = s + fib(25)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
function run(name, fn) {
|
||||
var start = time.number()
|
||||
var result = fn()
|
||||
var elapsed = time.number() - start
|
||||
var ms = whole(elapsed * 100000) / 100
|
||||
log.console(` ${name}: ${ms} ms (result: ${result})`)
|
||||
}
|
||||
|
||||
log.console("=== Object / String / Call Benchmark ===")
|
||||
log.console(` iterations: ${iterations}`)
|
||||
run("record_create ", bench_record_create)
|
||||
run("prop_read ", bench_prop_read)
|
||||
run("dynamic_prop ", bench_dynamic_prop)
|
||||
run("string_concat ", bench_string_concat)
|
||||
run("interpolation ", bench_interpolation)
|
||||
run("method_call ", bench_method_call)
|
||||
run("fncall_fib25 ", bench_fncall)
|
||||
99
bench_object.js
Normal file
99
bench_object.js
Normal file
@@ -0,0 +1,99 @@
|
||||
// bench_object.js — object/string/call benchmark (QuickJS)
|
||||
|
||||
const iterations = 200000;
|
||||
|
||||
function bench_record_create() {
|
||||
let r;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
r = {x: i, y: i + 1, z: i + 2};
|
||||
}
|
||||
return r.z;
|
||||
}
|
||||
|
||||
function bench_prop_read() {
|
||||
const obj = {x: 10, y: 20, z: 30, w: 40};
|
||||
let s = 0;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
s = s + obj.x + obj.y + obj.z + obj.w;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function bench_dynamic_prop() {
|
||||
const obj = {a: 1, b: 2, c: 3, d: 4, e: 5};
|
||||
const keys = ["a", "b", "c", "d", "e"];
|
||||
let s = 0;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
for (let j = 0; j < 5; j++) {
|
||||
s = s + obj[keys[j]];
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function bench_string_concat() {
|
||||
let s = "";
|
||||
const n = 10000;
|
||||
for (let i = 0; i < n; i++) {
|
||||
s = s + "x";
|
||||
}
|
||||
return s.length;
|
||||
}
|
||||
|
||||
function bench_interpolation() {
|
||||
let s = "";
|
||||
const n = 50000;
|
||||
for (let i = 0; i < n; i++) {
|
||||
s = `item_${i}`;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function make_point(x, y) {
|
||||
return {
|
||||
x: x,
|
||||
y: y,
|
||||
sum: function(self) {
|
||||
return self.x + self.y;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function bench_method_call() {
|
||||
const p = make_point(3, 4);
|
||||
let s = 0;
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
s = s + p.sum(p);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function fib(n) {
|
||||
if (n <= 1) return n;
|
||||
return fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
function bench_fncall() {
|
||||
let s = 0;
|
||||
for (let i = 0; i < 20; i++) {
|
||||
s = s + fib(25);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function run(name, fn) {
|
||||
const start = performance.now();
|
||||
const result = fn();
|
||||
const elapsed = performance.now() - start;
|
||||
console.log(` ${name}: ${elapsed.toFixed(2)} ms (result: ${result})`);
|
||||
}
|
||||
|
||||
console.log("=== Object / String / Call Benchmark ===");
|
||||
console.log(` iterations: ${iterations}`);
|
||||
run("record_create ", bench_record_create);
|
||||
run("prop_read ", bench_prop_read);
|
||||
run("dynamic_prop ", bench_dynamic_prop);
|
||||
run("string_concat ", bench_string_concat);
|
||||
run("interpolation ", bench_interpolation);
|
||||
run("method_call ", bench_method_call);
|
||||
run("fncall_fib25 ", bench_fncall);
|
||||
101
bench_object.lua
Normal file
101
bench_object.lua
Normal file
@@ -0,0 +1,101 @@
|
||||
-- bench_object.lua — object/string/call benchmark (Lua)
|
||||
|
||||
local iterations = 200000
|
||||
local clock = os.clock
|
||||
|
||||
local function bench_record_create()
|
||||
local r
|
||||
for i = 0, iterations - 1 do
|
||||
r = {x = i, y = i + 1, z = i + 2}
|
||||
end
|
||||
return r.z
|
||||
end
|
||||
|
||||
local function bench_prop_read()
|
||||
local obj = {x = 10, y = 20, z = 30, w = 40}
|
||||
local s = 0
|
||||
for i = 0, iterations - 1 do
|
||||
s = s + obj.x + obj.y + obj.z + obj.w
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function bench_dynamic_prop()
|
||||
local obj = {a = 1, b = 2, c = 3, d = 4, e = 5}
|
||||
local keys = {"a", "b", "c", "d", "e"}
|
||||
local s = 0
|
||||
for i = 0, iterations - 1 do
|
||||
for j = 1, 5 do
|
||||
s = s + obj[keys[j]]
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function bench_string_concat()
|
||||
local parts = {}
|
||||
local n = 10000
|
||||
for i = 1, n do
|
||||
parts[i] = "x"
|
||||
end
|
||||
local s = table.concat(parts)
|
||||
return #s
|
||||
end
|
||||
|
||||
local function bench_interpolation()
|
||||
local s = ""
|
||||
local n = 50000
|
||||
for i = 0, n - 1 do
|
||||
s = string.format("item_%d", i)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function make_point(x, y)
|
||||
return {
|
||||
x = x,
|
||||
y = y,
|
||||
sum = function(self)
|
||||
return self.x + self.y
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
local function bench_method_call()
|
||||
local p = make_point(3, 4)
|
||||
local s = 0
|
||||
for i = 0, iterations - 1 do
|
||||
s = s + p.sum(p)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function fib(n)
|
||||
if n <= 1 then return n end
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
end
|
||||
|
||||
local function bench_fncall()
|
||||
local s = 0
|
||||
for i = 0, 19 do
|
||||
s = s + fib(25)
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
local function run(name, fn)
|
||||
local start = clock()
|
||||
local result = fn()
|
||||
local elapsed = (clock() - start) * 1000
|
||||
print(string.format(" %s: %.2f ms (result: %s)", name, elapsed, tostring(result)))
|
||||
end
|
||||
|
||||
print("=== Object / String / Call Benchmark ===")
|
||||
print(string.format(" iterations: %d", iterations))
|
||||
run("record_create ", bench_record_create)
|
||||
run("prop_read ", bench_prop_read)
|
||||
run("dynamic_prop ", bench_dynamic_prop)
|
||||
run("string_concat ", bench_string_concat)
|
||||
run("interpolation ", bench_interpolation)
|
||||
run("method_call ", bench_method_call)
|
||||
run("fncall_fib25 ", bench_fncall)
|
||||
184
mcode.cm
184
mcode.cm
@@ -38,6 +38,11 @@ var mcode = function(ast) {
|
||||
is_array: "is_array", is_function: "is_func", is_object: "is_record",
|
||||
is_stone: "is_stone", is_integer: "is_int", is_text: "is_text",
|
||||
is_number: "is_num", is_logical: "is_bool", is_null: "is_null",
|
||||
is_blob: "is_blob", is_data: "is_data",
|
||||
is_true: "is_true", is_false: "is_false", is_fit: "is_fit",
|
||||
is_character: "is_char", is_digit: "is_digit", is_letter: "is_letter",
|
||||
is_lower: "is_lower", is_upper: "is_upper", is_whitespace: "is_ws",
|
||||
is_actor: "is_actor",
|
||||
length: "length"
|
||||
}
|
||||
|
||||
@@ -89,6 +94,8 @@ var mcode = function(ast) {
|
||||
var s_filename = null
|
||||
var s_has_disruption = false
|
||||
var s_slot_types = {}
|
||||
var s_num_err_label = null
|
||||
var s_num_err_emitted = false
|
||||
|
||||
// Shared closure vars for binop helpers (avoids >4 param functions)
|
||||
var _bp_dest = 0
|
||||
@@ -118,7 +125,9 @@ var mcode = function(ast) {
|
||||
cur_line: s_cur_line,
|
||||
cur_col: s_cur_col,
|
||||
has_disruption: s_has_disruption,
|
||||
slot_types: s_slot_types
|
||||
slot_types: s_slot_types,
|
||||
num_err_label: s_num_err_label,
|
||||
num_err_emitted: s_num_err_emitted
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +150,8 @@ var mcode = function(ast) {
|
||||
s_cur_col = saved.cur_col
|
||||
s_has_disruption = saved.has_disruption
|
||||
s_slot_types = saved.slot_types
|
||||
s_num_err_label = saved.num_err_label
|
||||
s_num_err_emitted = saved.num_err_emitted
|
||||
}
|
||||
|
||||
// Slot allocation
|
||||
@@ -413,29 +424,37 @@ var mcode = function(ast) {
|
||||
var emit_numeric_binop = function(op_str) {
|
||||
var left_known = is_known_number(_bp_ln) || slot_is_num(_bp_left)
|
||||
var right_known = is_known_number(_bp_rn) || slot_is_num(_bp_right)
|
||||
var t0 = null
|
||||
var done = null
|
||||
if (left_known && right_known) {
|
||||
emit_3(op_str, _bp_dest, _bp_left, _bp_right)
|
||||
mark_slot(_bp_dest, "num")
|
||||
return null
|
||||
}
|
||||
var t0 = alloc_slot()
|
||||
var err = gen_label("num_err")
|
||||
var done = gen_label("num_done")
|
||||
if (s_num_err_label == null) {
|
||||
s_num_err_label = gen_label("num_err")
|
||||
}
|
||||
t0 = alloc_slot()
|
||||
if (!left_known) {
|
||||
emit_2("is_num", t0, _bp_left)
|
||||
emit_jump_cond("jump_false", t0, err)
|
||||
emit_jump_cond("jump_false", t0, s_num_err_label)
|
||||
mark_slot(_bp_left, "num")
|
||||
}
|
||||
if (!right_known) {
|
||||
emit_2("is_num", t0, _bp_right)
|
||||
emit_jump_cond("jump_false", t0, err)
|
||||
emit_jump_cond("jump_false", t0, s_num_err_label)
|
||||
mark_slot(_bp_right, "num")
|
||||
}
|
||||
emit_3(op_str, _bp_dest, _bp_left, _bp_right)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(err)
|
||||
emit_log_error("cannot apply '" + _bp_op_sym + "': operands must be numbers")
|
||||
emit_0("disrupt")
|
||||
emit_label(done)
|
||||
if (!s_num_err_emitted) {
|
||||
done = gen_label("num_done")
|
||||
emit_jump(done)
|
||||
emit_label(s_num_err_label)
|
||||
emit_log_error("operands must be numbers")
|
||||
emit_0("disrupt")
|
||||
emit_label(done)
|
||||
s_num_err_emitted = true
|
||||
}
|
||||
mark_slot(_bp_dest, "num")
|
||||
return null
|
||||
}
|
||||
@@ -460,23 +479,30 @@ var mcode = function(ast) {
|
||||
|
||||
// emit_neg_decomposed: emit type-guarded negate
|
||||
var emit_neg_decomposed = function(dest, src, src_node) {
|
||||
var t0 = null
|
||||
var done = null
|
||||
if (is_known_number(src_node) || slot_is_num(src)) {
|
||||
emit_2("negate", dest, src)
|
||||
mark_slot(dest, "num")
|
||||
return null
|
||||
}
|
||||
var t0 = alloc_slot()
|
||||
var err = gen_label("neg_err")
|
||||
var done = gen_label("neg_done")
|
||||
if (s_num_err_label == null) {
|
||||
s_num_err_label = gen_label("num_err")
|
||||
}
|
||||
t0 = alloc_slot()
|
||||
emit_2("is_num", t0, src)
|
||||
emit_jump_cond("jump_false", t0, err)
|
||||
emit_jump_cond("jump_false", t0, s_num_err_label)
|
||||
mark_slot(src, "num")
|
||||
emit_2("negate", dest, src)
|
||||
emit_jump(done)
|
||||
|
||||
emit_label(err)
|
||||
emit_log_error("cannot negate: operand must be a number")
|
||||
emit_0("disrupt")
|
||||
emit_label(done)
|
||||
if (!s_num_err_emitted) {
|
||||
done = gen_label("num_done")
|
||||
emit_jump(done)
|
||||
emit_label(s_num_err_label)
|
||||
emit_log_error("operands must be numbers")
|
||||
emit_0("disrupt")
|
||||
emit_label(done)
|
||||
s_num_err_emitted = true
|
||||
}
|
||||
mark_slot(dest, "num")
|
||||
return null
|
||||
}
|
||||
@@ -852,6 +878,7 @@ var mcode = function(ast) {
|
||||
var inline_every = true
|
||||
var inline_some = true
|
||||
var inline_reduce = true
|
||||
var inline_map = true
|
||||
|
||||
// --- Helper: emit a reduce loop body ---
|
||||
// r = {acc, i, arr, fn, len, fn_arity}; emits loop updating acc in-place.
|
||||
@@ -1141,6 +1168,96 @@ var mcode = function(ast) {
|
||||
return dest
|
||||
}
|
||||
|
||||
// --- Inline expansion: array(arr, fn) → map ---
|
||||
var expand_inline_map = function(dest, arr_slot, fn_slot) {
|
||||
var result = alloc_slot()
|
||||
var len = alloc_slot()
|
||||
var i = alloc_slot()
|
||||
var check = alloc_slot()
|
||||
var item = alloc_slot()
|
||||
var fn_arity = alloc_slot()
|
||||
var arity_is_zero = alloc_slot()
|
||||
var arity_is_one = alloc_slot()
|
||||
var null_s = alloc_slot()
|
||||
var zero = alloc_slot()
|
||||
var one = alloc_slot()
|
||||
var f = alloc_slot()
|
||||
var val = alloc_slot()
|
||||
var loop_label = gen_label("map_loop")
|
||||
var call_one_label = gen_label("map_call_one")
|
||||
var call_two_label = gen_label("map_call_two")
|
||||
var call_done_label = gen_label("map_call_done")
|
||||
var done_label = gen_label("map_done")
|
||||
add_instr(["array", result, 0])
|
||||
emit_2("length", len, arr_slot)
|
||||
emit_2("int", i, 0)
|
||||
emit_2("int", zero, 0)
|
||||
emit_2("int", one, 1)
|
||||
emit_1("null", null_s)
|
||||
emit_2("length", fn_arity, fn_slot)
|
||||
emit_label(loop_label)
|
||||
emit_3("lt", check, i, len)
|
||||
emit_jump_cond("jump_false", check, done_label)
|
||||
emit_3("load_index", item, arr_slot, i)
|
||||
emit_3("eq", arity_is_zero, fn_arity, zero)
|
||||
emit_jump_cond("jump_false", arity_is_zero, call_one_label)
|
||||
emit_3("frame", f, fn_slot, 0)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_2("invoke", f, val)
|
||||
emit_jump(call_done_label)
|
||||
emit_label(call_one_label)
|
||||
emit_3("eq", arity_is_one, fn_arity, one)
|
||||
emit_jump_cond("jump_false", arity_is_one, call_two_label)
|
||||
emit_3("frame", f, fn_slot, 1)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_3("setarg", f, 1, item)
|
||||
emit_2("invoke", f, val)
|
||||
emit_jump(call_done_label)
|
||||
emit_label(call_two_label)
|
||||
emit_3("frame", f, fn_slot, 2)
|
||||
emit_3("setarg", f, 0, null_s)
|
||||
emit_3("setarg", f, 1, item)
|
||||
emit_3("setarg", f, 2, i)
|
||||
emit_2("invoke", f, val)
|
||||
emit_label(call_done_label)
|
||||
emit_2("push", result, val)
|
||||
emit_3("add", i, i, one)
|
||||
emit_jump(loop_label)
|
||||
emit_label(done_label)
|
||||
emit_2("move", dest, result)
|
||||
return dest
|
||||
}
|
||||
|
||||
// --- Inline expansion: array(arr, intrinsic_op) → map with direct opcode ---
|
||||
var expand_inline_map_intrinsic = function(dest, arr_slot, opcode) {
|
||||
var result = alloc_slot()
|
||||
var len = alloc_slot()
|
||||
var i = alloc_slot()
|
||||
var check = alloc_slot()
|
||||
var item = alloc_slot()
|
||||
var val = alloc_slot()
|
||||
var zero = alloc_slot()
|
||||
var one = alloc_slot()
|
||||
var loop_label = gen_label("mapi_loop")
|
||||
var done_label = gen_label("mapi_done")
|
||||
add_instr(["array", result, 0])
|
||||
emit_2("length", len, arr_slot)
|
||||
emit_2("int", i, 0)
|
||||
emit_2("int", zero, 0)
|
||||
emit_2("int", one, 1)
|
||||
emit_label(loop_label)
|
||||
emit_3("lt", check, i, len)
|
||||
emit_jump_cond("jump_false", check, done_label)
|
||||
emit_3("load_index", item, arr_slot, i)
|
||||
emit_2(opcode, val, item)
|
||||
emit_2("push", result, val)
|
||||
emit_3("add", i, i, one)
|
||||
emit_jump(loop_label)
|
||||
emit_label(done_label)
|
||||
emit_2("move", dest, result)
|
||||
return dest
|
||||
}
|
||||
|
||||
// --- Inline expansion: reduce(arr, fn[, initial[, reverse]]) ---
|
||||
var expand_inline_reduce = function(dest, args, nargs) {
|
||||
var arr_slot = args.arr
|
||||
@@ -1908,6 +2025,25 @@ var mcode = function(ast) {
|
||||
d = alloc_slot()
|
||||
return expand_inline_reduce(d, {arr: a0, fn: a1, init: a2, rev: a3}, nargs)
|
||||
}
|
||||
// array(arr, fn) → inline map expansion
|
||||
// Skip when first arg is a number literal (that's array(N, fn) — creation, not map)
|
||||
if (nargs == 2 && fname == "array" && inline_map
|
||||
&& args_list[0].kind != "number") {
|
||||
// Specialized: array(arr, known_sensory_intrinsic) → direct opcode loop
|
||||
if (args_list[1].kind == "name" && args_list[1].intrinsic == true
|
||||
&& sensory_ops[args_list[1].name] != null) {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
d = alloc_slot()
|
||||
return expand_inline_map_intrinsic(d, a0, sensory_ops[args_list[1].name])
|
||||
}
|
||||
// General: array(arr, fn_literal) → map loop with arity dispatch
|
||||
if (args_list[1].kind == "function") {
|
||||
a0 = gen_expr(args_list[0], -1)
|
||||
a1 = gen_expr(args_list[1], -1)
|
||||
d = alloc_slot()
|
||||
return expand_inline_map(d, a0, a1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect arg slots
|
||||
@@ -2605,6 +2741,8 @@ var mcode = function(ast) {
|
||||
s_vars = []
|
||||
s_intrinsic_cache = []
|
||||
s_slot_types = {}
|
||||
s_num_err_label = null
|
||||
s_num_err_emitted = false
|
||||
s_loop_break = null
|
||||
s_loop_continue = null
|
||||
s_label_map = {}
|
||||
@@ -2804,6 +2942,8 @@ var mcode = function(ast) {
|
||||
s_label_counter = 0
|
||||
s_func_counter = 0
|
||||
s_slot_types = {}
|
||||
s_num_err_label = null
|
||||
s_num_err_emitted = false
|
||||
s_loop_break = null
|
||||
s_loop_continue = null
|
||||
s_label_map = {}
|
||||
|
||||
159
source/mach.c
159
source/mach.c
@@ -269,6 +269,18 @@ typedef enum MachOpcode {
|
||||
MACH_IS_STONE, /* R(A) = is_stone(R(B)) */
|
||||
MACH_LENGTH, /* R(A) = length(R(B)) — array/text/blob length */
|
||||
MACH_IS_PROXY, /* R(A) = is_function(R(B)) && R(B).length == 2 */
|
||||
MACH_IS_BLOB, /* R(A) = is_blob(R(B)) */
|
||||
MACH_IS_DATA, /* R(A) = is_data(R(B)) — plain record, not array/func/blob */
|
||||
MACH_IS_TRUE, /* R(A) = (R(B) === true) */
|
||||
MACH_IS_FALSE, /* R(A) = (R(B) === false) */
|
||||
MACH_IS_FIT, /* R(A) = is_fit(R(B)) — safe integer */
|
||||
MACH_IS_CHAR, /* R(A) = is_character(R(B)) — single char text */
|
||||
MACH_IS_DIGIT, /* R(A) = is_digit(R(B)) */
|
||||
MACH_IS_LETTER, /* R(A) = is_letter(R(B)) */
|
||||
MACH_IS_LOWER, /* R(A) = is_lower(R(B)) */
|
||||
MACH_IS_UPPER, /* R(A) = is_upper(R(B)) */
|
||||
MACH_IS_WS, /* R(A) = is_whitespace(R(B)) */
|
||||
MACH_IS_ACTOR, /* R(A) = is_actor(R(B)) — has actor_sym property */
|
||||
|
||||
MACH_OP_COUNT
|
||||
} MachOpcode;
|
||||
@@ -381,6 +393,18 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
|
||||
[MACH_IS_STONE] = "is_stone",
|
||||
[MACH_LENGTH] = "length",
|
||||
[MACH_IS_PROXY] = "is_proxy",
|
||||
[MACH_IS_BLOB] = "is_blob",
|
||||
[MACH_IS_DATA] = "is_data",
|
||||
[MACH_IS_TRUE] = "is_true",
|
||||
[MACH_IS_FALSE] = "is_false",
|
||||
[MACH_IS_FIT] = "is_fit",
|
||||
[MACH_IS_CHAR] = "is_char",
|
||||
[MACH_IS_DIGIT] = "is_digit",
|
||||
[MACH_IS_LETTER] = "is_letter",
|
||||
[MACH_IS_LOWER] = "is_lower",
|
||||
[MACH_IS_UPPER] = "is_upper",
|
||||
[MACH_IS_WS] = "is_ws",
|
||||
[MACH_IS_ACTOR] = "is_actor",
|
||||
};
|
||||
|
||||
/* ---- Compile-time constant pool entry ---- */
|
||||
@@ -1396,6 +1420,12 @@ vm_dispatch:
|
||||
DT(MACH_IS_ARRAY), DT(MACH_IS_FUNC),
|
||||
DT(MACH_IS_RECORD), DT(MACH_IS_STONE),
|
||||
DT(MACH_LENGTH), DT(MACH_IS_PROXY),
|
||||
DT(MACH_IS_BLOB), DT(MACH_IS_DATA),
|
||||
DT(MACH_IS_TRUE), DT(MACH_IS_FALSE),
|
||||
DT(MACH_IS_FIT), DT(MACH_IS_CHAR),
|
||||
DT(MACH_IS_DIGIT), DT(MACH_IS_LETTER),
|
||||
DT(MACH_IS_LOWER), DT(MACH_IS_UPPER),
|
||||
DT(MACH_IS_WS), DT(MACH_IS_ACTOR),
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
#undef DT
|
||||
@@ -2386,6 +2416,123 @@ vm_dispatch:
|
||||
frame->slots[a] = JS_NewBool(ctx, is_proxy);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_BLOB):
|
||||
frame->slots[a] = JS_NewBool(ctx, mist_is_blob(frame->slots[b]));
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_IS_DATA): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (mist_is_gc_object(v) && !mist_is_array(v)
|
||||
&& !mist_is_function(v) && !mist_is_blob(v))
|
||||
result = 1;
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_TRUE):
|
||||
frame->slots[a] = JS_NewBool(ctx, frame->slots[b] == JS_TRUE);
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_IS_FALSE):
|
||||
frame->slots[a] = JS_NewBool(ctx, frame->slots[b] == JS_FALSE);
|
||||
VM_BREAK();
|
||||
VM_CASE(MACH_IS_FIT): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (JS_IsInt(v)) {
|
||||
result = 1;
|
||||
} else if (JS_IsShortFloat(v)) {
|
||||
double d = JS_VALUE_GET_FLOAT64(v);
|
||||
result = (isfinite(d) && trunc(d) == d && fabs(d) <= 9007199254740992.0);
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_CHAR): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v))
|
||||
result = (MIST_GetImmediateASCIILen(v) == 1);
|
||||
else if (mist_is_text(v))
|
||||
result = (js_string_value_len(v) == 1);
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_DIGIT): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch >= '0' && ch <= '9');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch >= '0' && ch <= '9');
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_LETTER): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_LOWER): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch >= 'a' && ch <= 'z');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch >= 'a' && ch <= 'z');
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_UPPER): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch >= 'A' && ch <= 'Z');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch >= 'A' && ch <= 'Z');
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_WS): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v');
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
VM_CASE(MACH_IS_ACTOR): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (mist_is_record(v) && !JS_IsNull(ctx->actor_sym)) {
|
||||
result = JS_HasPropertyKey(ctx, v, ctx->actor_sym) > 0;
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
}
|
||||
/* Logical */
|
||||
VM_CASE(MACH_NOT): {
|
||||
int bval = JS_ToBool(ctx, frame->slots[b]);
|
||||
@@ -3031,6 +3178,18 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
|
||||
else if (strcmp(op, "is_stone") == 0) { AB2(MACH_IS_STONE); }
|
||||
else if (strcmp(op, "length") == 0) { AB2(MACH_LENGTH); }
|
||||
else if (strcmp(op, "is_proxy") == 0) { AB2(MACH_IS_PROXY); }
|
||||
else if (strcmp(op, "is_blob") == 0) { AB2(MACH_IS_BLOB); }
|
||||
else if (strcmp(op, "is_data") == 0) { AB2(MACH_IS_DATA); }
|
||||
else if (strcmp(op, "is_true") == 0) { AB2(MACH_IS_TRUE); }
|
||||
else if (strcmp(op, "is_false") == 0) { AB2(MACH_IS_FALSE); }
|
||||
else if (strcmp(op, "is_fit") == 0) { AB2(MACH_IS_FIT); }
|
||||
else if (strcmp(op, "is_char") == 0) { AB2(MACH_IS_CHAR); }
|
||||
else if (strcmp(op, "is_digit") == 0) { AB2(MACH_IS_DIGIT); }
|
||||
else if (strcmp(op, "is_letter") == 0) { AB2(MACH_IS_LETTER); }
|
||||
else if (strcmp(op, "is_lower") == 0) { AB2(MACH_IS_LOWER); }
|
||||
else if (strcmp(op, "is_upper") == 0) { AB2(MACH_IS_UPPER); }
|
||||
else if (strcmp(op, "is_ws") == 0) { AB2(MACH_IS_WS); }
|
||||
else if (strcmp(op, "is_actor") == 0) { AB2(MACH_IS_ACTOR); }
|
||||
/* Logical */
|
||||
else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); }
|
||||
else if (strcmp(op, "and") == 0) { ABC3(MACH_AND); }
|
||||
|
||||
@@ -11405,6 +11405,79 @@ static JSValue js_cell_is_letter (JSContext *ctx, JSValue this_val, int argc, JS
|
||||
return JS_NewBool (ctx, (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
|
||||
}
|
||||
|
||||
/* is_true(val) - check if value is exactly true */
|
||||
static JSValue js_cell_is_true (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
return JS_NewBool (ctx, argv[0] == JS_TRUE);
|
||||
}
|
||||
|
||||
/* is_false(val) - check if value is exactly false */
|
||||
static JSValue js_cell_is_false (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
return JS_NewBool (ctx, argv[0] == JS_FALSE);
|
||||
}
|
||||
|
||||
/* is_fit(val) - check if value is a safe integer (int32 or float with integer value <= 2^53) */
|
||||
static JSValue js_cell_is_fit (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
JSValue val = argv[0];
|
||||
if (JS_IsInt (val)) return JS_TRUE;
|
||||
if (JS_IsShortFloat (val)) {
|
||||
double d = JS_VALUE_GET_FLOAT64 (val);
|
||||
return JS_NewBool (ctx, isfinite (d) && trunc (d) == d && fabs (d) <= 9007199254740992.0);
|
||||
}
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* is_character(val) - check if value is a single character text */
|
||||
static JSValue js_cell_is_character (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
JSValue val = argv[0];
|
||||
if (!JS_IsText (val)) return JS_FALSE;
|
||||
return JS_NewBool (ctx, js_string_value_len (val) == 1);
|
||||
}
|
||||
|
||||
/* is_digit(val) - check if value is a single digit character */
|
||||
static JSValue js_cell_is_digit (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
JSValue val = argv[0];
|
||||
if (!JS_IsText (val)) return JS_FALSE;
|
||||
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||
uint32_t c = js_string_value_get (val, 0);
|
||||
return JS_NewBool (ctx, c >= '0' && c <= '9');
|
||||
}
|
||||
|
||||
/* is_lower(val) - check if value is a single lowercase letter */
|
||||
static JSValue js_cell_is_lower (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
JSValue val = argv[0];
|
||||
if (!JS_IsText (val)) return JS_FALSE;
|
||||
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||
uint32_t c = js_string_value_get (val, 0);
|
||||
return JS_NewBool (ctx, c >= 'a' && c <= 'z');
|
||||
}
|
||||
|
||||
/* is_upper(val) - check if value is a single uppercase letter */
|
||||
static JSValue js_cell_is_upper (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
JSValue val = argv[0];
|
||||
if (!JS_IsText (val)) return JS_FALSE;
|
||||
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||
uint32_t c = js_string_value_get (val, 0);
|
||||
return JS_NewBool (ctx, c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
/* is_whitespace(val) - check if value is a single whitespace character */
|
||||
static JSValue js_cell_is_whitespace (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
JSValue val = argv[0];
|
||||
if (!JS_IsText (val)) return JS_FALSE;
|
||||
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||
uint32_t c = js_string_value_get (val, 0);
|
||||
return JS_NewBool (ctx, c == ' ' || c == '\t' || c == '\n'
|
||||
|| c == '\r' || c == '\f' || c == '\v');
|
||||
}
|
||||
|
||||
/* is_proto(val, master) - check if val has master in prototype chain */
|
||||
static JSValue js_cell_is_proto (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2) return JS_FALSE;
|
||||
@@ -11572,6 +11645,14 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
|
||||
js_set_global_cfunc(ctx, "is_text", js_cell_is_text, 1);
|
||||
js_set_global_cfunc(ctx, "is_proto", js_cell_is_proto, 2);
|
||||
js_set_global_cfunc(ctx, "is_letter", js_cell_is_letter, 1);
|
||||
js_set_global_cfunc(ctx, "is_true", js_cell_is_true, 1);
|
||||
js_set_global_cfunc(ctx, "is_false", js_cell_is_false, 1);
|
||||
js_set_global_cfunc(ctx, "is_fit", js_cell_is_fit, 1);
|
||||
js_set_global_cfunc(ctx, "is_character", js_cell_is_character, 1);
|
||||
js_set_global_cfunc(ctx, "is_digit", js_cell_is_digit, 1);
|
||||
js_set_global_cfunc(ctx, "is_lower", js_cell_is_lower, 1);
|
||||
js_set_global_cfunc(ctx, "is_upper", js_cell_is_upper, 1);
|
||||
js_set_global_cfunc(ctx, "is_whitespace", js_cell_is_whitespace, 1);
|
||||
|
||||
/* Utility functions */
|
||||
js_set_global_cfunc(ctx, "apply", js_cell_fn_apply, 2);
|
||||
|
||||
@@ -46,13 +46,17 @@ var streamline = function(ir, log) {
|
||||
not: true, and: true, or: true,
|
||||
is_int: true, is_text: true, is_num: true,
|
||||
is_bool: true, is_null: true, is_identical: true,
|
||||
is_array: true, is_func: true, is_record: true, is_stone: true
|
||||
is_array: true, is_func: true, is_record: true, is_stone: true,
|
||||
is_blob: true, is_data: true,
|
||||
is_true: true, is_false: true, is_fit: true,
|
||||
is_char: true, is_digit: true, is_letter: true,
|
||||
is_lower: true, is_upper: true, is_ws: true, is_actor: true
|
||||
}
|
||||
var type_check_map = {
|
||||
is_int: T_INT, is_text: T_TEXT, is_num: T_NUM,
|
||||
is_bool: T_BOOL, is_null: T_NULL,
|
||||
is_array: T_ARRAY, is_func: T_FUNCTION,
|
||||
is_record: T_RECORD
|
||||
is_record: T_RECORD, is_blob: T_BLOB
|
||||
}
|
||||
|
||||
// simplify_algebra dispatch tables
|
||||
@@ -374,7 +378,12 @@ var streamline = function(ir, log) {
|
||||
is_int: [1, T_BOOL], is_text: [1, T_BOOL], is_num: [1, T_BOOL],
|
||||
is_bool: [1, T_BOOL], is_null: [1, T_BOOL], is_identical: [1, T_BOOL],
|
||||
is_array: [1, T_BOOL], is_func: [1, T_BOOL],
|
||||
is_record: [1, T_BOOL], is_stone: [1, T_BOOL]
|
||||
is_record: [1, T_BOOL], is_stone: [1, T_BOOL],
|
||||
is_blob: [1, T_BOOL], is_data: [1, T_BOOL],
|
||||
is_true: [1, T_BOOL], is_false: [1, T_BOOL], is_fit: [1, T_BOOL],
|
||||
is_char: [1, T_BOOL], is_digit: [1, T_BOOL], is_letter: [1, T_BOOL],
|
||||
is_lower: [1, T_BOOL], is_upper: [1, T_BOOL], is_ws: [1, T_BOOL],
|
||||
is_actor: [1, T_BOOL]
|
||||
}
|
||||
|
||||
// Known intrinsic return types for invoke result inference.
|
||||
|
||||
146
vm_suite.ce
146
vm_suite.ce
@@ -981,6 +981,99 @@ run("is_proto", function() {
|
||||
if (!is_proto(b, a)) fail("is_proto failed on meme")
|
||||
})
|
||||
|
||||
run("is_data", function() {
|
||||
if (!is_data({})) fail("is_data {} should be true")
|
||||
if (is_data([])) fail("is_data [] should be false")
|
||||
if (is_data(42)) fail("is_data number should be false")
|
||||
if (is_data("hello")) fail("is_data string should be false")
|
||||
if (is_data(null)) fail("is_data null should be false")
|
||||
if (is_data(true)) fail("is_data bool should be false")
|
||||
if (is_data(function(){})) fail("is_data function should be false")
|
||||
})
|
||||
|
||||
run("is_true", function() {
|
||||
if (!is_true(true)) fail("is_true true should be true")
|
||||
if (is_true(false)) fail("is_true false should be false")
|
||||
if (is_true(1)) fail("is_true 1 should be false")
|
||||
if (is_true("true")) fail("is_true string should be false")
|
||||
if (is_true(null)) fail("is_true null should be false")
|
||||
})
|
||||
|
||||
run("is_false", function() {
|
||||
if (!is_false(false)) fail("is_false false should be true")
|
||||
if (is_false(true)) fail("is_false true should be false")
|
||||
if (is_false(0)) fail("is_false 0 should be false")
|
||||
if (is_false("")) fail("is_false empty string should be false")
|
||||
if (is_false(null)) fail("is_false null should be false")
|
||||
})
|
||||
|
||||
run("is_fit", function() {
|
||||
if (!is_fit(0)) fail("is_fit 0 should be true")
|
||||
if (!is_fit(42)) fail("is_fit 42 should be true")
|
||||
if (!is_fit(-100)) fail("is_fit -100 should be true")
|
||||
if (!is_fit(3.0)) fail("is_fit 3.0 should be true")
|
||||
if (is_fit(3.5)) fail("is_fit 3.5 should be false")
|
||||
if (is_fit("42")) fail("is_fit string should be false")
|
||||
if (is_fit(null)) fail("is_fit null should be false")
|
||||
})
|
||||
|
||||
run("is_character", function() {
|
||||
if (!is_character("a")) fail("is_character a should be true")
|
||||
if (!is_character("Z")) fail("is_character Z should be true")
|
||||
if (!is_character("5")) fail("is_character 5 should be true")
|
||||
if (!is_character(" ")) fail("is_character space should be true")
|
||||
if (is_character("ab")) fail("is_character ab should be false")
|
||||
if (is_character("")) fail("is_character empty should be false")
|
||||
if (is_character(42)) fail("is_character number should be false")
|
||||
if (is_character(null)) fail("is_character null should be false")
|
||||
})
|
||||
|
||||
run("is_digit", function() {
|
||||
if (!is_digit("0")) fail("is_digit 0 should be true")
|
||||
if (!is_digit("5")) fail("is_digit 5 should be true")
|
||||
if (!is_digit("9")) fail("is_digit 9 should be true")
|
||||
if (is_digit("a")) fail("is_digit a should be false")
|
||||
if (is_digit("55")) fail("is_digit 55 should be false")
|
||||
if (is_digit(5)) fail("is_digit number should be false")
|
||||
if (is_digit(null)) fail("is_digit null should be false")
|
||||
})
|
||||
|
||||
run("is_letter", function() {
|
||||
if (!is_letter("a")) fail("is_letter a should be true")
|
||||
if (!is_letter("Z")) fail("is_letter Z should be true")
|
||||
if (is_letter("5")) fail("is_letter 5 should be false")
|
||||
if (is_letter("ab")) fail("is_letter ab should be false")
|
||||
if (is_letter(42)) fail("is_letter number should be false")
|
||||
})
|
||||
|
||||
run("is_lower", function() {
|
||||
if (!is_lower("a")) fail("is_lower a should be true")
|
||||
if (!is_lower("z")) fail("is_lower z should be true")
|
||||
if (is_lower("A")) fail("is_lower A should be false")
|
||||
if (is_lower("5")) fail("is_lower 5 should be false")
|
||||
if (is_lower("ab")) fail("is_lower ab should be false")
|
||||
if (is_lower(42)) fail("is_lower number should be false")
|
||||
})
|
||||
|
||||
run("is_upper", function() {
|
||||
if (!is_upper("A")) fail("is_upper A should be true")
|
||||
if (!is_upper("Z")) fail("is_upper Z should be true")
|
||||
if (is_upper("a")) fail("is_upper a should be false")
|
||||
if (is_upper("5")) fail("is_upper 5 should be false")
|
||||
if (is_upper("AB")) fail("is_upper AB should be false")
|
||||
if (is_upper(42)) fail("is_upper number should be false")
|
||||
})
|
||||
|
||||
run("is_whitespace", function() {
|
||||
if (!is_whitespace(" ")) fail("is_whitespace space should be true")
|
||||
if (!is_whitespace("\t")) fail("is_whitespace tab should be true")
|
||||
if (!is_whitespace("\n")) fail("is_whitespace newline should be true")
|
||||
if (is_whitespace("a")) fail("is_whitespace a should be false")
|
||||
if (is_whitespace(" ")) fail("is_whitespace two spaces should be false")
|
||||
if (is_whitespace(42)) fail("is_whitespace number should be false")
|
||||
if (is_whitespace(null)) fail("is_whitespace null should be false")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// GLOBAL FUNCTIONS - LENGTH
|
||||
// ============================================================================
|
||||
@@ -3409,6 +3502,59 @@ run("array map with exit", function() {
|
||||
if (length(result) != 5) fail("array map with exit length unexpected")
|
||||
})
|
||||
|
||||
run("inline map intrinsic is_data", function() {
|
||||
var items = [{}, [], "hello", 42, true]
|
||||
var result = array(items, is_data)
|
||||
if (result[0] != true) fail("is_data {} should be true")
|
||||
if (result[1] != false) fail("is_data [] should be false")
|
||||
if (result[2] != false) fail("is_data string should be false")
|
||||
if (result[3] != false) fail("is_data number should be false")
|
||||
if (result[4] != false) fail("is_data bool should be false")
|
||||
if (length(result) != 5) fail("result length should be 5")
|
||||
})
|
||||
|
||||
run("inline map intrinsic is_number", function() {
|
||||
var items = [1, "two", 3.14, null, true]
|
||||
var result = array(items, is_number)
|
||||
if (result[0] != true) fail("1 should be number")
|
||||
if (result[1] != false) fail("'two' should not be number")
|
||||
if (result[2] != true) fail("3.14 should be number")
|
||||
if (result[3] != false) fail("null should not be number")
|
||||
if (result[4] != false) fail("true should not be number")
|
||||
})
|
||||
|
||||
run("inline map intrinsic is_text", function() {
|
||||
var items = ["hello", 42, "", null]
|
||||
var result = array(items, is_text)
|
||||
if (result[0] != true) fail("'hello' should be text")
|
||||
if (result[1] != false) fail("42 should not be text")
|
||||
if (result[2] != true) fail("'' should be text")
|
||||
if (result[3] != false) fail("null should not be text")
|
||||
})
|
||||
|
||||
run("inline map intrinsic is_digit", function() {
|
||||
var chars = array("a5B2 ")
|
||||
var result = array(chars, is_digit)
|
||||
if (result[0] != false) fail("a should not be digit")
|
||||
if (result[1] != true) fail("5 should be digit")
|
||||
if (result[2] != false) fail("B should not be digit")
|
||||
if (result[3] != true) fail("2 should be digit")
|
||||
if (result[4] != false) fail("space should not be digit")
|
||||
})
|
||||
|
||||
run("inline map lambda", function() {
|
||||
var arr = [10, 20, 30]
|
||||
var result = array(arr, function(x) { return x + 1 })
|
||||
if (result[0] != 11) fail("10+1 should be 11")
|
||||
if (result[1] != 21) fail("20+1 should be 21")
|
||||
if (result[2] != 31) fail("30+1 should be 31")
|
||||
})
|
||||
|
||||
run("inline map empty array", function() {
|
||||
var result = array([], is_number)
|
||||
if (length(result) != 0) fail("map of empty should be empty")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// STRING METHOD EDGE CASES
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user