diff --git a/source/compiler/codegen/expression/helper.ts b/source/compiler/codegen/expression/helper.ts index 12c1de0..ae365d9 100644 --- a/source/compiler/codegen/expression/helper.ts +++ b/source/compiler/codegen/expression/helper.ts @@ -69,7 +69,6 @@ export function InlineClamp(ctx: Context, type: IntrinsicType, min: number | nul } const x = ctx.scope.register.allocate(type.bitcode); - if (min !== null) { ctx.block.push(Instruction.local.tee(x.ref)); ctx.block.push(scope.const(min)); diff --git a/source/compiler/codegen/expression/infix.ts b/source/compiler/codegen/expression/infix.ts index 1bcd926..8fefba5 100644 --- a/source/compiler/codegen/expression/infix.ts +++ b/source/compiler/codegen/expression/infix.ts @@ -130,45 +130,59 @@ function CoerceToFloat(ctx: Context, type: IntrinsicType, goal: IntrinsicType) { } function CoerceToInt(ctx: Context, type: IntrinsicType, goal: IntrinsicType) { - // Is the value on the stack already in the right bitcode? - let corrected = false; - // Encoding conversions if (type === f32) { if (goal.bitcode === WasmTypes.Intrinsic.i32) { - if (goal.signed) ctx.block.push(Instruction.f32.convert_i32_s()); - else ctx.block.push(Instruction.f32.convert_i32_u()); + if (goal.signed) { + ctx.block.push(Instruction.f32.convert_i32_s()); + type = i32; + } else { + ctx.block.push(Instruction.f32.convert_i32_u()); + type = u32; + } } else { - if (goal.signed) ctx.block.push(Instruction.f32.convert_i64_s()); - else ctx.block.push(Instruction.f32.convert_i64_u()); + if (goal.signed) { + ctx.block.push(Instruction.f32.convert_i64_s()); + type = i64; + } else { + ctx.block.push(Instruction.f32.convert_i64_u()); + type = u64; + } } - corrected = true; } else if (type === f64) { if (goal.bitcode === WasmTypes.Intrinsic.i32) { - if (goal.signed) ctx.block.push(Instruction.f64.convert_i32_s()); - else ctx.block.push(Instruction.f64.convert_i32_u()); + if (goal.signed) { + ctx.block.push(Instruction.f64.convert_i32_s()); + goal = i32; + } else { + ctx.block.push(Instruction.f64.convert_i32_u()); + type = u32; + } } else { - if (goal.signed) ctx.block.push(Instruction.f64.convert_i64_s()); - else ctx.block.push(Instruction.f64.convert_i64_u()); + if (goal.signed) { + ctx.block.push(Instruction.f64.convert_i64_s()); + type = i64; + } + else { + ctx.block.push(Instruction.f64.convert_i64_u()); + type = u64; + } } - corrected = true; - } else { - corrected = type.bitcode === goal.bitcode; } // Bound the value to the correct size for the target - if (goal.effectiveSize() < type.effectiveSize()) { - const max = Math.pow(2, goal.effectiveSize()); - const min = goal.signed - ? Math.pow(2, goal.size-1) + if (goal.tciBitDepth() < type.tciBitDepth()) { + const max = Math.pow(2, goal.tciBitDepth()) + ( (goal.signed && type.signed) ? -1 : 0 ); + const min = (goal.signed && type.signed) + ? -Math.pow(2, goal.tciBitDepth()) : 0; InlineClamp(ctx, type, min, max); } - if (!corrected) { - if (goal === i32) { - if (type.signed) { /* how */ } + if (type.bitcode != goal.bitcode) { + if (goal.bitcode == WasmTypes.Intrinsic.i32) { + if (type.signed) ctx.block.push(Instruction.i32.warp_i64()); else ctx.block.push(Instruction.i32.warp_i64()); } else { diff --git a/source/compiler/intrinsic.ts b/source/compiler/intrinsic.ts index 9a7e4dc..0ebfcc6 100644 --- a/source/compiler/intrinsic.ts +++ b/source/compiler/intrinsic.ts @@ -43,9 +43,9 @@ export class IntrinsicType { return this.bitcode; } - effectiveSize() { - if (this.signed) return this.size - 1; - else return this.size; + tciBitDepth() { + if (this.signed) return this.size*8 - 1; + else return this.size*8; } } diff --git a/source/wasm/instruction/constant.ts b/source/wasm/instruction/constant.ts index 77987b2..4ee143a 100644 --- a/source/wasm/instruction/constant.ts +++ b/source/wasm/instruction/constant.ts @@ -1,5 +1,5 @@ // https://webassembly.github.io/spec/core/binary/instructions.html#numeric-instructions -import { EncodeF32, EncodeF64, EncodeI32, EncodeI64 } from "~/wasm/type.ts"; +import { EncodeF32, EncodeF64, EncodeI32, EncodeI64, EncodeU32, EncodeU64 } from "~/wasm/type.ts"; import { LatentOffset, LatentValue, Byte } from "~/helper.ts"; @@ -25,22 +25,24 @@ export class Constant { } toBinary(): Byte[] { + const val = this.read(); + switch (this.type) { case Type.i32: return [ this.type, - ...EncodeI32(this.read()) + ...(val < 0 ? EncodeI32(val) : EncodeU32(val)) ]; case Type.i64: return [ this.type, - ...EncodeI64(this.read()) + ...(val < 0 ? EncodeI64(val) : EncodeU64(val)) ]; case Type.f32: return [ this.type, - ...EncodeF32(this.read()) + ...EncodeF32(val) ]; case Type.f64: return [ this.type, - ...EncodeF64(this.read()) + ...EncodeF64(val) ]; } diff --git a/source/wasm/instruction/numeric.ts b/source/wasm/instruction/numeric.ts index a184a74..fff9bd3 100644 --- a/source/wasm/instruction/numeric.ts +++ b/source/wasm/instruction/numeric.ts @@ -192,8 +192,8 @@ const wrapper = { trunc_f64_s : () => i32trunc_f64_s, trunc_f64_u : () => i32trunc_f64_u, - extend8_s : () => i32extend8_s, - extend16_s : () => i32extend16_s, + extend_8_s : () => i32extend8_s, + extend_16_s : () => i32extend16_s, reinterpret_f32 : () => i32reinterpret_f32, }, i64: { @@ -228,6 +228,9 @@ const wrapper = { rotl: () => i64rotl, rotr: () => i64rotr, + extend_8_s: ()=> i64extend8_s, + extend_16_s: () => i64extend16_s, + extend_32_s: () => i64extend32_s, extend_i32_s: () => i64extend_i32_s, extend_i32_u: () => i64extend_i32_u, trunc_f32_s: () => i64trunc_f32_s, @@ -236,10 +239,6 @@ const wrapper = { trunc_f64_u: () => i64trunc_f64_u, reinterpret_f64: () => i64reinterpret_f64, - - extend8_s: ()=> i64extend8_s, - extend16_s: () => i64extend16_s, - extend32_s: () => i64extend32_s, }, f32: { eq : () => f32eq , diff --git a/source/wasm/type.ts b/source/wasm/type.ts index 805b4f3..9c8d468 100644 --- a/source/wasm/type.ts +++ b/source/wasm/type.ts @@ -46,16 +46,16 @@ export function EncodeLimitType(min: number, max?: number): Byte[] { export function EncodeF32(val: number): Byte[] { - let buffer = new ArrayBuffer(4); - let view = new DataView(buffer); + const buffer = new ArrayBuffer(4); + const view = new DataView(buffer); view.setFloat32(0, val, true); return [...(new Uint8Array(buffer))]; } export function EncodeF64(val: number): Byte[] { - let buffer = new ArrayBuffer(8); - let view = new DataView(buffer); + const buffer = new ArrayBuffer(8); + const view = new DataView(buffer); view.setFloat64(0, val, true); return [...(new Uint8Array(buffer))]; @@ -67,7 +67,7 @@ export function EncodeSignedLEB(val: number): Byte[] { if (val % 1 !== 0) throw new Error(`Requested u32 encode for non integer value ${val}`); - let result: Byte[] = []; + const result: Byte[] = []; // LEB128 encoding: https://en.wikipedia.org/wiki/LEB128#Encode_signed_32-bit_integer while (true) { @@ -86,10 +86,8 @@ export function EncodeSignedLEB(val: number): Byte[] { return result; } export function EncodeUnsignedLEB(val: number): Byte[] { - if (val % 1 !== 0) - throw new Error(`Requested u32 encode for non integer value ${val}`); - if (val < 0) - throw new Error(`Requested u32 encode for signed integer value ${val}`); + if (val % 1 !== 0) throw new Error(`Requested u32 encode for non integer value ${val}`); + if (val < 0) throw new Error(`Requested u32 encode for signed integer value ${val}`); // LEB128 encoding: https://en.wikipedia.org/wiki/LEB128#Encode_unsigned_32-bit_integer const result: Byte[] = []; @@ -102,32 +100,24 @@ export function EncodeUnsignedLEB(val: number): Byte[] { byte |= 0b10000000; } result.push(byte); - } while(val !== 0); + } while(val > 0); return result; } export function EncodeU32(val: number) { - if (val > 2**32) - throw new Error(`Requested to encode an u32 with too large a number ${val}`); - + // if (val > 2**32) throw new Error(`Requested to encode an u32 with too large a number ${val}`); return EncodeUnsignedLEB(val); } export function EncodeI32(val: number) { - if (Math.abs(val) > 2**31) - throw new Error(`Requested to encode an i32 with too large a number ${val}`); - + // if (Math.abs(val) > 2**31) throw new Error(`Requested to encode an i32 with too large a number ${val}`); return EncodeSignedLEB(val); } export function EncodeU64(val: number) { - if (val > 2**64) - throw new Error(`Requested to encode an u64 with too large a number ${val}`); - + // if (val > 2**64) throw new Error(`Requested to encode an u64 with too large a number ${val}`); return EncodeUnsignedLEB(val); } export function EncodeI64(val: number) { - if (Math.abs(val) > 2**63) - throw new Error(`Requested to encode an i64 with too large a number ${val}`); - - return EncodeUnsignedLEB(val); + // if (Math.abs(val) > 2**63) throw new Error(`Requested to encode an i64 with too large a number ${val}`); + return EncodeSignedLEB(val); } \ No newline at end of file diff --git a/tests/conversion.test.sa b/tests/conversion.test.sa index 264a828..edc993a 100644 --- a/tests/conversion.test.sa +++ b/tests/conversion.test.sa @@ -8,13 +8,13 @@ fn maxI16(): i16 { return 32_767; } fn maxU16(): u16 { - return 65535; + return 65_535; } fn maxU32(): u32 { - return 2_147_483_647; + return 4_294_967_295; } fn maxI32(): i32 { - return 4_294_967_295; + return 2_147_483_647; } fn maxI64(): i64 { return 9_223_372_036_854_775_807; @@ -23,42 +23,47 @@ fn maxU64(): u64 { return 18_446_744_073_709_551_615; } -// test "Constant Saturation" { -// let t = 300 as u8; +// Stop auto type cohersion +fn int(val: i64): i64 { + return val; +} -// if (300 as u8) != maxU8() { return false; }; -// if (16 as u8) != 16 { return false; }; -// if (200 as i8) != maxI8() { return false; }; -// if (120 as i8) != 120 { return false; }; +test "Constant Saturation" { + if (int(300) as u8) != maxU8() { return false; }; + if (int(16) as u8) != 16 { return false; }; + if (int(200) as i8) != maxI8() { return false; }; + if (int(120) as i8) != 120 { return false; }; -// if (66_000 as u16) != maxU16() { return false; }; -// if (65_534 as u16) != 65_534 { return false; }; -// if (34_767 as i16) != maxI16() { return false; }; -// if (32_000 as i16) != 32_000 { return false; }; + if (int(66_000) as u16) != maxU16() { return false; }; + if (int(65_534) as u16) != 65_534 { return false; }; + if (int(34_767) as i16) != maxI16() { return false; }; + if (int(32_000) as i16) != 32_000 { return false; }; -// if (300 as u32) != maxU32() { return false; }; -// if (116 as u32) != 116 { return false; }; -// if (200 as i32) != maxI32() { return false; }; -// if (120 as i32) != 120 { return false; }; + // Cannot do big integers (+31bits) because JS moment + // if (int(17179869184) as u32) != maxU32() { return false; }; + // if (int(116) as u32) != 116 { return false; }; + // if (int(17179869184) as i32) != maxI32() { return false; }; + // if (int(120) as i32) != 120 { return false; }; -// return true; -// } + return true; +} -// test "Runtime Saturation" { -// let bigInt = maxU64(); +test "Runtime Saturation" { + let bigInt = maxU32(); -// if (bigInt as u8) != maxU8() { return false; }; -// if (bigInt as i8) != maxI8() { return false; }; + if (bigInt as u8) != maxU8() { return false; }; + if (bigInt as i8) != maxI8() { return false; }; -// if (bigInt as u16) != maxU16() { return false; }; -// if (bigInt as i16) != maxI16() { return false; }; + if (bigInt as u16) != maxU16() { return false; }; + if (bigInt as i16) != maxI16() { return false; }; -// if (bigInt as u32) != maxU32() { return false; }; -// if (bigInt as i32) != maxI32() { return false; }; + if (bigInt as u32) != maxU32() { return false; }; + if (bigInt as i32) != maxI32() { return false; }; -// if (bigInt as i64) != maxI64() { return false; }; + // Cannot do big integers (+31bits) because JS moment + // if (bigInt as i64) != maxI64() { return false; }; -// return true; -// } \ No newline at end of file + return true; +} \ No newline at end of file diff --git a/tests/time.test.sa b/tests/time.test.sa index af7d9fc..b3e4147 100644 --- a/tests/time.test.sa +++ b/tests/time.test.sa @@ -110,8 +110,6 @@ fn main(): none { return; } -fish - // test "Valid Epoch" { // let t = DateTimeFromUnix(0); // if ( t.date.day != 0