From d5c9028be4d5e85a424cc1e62509775045150a80 Mon Sep 17 00:00:00 2001 From: Rerumu Date: Thu, 19 May 2022 00:10:31 -0400 Subject: [PATCH] Start adding integer support for Luau --- codegen-luau/runtime/runtime.lua | 381 ++++++++++++++++++++----------- codegen-luau/src/lib.rs | 7 +- 2 files changed, 248 insertions(+), 140 deletions(-) diff --git a/codegen-luau/runtime/runtime.lua b/codegen-luau/runtime/runtime.lua index db4b9ee..e822957 100644 --- a/codegen-luau/runtime/runtime.lua +++ b/codegen-luau/runtime/runtime.lua @@ -1,37 +1,23 @@ local module = {} -local math_floor = math.floor -local math_ceil = math.ceil +local MAX_SIGNED = 0x7fffffff +local BIT_SET_32 = 0x100000000 -local bit32 = bit32 -local bit_band = bit32.band +local to_u32 = bit32.band -local function no_op(x) - return x -end +local num_from_u32 = I64.from_u32 +local num_into_u32 = I64.into_u32 -local function to_u32(x) - return bit_band(x, 0xFFFFFFFF) -end - -local function to_i32(x) - if x > 0x7FFFFFFF then - x = x - 0x100000000 +local function to_i32(num) + if num > MAX_SIGNED then + num = num - BIT_SET_32 end - return x + return num end -local function wrap_i32(x) - return to_i32(to_u32(x)) -end - -local function truncate(num) - if num >= 0 then - return math_floor(num) - else - return math_ceil(num) - end +local function no_op(num) + return num end do @@ -40,33 +26,45 @@ do local mul = {} local div = {} + local assert = assert + function add.i32(a, b) - return wrap_i32(a + b) + return to_u32(a + b) end + add.i64 = I64.add + function sub.i32(a, b) - return wrap_i32(a - b) + return to_u32(a - b) end + sub.i64 = I64.subtract + function mul.i32(a, b) - return wrap_i32(a * b) + return to_u32(a * b) end + mul.i64 = I64.multiply + function div.i32(lhs, rhs) assert(rhs ~= 0, "division by zero") - return truncate(lhs / rhs) + lhs = to_i32(lhs) + rhs = to_i32(rhs) + + return to_u32(lhs / rhs) end + div.i64 = I64.divide_signed + function div.u32(lhs, rhs) assert(rhs ~= 0, "division by zero") - lhs = to_u32(lhs) - rhs = to_u32(rhs) - - return to_i32(math.floor(lhs / rhs)) + return to_u32(lhs / rhs) end + div.u64 = I64.divide_unsigned + module.add = add module.sub = sub module.mul = mul @@ -78,6 +76,8 @@ do local ctz = {} local popcnt = {} + local bit_and = bit32.band + clz.i32 = bit32.countlz ctz.i32 = bit32.countrz @@ -85,7 +85,7 @@ do local count = 0 while num ~= 0 do - num = bit32.band(num, num - 1) + num = bit_and(num, num - 1) count = count + 1 end @@ -98,71 +98,55 @@ do end do - local eqz = {} - local eq = {} - local ne = {} local le = {} local lt = {} local ge = {} local gt = {} - local function to_boolean(cond) - if cond then - return 1 - else - return 0 - end - end - - function eq.i32(lhs, rhs) - return to_boolean(lhs == rhs) - end - function eq.num(lhs, rhs) - return to_boolean(lhs == rhs) - end - - function eqz.i32(lhs) - return to_boolean(lhs == 0) - end - - function ne.i32(lhs, rhs) - return to_boolean(lhs ~= rhs) - end - function ne.num(lhs, rhs) - return to_boolean(lhs ~= rhs) - end + local num_is_equal = I64.is_equal + local num_is_greater_signed = I64.is_greater_signed + local num_is_greater_unsigned = I64.is_greater_unsigned + local num_is_less_signed = I64.is_less_signed + local num_is_less_unsigned = I64.is_less_unsigned function ge.i32(lhs, rhs) - return to_boolean(lhs >= rhs) + return to_i32(lhs) >= to_i32(rhs) end - function ge.u32(lhs, rhs) - return to_boolean(to_u32(lhs) >= to_u32(rhs)) + + function ge.i64(lhs, rhs) + return num_is_greater_signed(lhs, rhs) or num_is_equal(lhs, rhs) + end + + function ge.u64(lhs, rhs) + return num_is_greater_unsigned(lhs, rhs) or num_is_equal(lhs, rhs) end function gt.i32(lhs, rhs) - return to_boolean(lhs > rhs) - end - function gt.u32(lhs, rhs) - return to_boolean(to_u32(lhs) > to_u32(rhs)) + return to_i32(lhs) > to_i32(rhs) end + gt.i64 = num_is_greater_signed + gt.u64 = num_is_greater_unsigned + function le.i32(lhs, rhs) - return to_boolean(lhs <= rhs) + return to_i32(lhs) <= to_i32(rhs) end - function le.u32(lhs, rhs) - return to_boolean(to_u32(lhs) <= to_u32(rhs)) + + function le.i64(lhs, rhs) + return num_is_less_signed(lhs, rhs) or num_is_equal(lhs, rhs) + end + + function le.u64(lhs, rhs) + return num_is_less_unsigned(lhs, rhs) or num_is_equal(lhs, rhs) end function lt.i32(lhs, rhs) - return to_boolean(lhs < rhs) - end - function lt.u32(lhs, rhs) - return to_boolean(to_u32(lhs) < to_u32(rhs)) + return to_i32(lhs) < to_i32(rhs) end - module.eqz = eqz - module.eq = eq - module.ne = ne + lt.i64 = num_is_less_signed + lt.u64 = num_is_less_unsigned + module.le = le module.lt = lt module.ge = ge @@ -176,12 +160,16 @@ do local bnot = {} band.i32 = bit32.band + band.i64 = I64.bit_and bnot.i32 = bit32.bnot + bnot.i64 = I64.bit_not bor.i32 = bit32.bor + bor.i64 = I64.bit_or bxor.i32 = bit32.bxor + bxor.i64 = I64.bit_xor module.band = band module.bor = bor @@ -196,14 +184,20 @@ do local rotr = {} rotl.i32 = bit32.lrotate + rotl.i64 = bit32.lrotate rotr.i32 = bit32.rrotate + rotr.i64 = bit32.rrotate shl.i32 = bit32.lshift + shl.i64 = bit32.lshift shl.u32 = bit32.lshift + shl.u64 = bit32.lshift shr.i32 = bit32.arshift + shr.i64 = bit32.arshift shr.u32 = bit32.rshift + shr.u64 = bit32.rshift module.shl = shl module.shr = shr @@ -218,19 +212,123 @@ do local convert = {} local reinterpret = {} - trunc.i32_f32 = truncate - trunc.i32_f64 = truncate - trunc.u32_f32 = truncate - trunc.u32_f64 = truncate + local math_ceil = math.ceil + local math_floor = math.floor - extend.i64_i32 = no_op + local string_pack = string.pack + local string_unpack = string.unpack - function convert.f32_i32(num) - return num + local num_from_u64 = I64.from_u64 + local num_into_u64 = I64.into_u64 + + local num_negate = I64.negate + local num_is_negative = I64.is_negative + + function wrap.i32_i64(num) + local data_1, _ = num_into_u32(num) + + return data_1 end - function convert.f64_i32(num) - return num + trunc.i32_f32 = to_u32 + trunc.i32_f64 = to_u32 + trunc.u32_f32 = no_op + trunc.u32_f64 = no_op + + function trunc.i64_f32(num) + if num < 0 then + local temp = num_from_u64(-math_ceil(num)) + + return num_negate(temp) + else + local temp = math_floor(num) + + return num_from_u64(temp) + end + end + + function trunc.i64_f64(num) + if num < 0 then + local temp = num_from_u64(-math_ceil(num)) + + return num_negate(temp) + else + local temp = math_floor(num) + + return num_from_u64(temp) + end + end + + trunc.u64_f32 = num_from_u64 + trunc.u64_f64 = num_from_u64 + + function extend.i64_i32(num) + if num > MAX_SIGNED then + local temp = num_from_u32(-num + BIT_SET_32, 0) + + return num_negate(temp) + else + return num_from_u32(num, 0) + end + end + + function extend.u64_i32(num) + return num_from_u32(num, 0) + end + + convert.f32_i32 = no_op + convert.f32_u32 = no_op + + function convert.f32_i64(num) + if num_is_negative(num) then + local temp = num_negate(num) + + return -num_into_u64(temp) + else + return num_into_u64(num) + end + end + + convert.f32_u64 = num_into_u64 + convert.f64_i32 = to_i32 + convert.f64_u32 = no_op + + function convert.f64_i64(num) + if num_is_negative(num) then + local temp = num_negate(num) + + return -num_into_u64(temp) + else + return num_into_u64(num) + end + end + + convert.f64_u64 = num_into_u64 + + function reinterpret.i32_f32(num) + local packed = string_pack("f", num) + + return string_unpack(" 0x7F then b = b - 0x100 @@ -285,71 +377,82 @@ do return b end - load.i32_u8 = load_byte + function load.i32_u8(memory, addr) + return load_byte(memory.data, addr) + end function load.i32(memory, addr) + local data = memory.data + if addr % 4 == 0 then -- aligned read - return memory.data[addr / 4] or 0 + return data[addr / 4] or 0 else -- unaligned read - local b1 = load_byte(memory, addr) - local b2 = bit32.lshift(load_byte(memory, addr + 1), 8) - local b3 = bit32.lshift(load_byte(memory, addr + 2), 16) - local b4 = bit32.lshift(load_byte(memory, addr + 3), 24) + local b1 = load_byte(data, addr) + local b2 = bit_lshift(load_byte(data, addr + 1), 8) + local b3 = bit_lshift(load_byte(data, addr + 2), 16) + local b4 = bit_lshift(load_byte(data, addr + 3), 24) - return bit32.bor(b1, b2, b3, b4) + return bit_bor(b1, b2, b3, b4) end end + local load_i32 = load.i32 + function load.i64(memory, addr) - local hi = load.i32(memory, addr + 4) - local lo = load.i32(memory, addr) + local data_1 = load_i32(memory, addr) + local data_2 = load_i32(memory, addr + 4) - return merge_u64(hi, lo) + return num_from_u32(data_1, data_2) end - store.i32_n8 = store_byte + function store.i32_i8(memory, addr, value) + store_byte(memory.data, addr, value) + end function store.i32(memory, addr, value) + local data = memory.data + if addr % 4 == 0 then -- aligned write - memory.data[addr / 4] = value + data[addr / 4] = value else -- unaligned write - store_byte(memory, addr, value) - store_byte(memory, addr + 1, bit32.rshift(value, 8)) - store_byte(memory, addr + 2, bit32.rshift(value, 16)) - store_byte(memory, addr + 3, bit32.rshift(value, 24)) + store_byte(data, addr, value) + store_byte(data, addr + 1, bit_rshift(value, 8)) + store_byte(data, addr + 2, bit_rshift(value, 16)) + store_byte(data, addr + 3, bit_rshift(value, 24)) end end - function store.i64(memory, addr, value) - local hi, lo = rip_u64(value) + local store_i32 = store.i32 - store.i32(memory, addr, lo) - store.i32(memory, addr + 4, hi) + function store.i64(memory, addr, value) + local data_1, data_2 = num_into_u32(value) + + store_i32(memory, addr, data_1) + store_i32(memory, addr + 4, data_2) end function allocator.new(min, max) return { min = min, max = max, data = {} } end - function allocator.init(memory, offset, data) - local store_i8 = module.store.i32_n8 - local store_i32 = module.store.i32 + local store_i8 = store.i32_n8 + function allocator.init(memory, offset, data) local len = #data local rem = len % 4 for i = 1, len - rem, 4 do - local v = string.unpack("