diff --git a/assembly/arguments.ts b/assembly/arguments.ts index a0463b4..8b52630 100644 --- a/assembly/arguments.ts +++ b/assembly/arguments.ts @@ -1,4 +1,4 @@ -import { minI32 } from "./math"; +import { IntMath } from "./math"; import { portable } from "./portable"; export enum Arguments { @@ -55,7 +55,7 @@ type ArgsDecoder = (args: Args, code: StaticArray, offset: u32, end: u32) => function twoImm(args: Args, code: StaticArray, offset: u32, end: u32): Args { const low = lowNibble(portable.staticArrayAt(code, offset)); - const split = minI32(4, low) + 1; + const split = IntMath.minI32(4, low) + 1; const first = decodeI32(code, offset + 1, offset + split); const second = decodeI32(code, offset + split, end); return args.fill(first, second, 0, 0); @@ -91,7 +91,7 @@ export const DECODERS: StaticArray = StaticArray.fromArray { const h = higNibble(data[o]); const l = lowNibble(data[o]); - const split = minI32(4, h) + 1; + const split = IntMath.minI32(4, h) + 1; const immA = decodeI32(data, o + 1, o + split); const immB = decodeI32(data, o + split, lim); return args.fill(l, immA, immB, 0); @@ -100,7 +100,7 @@ export const DECODERS: StaticArray = StaticArray.fromArray { const h = higNibble(data[o]); const l = lowNibble(data[o]); - const split = minI32(4, h) + 1; + const split = IntMath.minI32(4, h) + 1; const immA = decodeI32(data, o + 1, o + split); const offs = decodeI32(data, o + split, lim); return args.fill(l, immA, offs, 0); diff --git a/assembly/instructions/bit.test.ts b/assembly/instructions/bit.test.ts index 5f5cc00..6cde8bc 100644 --- a/assembly/instructions/bit.test.ts +++ b/assembly/instructions/bit.test.ts @@ -15,7 +15,7 @@ import { zero_extend_16, } from "./bit"; import { Outcome, OutcomeData } from "./outcome"; -import { reg } from "./utils"; +import { Inst } from "./utils"; export const TESTS: Test[] = [ test("count_set_bits_64", () => { @@ -25,7 +25,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xffff_0000_1111; + regs[Inst.reg(args.a)] = 0xffff_0000_1111; const memo = new MemoryBuilder().build(); // when @@ -34,7 +34,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 2 * 8 + 4); + assert.isEqual(regs[Inst.reg(0xf)], 2 * 8 + 4); return assert; }), test("count_set_bits_32", () => { @@ -44,7 +44,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xffff_0000_1111; + regs[Inst.reg(args.a)] = 0xffff_0000_1111; const memo = new MemoryBuilder().build(); // when @@ -53,7 +53,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 4); + assert.isEqual(regs[Inst.reg(0xf)], 4); return assert; }), test("leading_zero_bits_64", () => { @@ -63,7 +63,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xfff0_0000_0111; + regs[Inst.reg(args.a)] = 0xfff0_0000_0111; const memo = new MemoryBuilder().build(); // when @@ -72,7 +72,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 16); + assert.isEqual(regs[Inst.reg(0xf)], 16); return assert; }), test("leading_zero_bits_32", () => { @@ -82,7 +82,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xffff_0000_1111; + regs[Inst.reg(args.a)] = 0xffff_0000_1111; const memo = new MemoryBuilder().build(); // when @@ -91,7 +91,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 19); + assert.isEqual(regs[Inst.reg(0xf)], 19); return assert; }), test("trailing_zero_bits_64", () => { @@ -101,7 +101,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xfff0_0000_0000; + regs[Inst.reg(args.a)] = 0xfff0_0000_0000; const memo = new MemoryBuilder().build(); // when @@ -110,7 +110,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 36); + assert.isEqual(regs[Inst.reg(0xf)], 36); return assert; }), test("trailing_zero_bits_32", () => { @@ -120,7 +120,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xfff0_0000_0000; + regs[Inst.reg(args.a)] = 0xfff0_0000_0000; const memo = new MemoryBuilder().build(); // when @@ -129,7 +129,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 32); + assert.isEqual(regs[Inst.reg(0xf)], 32); return assert; }), test("sign_extend_8", () => { @@ -139,7 +139,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xdead_beef; + regs[Inst.reg(args.a)] = 0xdead_beef; const memo = new MemoryBuilder().build(); // when @@ -148,7 +148,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 0xffff_ffff_ffff_ffef); + assert.isEqual(regs[Inst.reg(0xf)], 0xffff_ffff_ffff_ffef); return assert; }), test("sign_extend_16", () => { @@ -158,7 +158,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xdead_beef; + regs[Inst.reg(args.a)] = 0xdead_beef; const memo = new MemoryBuilder().build(); // when @@ -167,7 +167,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 0xffff_ffff_ffff_beef); + assert.isEqual(regs[Inst.reg(0xf)], 0xffff_ffff_ffff_beef); return assert; }), test("zero_extend_16", () => { @@ -177,7 +177,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xdead_beef; + regs[Inst.reg(args.a)] = 0xdead_beef; const memo = new MemoryBuilder().build(); // when @@ -186,7 +186,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 0x0000_beef); + assert.isEqual(regs[Inst.reg(0xf)], 0x0000_beef); return assert; }), test("reverse_bytes", () => { @@ -196,7 +196,7 @@ export const TESTS: Test[] = [ args.a = 0x1; args.b = 0xf; const regs = newRegisters(); - regs[reg(args.a)] = 0xfff0_dead_beef; + regs[Inst.reg(args.a)] = 0xfff0_dead_beef; const memo = new MemoryBuilder().build(); // when @@ -205,7 +205,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(res.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(0xf)], 0xefbe_adde_f0ff_0000); + assert.isEqual(regs[Inst.reg(0xf)], 0xefbe_adde_f0ff_0000); return assert; }), ]; diff --git a/assembly/instructions/bit.ts b/assembly/instructions/bit.ts index 99b457f..1d374b9 100644 --- a/assembly/instructions/bit.ts +++ b/assembly/instructions/bit.ts @@ -1,63 +1,63 @@ import { portable } from "../portable"; -import { InstructionRun, ok } from "./outcome"; -import { reg, u8SignExtend, u16SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; // COUNT_SET_BITS_64 export const count_set_bits_64: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = portable.popcnt_u64(regs[reg(args.a)]); - return ok(r); + regs[Inst.reg(args.b)] = portable.popcnt_u64(regs[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // COUNT_SET_BITS_32 export const count_set_bits_32: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = u64(portable.popcnt_u32(u32(regs[reg(args.a)]))); - return ok(r); + regs[Inst.reg(args.b)] = u64(portable.popcnt_u32(u32(regs[Inst.reg(args.a)]))); + return OutcomeData.ok(r); }; // LEADING_ZERO_BITS_64 export const leading_zero_bits_64: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = portable.clz_u64(regs[reg(args.a)]); - return ok(r); + regs[Inst.reg(args.b)] = portable.clz_u64(regs[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // LEADING_ZERO_BITS_32 export const leading_zero_bits_32: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = u64(portable.clz_u32(u32(regs[reg(args.a)]))); - return ok(r); + regs[Inst.reg(args.b)] = u64(portable.clz_u32(u32(regs[Inst.reg(args.a)]))); + return OutcomeData.ok(r); }; // TRAILING_ZERO_BITS_64 export const trailing_zero_bits_64: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = portable.ctz_u64(regs[reg(args.a)]); - return ok(r); + regs[Inst.reg(args.b)] = portable.ctz_u64(regs[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // TRAILING_ZERO_BITS_32 export const trailing_zero_bits_32: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = u64(portable.ctz_u32(u32(regs[reg(args.a)]))); - return ok(r); + regs[Inst.reg(args.b)] = u64(portable.ctz_u32(u32(regs[Inst.reg(args.a)]))); + return OutcomeData.ok(r); }; // SIGN_EXTEND_8 export const sign_extend_8: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = u8SignExtend(u8(regs[reg(args.a)])); - return ok(r); + regs[Inst.reg(args.b)] = Inst.u8SignExtend(u8(regs[Inst.reg(args.a)])); + return OutcomeData.ok(r); }; // SIGN_EXTEND_16 export const sign_extend_16: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = u16SignExtend(u16(regs[reg(args.a)])); - return ok(r); + regs[Inst.reg(args.b)] = Inst.u16SignExtend(u16(regs[Inst.reg(args.a)])); + return OutcomeData.ok(r); }; // ZERO_EXTEND_16 export const zero_extend_16: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = u64(u16(regs[reg(args.a)])); - return ok(r); + regs[Inst.reg(args.b)] = u64(u16(regs[Inst.reg(args.a)])); + return OutcomeData.ok(r); }; // REVERSE_BYTES export const reverse_bytes: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = portable.bswap_u64(regs[reg(args.a)]); - return ok(r); + regs[Inst.reg(args.b)] = portable.bswap_u64(regs[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; diff --git a/assembly/instructions/branch.test.ts b/assembly/instructions/branch.test.ts index e90e9cd..373a35d 100644 --- a/assembly/instructions/branch.test.ts +++ b/assembly/instructions/branch.test.ts @@ -4,7 +4,7 @@ import { newRegisters } from "../registers"; import { Assert, Test, test } from "../test"; import { branch_eq_imm } from "./branch"; import { Outcome, OutcomeData } from "./outcome"; -import { reg } from "./utils"; +import { Inst } from "./utils"; export const TESTS: Test[] = [ test("branch_eq_imm", () => { @@ -15,7 +15,7 @@ export const TESTS: Test[] = [ args.b = 0xfe; args.c = 0xdeadbeef; const regs = newRegisters(); - regs[reg(args.a)] = 0xfe; + regs[Inst.reg(args.a)] = 0xfe; const memo = new MemoryBuilder().build(); diff --git a/assembly/instructions/branch.ts b/assembly/instructions/branch.ts index 1f4f189..4417a2a 100644 --- a/assembly/instructions/branch.ts +++ b/assembly/instructions/branch.ts @@ -1,136 +1,136 @@ -import { InstructionRun, ok, staticJump } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; // BRANCH_EQ_IMM export const branch_eq_imm: InstructionRun = (r, args, registers) => { - const b = u64(u32SignExtend(args.b)); - if (registers[reg(args.a)] === b) { - return staticJump(r, args.c); + const b = u64(Inst.u32SignExtend(args.b)); + if (registers[Inst.reg(args.a)] === b) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_NE_IMM export const branch_ne_imm: InstructionRun = (r, args, registers) => { - const b = u64(u32SignExtend(args.b)); - if (registers[reg(args.a)] !== b) { - return staticJump(r, args.c); + const b = u64(Inst.u32SignExtend(args.b)); + if (registers[Inst.reg(args.a)] !== b) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_LT_U_IMM export const branch_lt_u_imm: InstructionRun = (r, args, registers) => { - const b = u64(u32SignExtend(args.b)); - if (registers[reg(args.a)] < b) { - return staticJump(r, args.c); + const b = u64(Inst.u32SignExtend(args.b)); + if (registers[Inst.reg(args.a)] < b) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_LE_U_IMM export const branch_le_u_imm: InstructionRun = (r, args, registers) => { - const b = u64(u32SignExtend(args.b)); - if (registers[reg(args.a)] <= b) { - return staticJump(r, args.c); + const b = u64(Inst.u32SignExtend(args.b)); + if (registers[Inst.reg(args.a)] <= b) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_GE_U_IMM export const branch_ge_u_imm: InstructionRun = (r, args, registers) => { - const b = u64(u32SignExtend(args.b)); - if (registers[reg(args.a)] >= b) { - return staticJump(r, args.c); + const b = u64(Inst.u32SignExtend(args.b)); + if (registers[Inst.reg(args.a)] >= b) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_GT_U_IMM export const branch_gt_u_imm: InstructionRun = (r, args, registers) => { - const b = u64(u32SignExtend(args.b)); - if (registers[reg(args.a)] > b) { - return staticJump(r, args.c); + const b = u64(Inst.u32SignExtend(args.b)); + if (registers[Inst.reg(args.a)] > b) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_LT_S_IMM export const branch_lt_s_imm: InstructionRun = (r, args, registers) => { - if (i64(registers[reg(args.a)]) < i64(u32SignExtend(args.b))) { - return staticJump(r, args.c); + if (i64(registers[Inst.reg(args.a)]) < i64(Inst.u32SignExtend(args.b))) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_LE_S_IMM export const branch_le_s_imm: InstructionRun = (r, args, registers) => { - if (i64(registers[reg(args.a)]) <= i64(u32SignExtend(args.b))) { - return staticJump(r, args.c); + if (i64(registers[Inst.reg(args.a)]) <= i64(Inst.u32SignExtend(args.b))) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_GE_S_IMM export const branch_ge_s_imm: InstructionRun = (r, args, registers) => { - if (i64(registers[reg(args.a)]) >= i64(u32SignExtend(args.b))) { - return staticJump(r, args.c); + if (i64(registers[Inst.reg(args.a)]) >= i64(Inst.u32SignExtend(args.b))) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_GT_S_IMM export const branch_gt_s_imm: InstructionRun = (r, args, registers) => { - if (i64(registers[reg(args.a)]) > i64(u32SignExtend(args.b))) { - return staticJump(r, args.c); + if (i64(registers[Inst.reg(args.a)]) > i64(Inst.u32SignExtend(args.b))) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_EQ export const branch_eq: InstructionRun = (r, args, registers) => { - if (registers[reg(args.a)] === registers[reg(args.b)]) { - return staticJump(r, args.c); + if (registers[Inst.reg(args.a)] === registers[Inst.reg(args.b)]) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_NE export const branch_ne: InstructionRun = (r, args, registers) => { - if (registers[reg(args.a)] !== registers[reg(args.b)]) { - return staticJump(r, args.c); + if (registers[Inst.reg(args.a)] !== registers[Inst.reg(args.b)]) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_LT_U export const branch_lt_u: InstructionRun = (r, args, registers) => { - if (registers[reg(args.b)] < registers[reg(args.a)]) { - return staticJump(r, args.c); + if (registers[Inst.reg(args.b)] < registers[Inst.reg(args.a)]) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_LT_S export const branch_lt_s: InstructionRun = (r, args, registers) => { - if (i64(registers[reg(args.b)]) < i64(registers[reg(args.a)])) { - return staticJump(r, args.c); + if (i64(registers[Inst.reg(args.b)]) < i64(registers[Inst.reg(args.a)])) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_GE_U export const branch_ge_u: InstructionRun = (r, args, registers) => { - if (registers[reg(args.b)] >= registers[reg(args.a)]) { - return staticJump(r, args.c); + if (registers[Inst.reg(args.b)] >= registers[Inst.reg(args.a)]) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; // BRANCH_GE_S export const branch_ge_s: InstructionRun = (r, args, registers) => { - if (i64(registers[reg(args.b)]) >= i64(registers[reg(args.a)])) { - return staticJump(r, args.c); + if (i64(registers[Inst.reg(args.b)]) >= i64(registers[Inst.reg(args.a)])) { + return OutcomeData.staticJump(r, args.c); } - return ok(r); + return OutcomeData.ok(r); }; diff --git a/assembly/instructions/jump.ts b/assembly/instructions/jump.ts index 71400d5..a498dc6 100644 --- a/assembly/instructions/jump.ts +++ b/assembly/instructions/jump.ts @@ -1,25 +1,25 @@ import { portable } from "../portable"; -import { dJump, InstructionRun, staticJump } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; // JUMP -export const jump: InstructionRun = (r, args) => staticJump(r, args.a); +export const jump: InstructionRun = (r, args) => OutcomeData.staticJump(r, args.a); // JUMP_IND export const jump_ind: InstructionRun = (r, args, registers) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.b))); - return dJump(r, address); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.b))); + return OutcomeData.dJump(r, address); }; // LOAD_IMM_JUMP export const load_imm_jump: InstructionRun = (r, args, registers) => { - registers[reg(args.a)] = u32SignExtend(args.b); - return staticJump(r, args.c); + registers[Inst.reg(args.a)] = Inst.u32SignExtend(args.b); + return OutcomeData.staticJump(r, args.c); }; // LOAD_IMM_JUMP_IND export const load_imm_jump_ind: InstructionRun = (r, args, registers) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.d))); - registers[reg(args.b)] = u32SignExtend(args.c); - return dJump(r, address); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.d))); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(args.c); + return OutcomeData.dJump(r, address); }; diff --git a/assembly/instructions/load.ts b/assembly/instructions/load.ts index 7b0a7e7..b510767 100644 --- a/assembly/instructions/load.ts +++ b/assembly/instructions/load.ts @@ -1,151 +1,151 @@ import { MaybePageFault } from "../memory"; import { portable } from "../portable"; -import { InstructionRun, ok, okOrFault } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; const faultRes: MaybePageFault = new MaybePageFault(); // LOAD_IMM_64 export const load_imm_64: InstructionRun = (r, args, registers) => { - registers[reg(args.a)] = portable.u64_add(u64(args.b), u64(args.c) << u64(32)); - return ok(r); + registers[Inst.reg(args.a)] = portable.u64_add(u64(args.b), u64(args.c) << u64(32)); + return OutcomeData.ok(r); }; // LOAD_IMM export const load_imm: InstructionRun = (r, args, registers) => { - registers[reg(args.a)] = u32SignExtend(args.b); - return ok(r); + registers[Inst.reg(args.a)] = Inst.u32SignExtend(args.b); + return OutcomeData.ok(r); }; // LOAD_U8 export const load_u8: InstructionRun = (r, args, registers, memory) => { const result = memory.getU8(faultRes, args.b); if (!faultRes.isFault) { - registers[reg(args.a)] = result; + registers[Inst.reg(args.a)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_I8 export const load_i8: InstructionRun = (r, args, registers, memory) => { const result = memory.getI8(faultRes, args.b); if (!faultRes.isFault) { - registers[reg(args.a)] = result; + registers[Inst.reg(args.a)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_U16 export const load_u16: InstructionRun = (r, args, registers, memory) => { const result = memory.getU16(faultRes, args.b); if (!faultRes.isFault) { - registers[reg(args.a)] = result; + registers[Inst.reg(args.a)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_I16 export const load_i16: InstructionRun = (r, args, registers, memory) => { const result = memory.getI16(faultRes, args.b); if (!faultRes.isFault) { - registers[reg(args.a)] = result; + registers[Inst.reg(args.a)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_U32 export const load_u32: InstructionRun = (r, args, registers, memory) => { const result = memory.getU32(faultRes, args.b); if (!faultRes.isFault) { - registers[reg(args.a)] = result; + registers[Inst.reg(args.a)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_I32 export const load_i32: InstructionRun = (r, args, registers, memory) => { const result = memory.getI32(faultRes, args.b); if (!faultRes.isFault) { - registers[reg(args.a)] = result; + registers[Inst.reg(args.a)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_U64 export const load_u64: InstructionRun = (r, args, registers, memory) => { const result = memory.getU64(faultRes, args.b); if (!faultRes.isFault) { - registers[reg(args.a)] = result; + registers[Inst.reg(args.a)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_IND_U8 export const load_ind_u8: InstructionRun = (r, args, registers, memory) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.c))); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c))); const result = memory.getU8(faultRes, address); if (!faultRes.isFault) { - registers[reg(args.b)] = result; + registers[Inst.reg(args.b)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_IND_I8 export const load_ind_i8: InstructionRun = (r, args, registers, memory) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.c))); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c))); const result = memory.getI8(faultRes, address); if (!faultRes.isFault) { - registers[reg(args.b)] = result; + registers[Inst.reg(args.b)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_IND_U16 export const load_ind_u16: InstructionRun = (r, args, registers, memory) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.c))); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c))); const result = memory.getU16(faultRes, address); if (!faultRes.isFault) { - registers[reg(args.b)] = result; + registers[Inst.reg(args.b)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_IND_I16 export const load_ind_i16: InstructionRun = (r, args, registers, memory) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.c))); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c))); const result = memory.getI16(faultRes, address); if (!faultRes.isFault) { - registers[reg(args.b)] = result; + registers[Inst.reg(args.b)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_IND_U32 export const load_ind_u32: InstructionRun = (r, args, registers, memory) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.c))); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c))); const result = memory.getU32(faultRes, address); if (!faultRes.isFault) { - registers[reg(args.b)] = result; + registers[Inst.reg(args.b)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_IND_I32 export const load_ind_i32: InstructionRun = (r, args, registers, memory) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.c))); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c))); const result = memory.getI32(faultRes, address); if (!faultRes.isFault) { - registers[reg(args.b)] = result; + registers[Inst.reg(args.b)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // LOAD_IND_U64 export const load_ind_u64: InstructionRun = (r, args, registers, memory) => { - const address = u32(portable.u64_add(registers[reg(args.a)], u32SignExtend(args.c))); + const address = u32(portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c))); const result = memory.getU64(faultRes, u32(address)); if (!faultRes.isFault) { - registers[reg(args.b)] = result; + registers[Inst.reg(args.b)] = result; } - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; diff --git a/assembly/instructions/logic.test.ts b/assembly/instructions/logic.test.ts index ad25689..11fdb26 100644 --- a/assembly/instructions/logic.test.ts +++ b/assembly/instructions/logic.test.ts @@ -4,7 +4,7 @@ import { newRegisters } from "../registers"; import { Assert, Test, test } from "../test"; import { and_inv, or_inv, xnor } from "./logic"; import { Outcome, OutcomeData } from "./outcome"; -import { reg } from "./utils"; +import { Inst } from "./utils"; export const TESTS: Test[] = [ test("and_inv", () => { @@ -15,8 +15,8 @@ export const TESTS: Test[] = [ args.b = 0x1; args.c = 0x3; const regs = newRegisters(); - regs[reg(args.a)] = 0x0000_0000_000f; - regs[reg(args.b)] = 0xf000_0000_0001; + regs[Inst.reg(args.a)] = 0x0000_0000_000f; + regs[Inst.reg(args.b)] = 0xf000_0000_0001; const memo = new MemoryBuilder().build(); @@ -26,7 +26,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], 0x0000_f000_0000_0000); + assert.isEqual(regs[Inst.reg(args.c)], 0x0000_f000_0000_0000); return assert; }), test("or_inv", () => { @@ -37,8 +37,8 @@ export const TESTS: Test[] = [ args.b = 0x1; args.c = 0x3; const regs = newRegisters(); - regs[reg(args.a)] = 0x0000_0000_000f; - regs[reg(args.b)] = 0xf000_0000_0001; + regs[Inst.reg(args.a)] = 0x0000_0000_000f; + regs[Inst.reg(args.b)] = 0xf000_0000_0001; const memo = new MemoryBuilder().build(); @@ -48,7 +48,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], 0xffff_ffff_ffff_fff1); + assert.isEqual(regs[Inst.reg(args.c)], 0xffff_ffff_ffff_fff1); return assert; }), test("xnor", () => { @@ -59,8 +59,8 @@ export const TESTS: Test[] = [ args.b = 0x1; args.c = 0x3; const regs = newRegisters(); - regs[reg(args.a)] = 0x0000_0000_000f; - regs[reg(args.b)] = 0xf000_0000_0000; + regs[Inst.reg(args.a)] = 0x0000_0000_000f; + regs[Inst.reg(args.b)] = 0xf000_0000_0000; const memo = new MemoryBuilder().build(); @@ -70,7 +70,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], 0xffff_0fff_ffff_fff0); + assert.isEqual(regs[Inst.reg(args.c)], 0xffff_0fff_ffff_fff0); return assert; }), ]; diff --git a/assembly/instructions/logic.ts b/assembly/instructions/logic.ts index 2b37413..7d4eaa4 100644 --- a/assembly/instructions/logic.ts +++ b/assembly/instructions/logic.ts @@ -1,56 +1,56 @@ -import { InstructionRun, ok } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; // AND_IMM export const and_imm: InstructionRun = (r, args, registers) => { - registers[reg(args.b)] = registers[reg(args.a)] & u32SignExtend(args.c); - return ok(r); + registers[Inst.reg(args.b)] = registers[Inst.reg(args.a)] & Inst.u32SignExtend(args.c); + return OutcomeData.ok(r); }; // XOR_IMM export const xor_imm: InstructionRun = (r, args, registers) => { - registers[reg(args.b)] = registers[reg(args.a)] ^ u32SignExtend(args.c); - return ok(r); + registers[Inst.reg(args.b)] = registers[Inst.reg(args.a)] ^ Inst.u32SignExtend(args.c); + return OutcomeData.ok(r); }; // OR_IMM export const or_imm: InstructionRun = (r, args, registers) => { - registers[reg(args.b)] = registers[reg(args.a)] | u32SignExtend(args.c); - return ok(r); + registers[Inst.reg(args.b)] = registers[Inst.reg(args.a)] | Inst.u32SignExtend(args.c); + return OutcomeData.ok(r); }; // AND export const and: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = registers[reg(args.b)] & registers[reg(args.a)]; - return ok(r); + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)] & registers[Inst.reg(args.a)]; + return OutcomeData.ok(r); }; // XOR export const xor: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = registers[reg(args.b)] ^ registers[reg(args.a)]; - return ok(r); + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)] ^ registers[Inst.reg(args.a)]; + return OutcomeData.ok(r); }; // OR export const or: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = registers[reg(args.b)] | registers[reg(args.a)]; - return ok(r); + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)] | registers[Inst.reg(args.a)]; + return OutcomeData.ok(r); }; // AND_INV export const and_inv: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = registers[reg(args.b)] & ~registers[reg(args.a)]; - return ok(r); + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)] & ~registers[Inst.reg(args.a)]; + return OutcomeData.ok(r); }; // OR_INV export const or_inv: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = u64(registers[reg(args.b)] | ~registers[reg(args.a)]); - return ok(r); + registers[Inst.reg(args.c)] = u64(registers[Inst.reg(args.b)] | ~registers[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // XNOR export const xnor: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = u64(~(registers[reg(args.b)] ^ registers[reg(args.a)])); - return ok(r); + registers[Inst.reg(args.c)] = u64(~(registers[Inst.reg(args.b)] ^ registers[Inst.reg(args.a)])); + return OutcomeData.ok(r); }; diff --git a/assembly/instructions/math.test.ts b/assembly/instructions/math.test.ts index 675e713..48bf944 100644 --- a/assembly/instructions/math.test.ts +++ b/assembly/instructions/math.test.ts @@ -4,7 +4,7 @@ import { newRegisters } from "../registers"; import { Assert, Test, test } from "../test"; import * as math from "./math"; import { Outcome, OutcomeData } from "./outcome"; -import { reg } from "./utils"; +import { Inst } from "./utils"; export const TESTS: Test[] = [ test("max", () => { @@ -12,8 +12,8 @@ export const TESTS: Test[] = [ const r = new OutcomeData(); const args = new Args().fill(0x0, 0x1, 0x3); const regs = newRegisters(); - regs[reg(args.a)] = -(2 ** 63); - regs[reg(args.b)] = 2; + regs[Inst.reg(args.a)] = -(2 ** 63); + regs[Inst.reg(args.b)] = 2; const memo = new MemoryBuilder().build(); @@ -23,7 +23,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], 2); + assert.isEqual(regs[Inst.reg(args.c)], 2); return assert; }), test("max_u", () => { @@ -31,8 +31,8 @@ export const TESTS: Test[] = [ const r = new OutcomeData(); const args = new Args().fill(0x0, 0x1, 0x3); const regs = newRegisters(); - regs[reg(args.a)] = -(2 ** 63); - regs[reg(args.b)] = 2; + regs[Inst.reg(args.a)] = -(2 ** 63); + regs[Inst.reg(args.b)] = 2; const memo = new MemoryBuilder().build(); @@ -42,7 +42,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], -(2 ** 63)); + assert.isEqual(regs[Inst.reg(args.c)], -(2 ** 63)); return assert; }), test("min", () => { @@ -50,8 +50,8 @@ export const TESTS: Test[] = [ const r = new OutcomeData(); const args = new Args().fill(0x0, 0x1, 0x3); const regs = newRegisters(); - regs[reg(args.a)] = -(2 ** 63); - regs[reg(args.b)] = 2; + regs[Inst.reg(args.a)] = -(2 ** 63); + regs[Inst.reg(args.b)] = 2; const memo = new MemoryBuilder().build(); @@ -61,7 +61,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], -(2 ** 63)); + assert.isEqual(regs[Inst.reg(args.c)], -(2 ** 63)); return assert; }), test("min_u", () => { @@ -69,8 +69,8 @@ export const TESTS: Test[] = [ const r = new OutcomeData(); const args = new Args().fill(0x0, 0x1, 0x3); const regs = newRegisters(); - regs[reg(args.a)] = -(2 ** 63); - regs[reg(args.b)] = 2; + regs[Inst.reg(args.a)] = -(2 ** 63); + regs[Inst.reg(args.b)] = 2; const memo = new MemoryBuilder().build(); @@ -80,7 +80,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], 2); + assert.isEqual(regs[Inst.reg(args.c)], 2); return assert; }), test("add_32", () => { @@ -88,8 +88,8 @@ export const TESTS: Test[] = [ const r = new OutcomeData(); const args = new Args().fill(0x0, 0x1, 0x3); const regs = newRegisters(); - regs[reg(args.a)] = 2 ** 64 - 1; - regs[reg(args.b)] = 2 ** 64 - 1; + regs[Inst.reg(args.a)] = 2 ** 64 - 1; + regs[Inst.reg(args.b)] = 2 ** 64 - 1; const memo = new MemoryBuilder().build(); @@ -99,7 +99,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], 0xffff_ffff_ffff_fffe); + assert.isEqual(regs[Inst.reg(args.c)], 0xffff_ffff_ffff_fffe); return assert; }), ]; diff --git a/assembly/instructions/math.ts b/assembly/instructions/math.ts index 193dccc..6e06764 100644 --- a/assembly/instructions/math.ts +++ b/assembly/instructions/math.ts @@ -1,240 +1,242 @@ import { portable } from "../portable"; -import { InstructionRun, ok } from "./outcome"; -import { mulUpperSigned, mulUpperSignedUnsigned, mulUpperUnsigned, reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst, mulUpperSigned, mulUpperSignedUnsigned, mulUpperUnsigned } from "./utils"; // ADD_IMM_32 export const add_imm_32: InstructionRun = (r, args, registers) => { - const a = registers[reg(args.a)]; - const c = u32SignExtend(args.c); - registers[reg(args.b)] = u32SignExtend(u32(portable.u64_add(a, c))); - return ok(r); + const a = registers[Inst.reg(args.a)]; + const c = Inst.u32SignExtend(args.c); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(u32(portable.u64_add(a, c))); + return OutcomeData.ok(r); }; // MUL_IMM_32 export const mul_imm_32: InstructionRun = (r, args, registers) => { - registers[reg(args.b)] = u32SignExtend(u32(portable.u64_mul(registers[reg(args.a)], u64(args.c)))); - return ok(r); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(u32(portable.u64_mul(registers[Inst.reg(args.a)], u64(args.c)))); + return OutcomeData.ok(r); }; // NEG_ADD_IMM_32 export const neg_add_imm_32: InstructionRun = (r, args, registers) => { - const sum = portable.u64_sub(u64(args.c) | u64(0x1_0000_0000), registers[reg(args.a)]); - registers[reg(args.b)] = u32SignExtend(u32(sum)); - return ok(r); + const sum = portable.u64_sub(u64(args.c) | u64(0x1_0000_0000), registers[Inst.reg(args.a)]); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(u32(sum)); + return OutcomeData.ok(r); }; // ADD_IMM export const add_imm: InstructionRun = (r, args, registers) => { - const sum: u64 = portable.u64_add(registers[reg(args.a)], u32SignExtend(args.c)); - registers[reg(args.b)] = sum; - return ok(r); + const sum: u64 = portable.u64_add(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c)); + registers[Inst.reg(args.b)] = sum; + return OutcomeData.ok(r); }; // MUL_IMM export const mul_imm: InstructionRun = (r, args, registers) => { - registers[reg(args.b)] = portable.u64_mul(registers[reg(args.a)], u32SignExtend(args.c)); - return ok(r); + registers[Inst.reg(args.b)] = portable.u64_mul(registers[Inst.reg(args.a)], Inst.u32SignExtend(args.c)); + return OutcomeData.ok(r); }; // NEG_ADD_IMM export const neg_add_imm: InstructionRun = (r, args, registers) => { - const sum = portable.u64_sub(u32SignExtend(args.c), registers[reg(args.a)]); - registers[reg(args.b)] = sum; - return ok(r); + const sum = portable.u64_sub(Inst.u32SignExtend(args.c), registers[Inst.reg(args.a)]); + registers[Inst.reg(args.b)] = sum; + return OutcomeData.ok(r); }; // ADD_32 export const add_32: InstructionRun = (r, args, registers) => { - const a = u32(registers[reg(args.a)]); - const b = u32(registers[reg(args.b)]); - registers[reg(args.c)] = u32SignExtend(a + b); - return ok(r); + const a = u32(registers[Inst.reg(args.a)]); + const b = u32(registers[Inst.reg(args.b)]); + registers[Inst.reg(args.c)] = Inst.u32SignExtend(a + b); + return OutcomeData.ok(r); }; // SUB_32 export const sub_32: InstructionRun = (r, args, registers) => { - const a = registers[reg(args.b)]; - const b = u64(0x1_0000_0000 - u32(registers[reg(args.a)])); - registers[reg(args.c)] = u32SignExtend(u32(portable.u64_add(a, b))); - return ok(r); + const a = registers[Inst.reg(args.b)]; + const b = u64(0x1_0000_0000 - u32(registers[Inst.reg(args.a)])); + registers[Inst.reg(args.c)] = Inst.u32SignExtend(u32(portable.u64_add(a, b))); + return OutcomeData.ok(r); }; // MUL_32 export const mul_32: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = u32SignExtend(u32(portable.u64_mul(registers[reg(args.a)], registers[reg(args.b)]))); - return ok(r); + registers[Inst.reg(args.c)] = Inst.u32SignExtend( + u32(portable.u64_mul(registers[Inst.reg(args.a)], registers[Inst.reg(args.b)])), + ); + return OutcomeData.ok(r); }; // DIV_U_32 export const div_u_32: InstructionRun = (r, args, registers) => { - const a = u32(registers[reg(args.a)]); + const a = u32(registers[Inst.reg(args.a)]); if (a === 0) { - registers[reg(args.c)] = u64.MAX_VALUE; + registers[Inst.reg(args.c)] = u64.MAX_VALUE; } else { - const b = u32(registers[reg(args.b)]); - registers[reg(args.c)] = u32SignExtend(b / a); + const b = u32(registers[Inst.reg(args.b)]); + registers[Inst.reg(args.c)] = Inst.u32SignExtend(b / a); } - return ok(r); + return OutcomeData.ok(r); }; // DIV_S_32 export const div_s_32: InstructionRun = (r, args, registers) => { - const b = i64(u32SignExtend(u32(registers[reg(args.b)]))); - const a = i64(u32SignExtend(u32(registers[reg(args.a)]))); + const b = i64(Inst.u32SignExtend(u32(registers[Inst.reg(args.b)]))); + const a = i64(Inst.u32SignExtend(u32(registers[Inst.reg(args.a)]))); if (a === i64(0)) { - registers[reg(args.c)] = u64.MAX_VALUE; + registers[Inst.reg(args.c)] = u64.MAX_VALUE; } else if (a === i64(-1) && b === i64(i32.MIN_VALUE)) { - registers[reg(args.c)] = u64(b); + registers[Inst.reg(args.c)] = u64(b); } else { - registers[reg(args.c)] = u64(b / a); + registers[Inst.reg(args.c)] = u64(b / a); } - return ok(r); + return OutcomeData.ok(r); }; // REM_U_32 export const rem_u_32: InstructionRun = (r, args, registers) => { - const a = u32(registers[reg(args.a)]); - const b = u32(registers[reg(args.b)]); + const a = u32(registers[Inst.reg(args.a)]); + const b = u32(registers[Inst.reg(args.b)]); if (a === 0) { - registers[reg(args.c)] = u32SignExtend(b); + registers[Inst.reg(args.c)] = Inst.u32SignExtend(b); } else { - registers[reg(args.c)] = u32SignExtend(b % a); + registers[Inst.reg(args.c)] = Inst.u32SignExtend(b % a); } - return ok(r); + return OutcomeData.ok(r); }; // REM_S_32 export const rem_s_32: InstructionRun = (r, args, registers) => { - const b = i32(registers[reg(args.b)]); - const a = i32(registers[reg(args.a)]); + const b = i32(registers[Inst.reg(args.b)]); + const a = i32(registers[Inst.reg(args.a)]); if (a === 0) { - registers[reg(args.c)] = u64(i64(b)); + registers[Inst.reg(args.c)] = u64(i64(b)); } else if (a === -1 && b === i32.MIN_VALUE) { - registers[reg(args.c)] = u64(0); + registers[Inst.reg(args.c)] = u64(0); } else { - registers[reg(args.c)] = u64(i64(b) % i64(a)); + registers[Inst.reg(args.c)] = u64(i64(b) % i64(a)); } - return ok(r); + return OutcomeData.ok(r); }; // ADD_64 export const add_64: InstructionRun = (r, args, registers) => { - const a = registers[reg(args.a)]; - const b = registers[reg(args.b)]; - registers[reg(args.c)] = portable.u64_add(a, b); - return ok(r); + const a = registers[Inst.reg(args.a)]; + const b = registers[Inst.reg(args.b)]; + registers[Inst.reg(args.c)] = portable.u64_add(a, b); + return OutcomeData.ok(r); }; // SUB export const sub: InstructionRun = (r, args, registers) => { - const a = registers[reg(args.a)]; - const b = registers[reg(args.b)]; - registers[reg(args.c)] = portable.u64_sub(b, a); - return ok(r); + const a = registers[Inst.reg(args.a)]; + const b = registers[Inst.reg(args.b)]; + registers[Inst.reg(args.c)] = portable.u64_sub(b, a); + return OutcomeData.ok(r); }; // MUL export const mul: InstructionRun = (r, args, registers) => { - const a = registers[reg(args.a)]; - const b = registers[reg(args.b)]; - registers[reg(args.c)] = portable.u64_mul(a, b); - return ok(r); + const a = registers[Inst.reg(args.a)]; + const b = registers[Inst.reg(args.b)]; + registers[Inst.reg(args.c)] = portable.u64_mul(a, b); + return OutcomeData.ok(r); }; // DIV_U export const div_u: InstructionRun = (r, args, registers) => { - if (registers[reg(args.a)] === u64(0)) { - registers[reg(args.c)] = u64.MAX_VALUE; + if (registers[Inst.reg(args.a)] === u64(0)) { + registers[Inst.reg(args.c)] = u64.MAX_VALUE; } else { - registers[reg(args.c)] = registers[reg(args.b)] / registers[reg(args.a)]; + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)] / registers[Inst.reg(args.a)]; } - return ok(r); + return OutcomeData.ok(r); }; // DIV_S export const div_s: InstructionRun = (r, args, registers) => { - const b = i64(registers[reg(args.b)]); - const a = i64(registers[reg(args.a)]); + const b = i64(registers[Inst.reg(args.b)]); + const a = i64(registers[Inst.reg(args.a)]); if (a === i64(0)) { - registers[reg(args.c)] = u64.MAX_VALUE; + registers[Inst.reg(args.c)] = u64.MAX_VALUE; } else if (a === i64(-1) && b === i64.MIN_VALUE) { - registers[reg(args.c)] = u64(b); + registers[Inst.reg(args.c)] = u64(b); } else { - registers[reg(args.c)] = u64(b / a); + registers[Inst.reg(args.c)] = u64(b / a); } - return ok(r); + return OutcomeData.ok(r); }; // REM_U export const rem_u: InstructionRun = (r, args, registers) => { - if (registers[reg(args.a)] === u64(0)) { - registers[reg(args.c)] = registers[reg(args.b)]; + if (registers[Inst.reg(args.a)] === u64(0)) { + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)]; } else { - registers[reg(args.c)] = registers[reg(args.b)] % registers[reg(args.a)]; + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)] % registers[Inst.reg(args.a)]; } - return ok(r); + return OutcomeData.ok(r); }; // REM_S export const rem_s: InstructionRun = (r, args, registers) => { - const b = i64(registers[reg(args.b)]); - const a = i64(registers[reg(args.a)]); + const b = i64(registers[Inst.reg(args.b)]); + const a = i64(registers[Inst.reg(args.a)]); if (a === i64(0)) { - registers[reg(args.c)] = u64(b); + registers[Inst.reg(args.c)] = u64(b); } else if (a === i64(-1) && b === i64.MIN_VALUE) { - registers[reg(args.c)] = u64(0); + registers[Inst.reg(args.c)] = u64(0); } else { - registers[reg(args.c)] = u64(b % a); + registers[Inst.reg(args.c)] = u64(b % a); } - return ok(r); + return OutcomeData.ok(r); }; // MUL_UPPER_S_S export const mul_upper_s_s: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = mulUpperSigned(i64(registers[reg(args.b)]), i64(registers[reg(args.a)])); - return ok(r); + registers[Inst.reg(args.c)] = mulUpperSigned(i64(registers[Inst.reg(args.b)]), i64(registers[Inst.reg(args.a)])); + return OutcomeData.ok(r); }; // MUL_UPPER_U_U export const mul_upper_u_u: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = mulUpperUnsigned(registers[reg(args.b)], registers[reg(args.a)]); - return ok(r); + registers[Inst.reg(args.c)] = mulUpperUnsigned(registers[Inst.reg(args.b)], registers[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // MUL_UPPER_S_U export const mul_upper_s_u: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = mulUpperSignedUnsigned(i64(registers[reg(args.b)]), registers[reg(args.a)]); - return ok(r); + registers[Inst.reg(args.c)] = mulUpperSignedUnsigned(i64(registers[Inst.reg(args.b)]), registers[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // MAX export const max: InstructionRun = (r, args, registers) => { - const a = i64(registers[reg(args.a)]); - const b = i64(registers[reg(args.b)]); - registers[reg(args.c)] = u64(a < b ? b : a); - return ok(r); + const a = i64(registers[Inst.reg(args.a)]); + const b = i64(registers[Inst.reg(args.b)]); + registers[Inst.reg(args.c)] = u64(a < b ? b : a); + return OutcomeData.ok(r); }; // MAX_U export const max_u: InstructionRun = (r, args, registers) => { - const a = registers[reg(args.a)]; - const b = registers[reg(args.b)]; - registers[reg(args.c)] = a < b ? b : a; - return ok(r); + const a = registers[Inst.reg(args.a)]; + const b = registers[Inst.reg(args.b)]; + registers[Inst.reg(args.c)] = a < b ? b : a; + return OutcomeData.ok(r); }; // MIN export const min: InstructionRun = (r, args, registers) => { - const a = i64(registers[reg(args.a)]); - const b = i64(registers[reg(args.b)]); - registers[reg(args.c)] = u64(a > b ? b : a); - return ok(r); + const a = i64(registers[Inst.reg(args.a)]); + const b = i64(registers[Inst.reg(args.b)]); + registers[Inst.reg(args.c)] = u64(a > b ? b : a); + return OutcomeData.ok(r); }; // MIN_U export const min_u: InstructionRun = (r, args, registers) => { - const a = registers[reg(args.a)]; - const b = registers[reg(args.b)]; - registers[reg(args.c)] = a > b ? b : a; - return ok(r); + const a = registers[Inst.reg(args.a)]; + const b = registers[Inst.reg(args.b)]; + registers[Inst.reg(args.c)] = a > b ? b : a; + return OutcomeData.ok(r); }; diff --git a/assembly/instructions/misc.ts b/assembly/instructions/misc.ts index f39377d..9ccb915 100644 --- a/assembly/instructions/misc.ts +++ b/assembly/instructions/misc.ts @@ -1,28 +1,28 @@ import { MaybePageFault } from "../memory"; -import { hostCall, InstructionRun, ok, okOrFault, panic } from "./outcome"; -import { reg } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; const faultRes = new MaybePageFault(); // INVALID -export const INVALID: InstructionRun = (r) => panic(r); +export const INVALID: InstructionRun = (r) => OutcomeData.panic(r); // TRAP -export const trap: InstructionRun = (r) => panic(r); +export const trap: InstructionRun = (r) => OutcomeData.panic(r); // FALLTHROUGH -export const fallthrough: InstructionRun = (r) => ok(r); +export const fallthrough: InstructionRun = (r) => OutcomeData.ok(r); // ECALLI -export const ecalli: InstructionRun = (r, args) => hostCall(r, args.a); +export const ecalli: InstructionRun = (r, args) => OutcomeData.hostCall(r, args.a); // SBRK export const sbrk: InstructionRun = (r, args, registers, memory) => { - const res = memory.sbrk(faultRes, u32(registers[reg(args.a)])); + const res = memory.sbrk(faultRes, u32(registers[Inst.reg(args.a)])); // out of memory if (faultRes.isFault) { - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); } - registers[reg(args.b)] = res; - return ok(r); + registers[Inst.reg(args.b)] = res; + return OutcomeData.ok(r); }; diff --git a/assembly/instructions/mov.ts b/assembly/instructions/mov.ts index f0cb96f..cce4004 100644 --- a/assembly/instructions/mov.ts +++ b/assembly/instructions/mov.ts @@ -1,40 +1,40 @@ -import { InstructionRun, ok } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; // MOVE_REG export const move_reg: InstructionRun = (r, args, registers) => { - registers[reg(args.b)] = registers[reg(args.a)]; - return ok(r); + registers[Inst.reg(args.b)] = registers[Inst.reg(args.a)]; + return OutcomeData.ok(r); }; // CMOV_IZ_IMM export const cmov_iz_imm: InstructionRun = (r, args, registers) => { - if (registers[reg(args.a)] === u64(0)) { - registers[reg(args.b)] = u32SignExtend(args.c); + if (registers[Inst.reg(args.a)] === u64(0)) { + registers[Inst.reg(args.b)] = Inst.u32SignExtend(args.c); } - return ok(r); + return OutcomeData.ok(r); }; // CMOV_NZ_IMM export const cmov_nz_imm: InstructionRun = (r, args, registers) => { - if (registers[reg(args.a)] !== u64(0)) { - registers[reg(args.b)] = u32SignExtend(args.c); + if (registers[Inst.reg(args.a)] !== u64(0)) { + registers[Inst.reg(args.b)] = Inst.u32SignExtend(args.c); } - return ok(r); + return OutcomeData.ok(r); }; // CMOV_IZ export const cmov_iz: InstructionRun = (r, args, registers) => { - if (registers[reg(args.a)] === u64(0)) { - registers[reg(args.c)] = registers[reg(args.b)]; + if (registers[Inst.reg(args.a)] === u64(0)) { + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)]; } - return ok(r); + return OutcomeData.ok(r); }; // CMOV_NZ export const cmov_nz: InstructionRun = (r, args, registers) => { - if (registers[reg(args.a)] !== u64(0)) { - registers[reg(args.c)] = registers[reg(args.b)]; + if (registers[Inst.reg(args.a)] !== u64(0)) { + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)]; } - return ok(r); + return OutcomeData.ok(r); }; diff --git a/assembly/instructions/outcome.ts b/assembly/instructions/outcome.ts index b1a0bcc..44c23aa 100644 --- a/assembly/instructions/outcome.ts +++ b/assembly/instructions/outcome.ts @@ -24,49 +24,56 @@ export class OutcomeData { dJump: u32 = 0; result: Result = Result.PANIC; exitCode: u32 = 0; -} - -export function status(r: OutcomeData, result: Result): OutcomeData { - r.outcome = Outcome.Result; - r.result = result; - return r; -} -export function staticJump(r: OutcomeData, offset: i32): OutcomeData { - r.outcome = Outcome.StaticJump; - r.staticJump = offset; - return r; -} + @inline + static status(r: OutcomeData, result: Result): OutcomeData { + r.outcome = Outcome.Result; + r.result = result; + return r; + } -export function dJump(r: OutcomeData, address: u32): OutcomeData { - r.outcome = Outcome.DynamicJump; - r.dJump = address; - return r; -} + @inline + static staticJump(r: OutcomeData, offset: i32): OutcomeData { + r.outcome = Outcome.StaticJump; + r.staticJump = offset; + return r; + } -export function ok(r: OutcomeData): OutcomeData { - // outcome is already pre-set to Ok by the interpreter loop - return r; -} + @inline + static dJump(r: OutcomeData, address: u32): OutcomeData { + r.outcome = Outcome.DynamicJump; + r.dJump = address; + return r; + } -export function panic(r: OutcomeData): OutcomeData { - return status(r, Result.PANIC); -} + @inline + static ok(r: OutcomeData): OutcomeData { + // outcome is already pre-set to Ok by the interpreter loop + return r; + } -export function hostCall(r: OutcomeData, id: u32): OutcomeData { - r.outcome = Outcome.Result; - r.result = Result.HOST; - r.exitCode = id; - return r; -} + @inline + static panic(r: OutcomeData): OutcomeData { + return OutcomeData.status(r, Result.PANIC); + } -export function okOrFault(r: OutcomeData, pageFault: MaybePageFault): OutcomeData { - if (pageFault.isFault) { + @inline + static hostCall(r: OutcomeData, id: u32): OutcomeData { r.outcome = Outcome.Result; - // not accessible memory does not result in `FAULT`, but rather goes straight to TRAP - // yet in gas calculations we still subtract 1, unlike TRAP, but like FAULT. - r.result = pageFault.isAccess ? Result.FAULT_ACCESS : Result.FAULT; - r.exitCode = pageFault.fault; + r.result = Result.HOST; + r.exitCode = id; + return r; + } + + @inline + static okOrFault(r: OutcomeData, pageFault: MaybePageFault): OutcomeData { + if (pageFault.isFault) { + r.outcome = Outcome.Result; + // not accessible memory does not result in `FAULT`, but rather goes straight to TRAP + // yet in gas calculations we still subtract 1, unlike TRAP, but like FAULT. + r.result = pageFault.isAccess ? Result.FAULT_ACCESS : Result.FAULT; + r.exitCode = pageFault.fault; + } + return r; } - return r; } diff --git a/assembly/instructions/rot.test.ts b/assembly/instructions/rot.test.ts index bc8ce75..f37105f 100644 --- a/assembly/instructions/rot.test.ts +++ b/assembly/instructions/rot.test.ts @@ -4,7 +4,7 @@ import { newRegisters } from "../registers"; import { Assert, Test, test } from "../test"; import { Outcome, OutcomeData } from "./outcome"; import { rot_l_32, rot_l_64, rot_r_64_imm, rot_r_64_imm_alt } from "./rot"; -import { reg } from "./utils"; +import { Inst } from "./utils"; export const TESTS: Test[] = [ test("rot_r_64_imm", () => { @@ -15,7 +15,7 @@ export const TESTS: Test[] = [ args.b = 0x1; args.c = 0x8; const regs = newRegisters(); - regs[reg(args.a)] = 0xdead_beef; + regs[Inst.reg(args.a)] = 0xdead_beef; const memo = new MemoryBuilder().build(); @@ -25,7 +25,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.b)], 0xef00_0000_00de_adbe); + assert.isEqual(regs[Inst.reg(args.b)], 0xef00_0000_00de_adbe); return assert; }), test("rot_r_64_imm_alt", () => { @@ -36,7 +36,7 @@ export const TESTS: Test[] = [ args.b = 0x1; args.c = 0xdead_beef; const regs = newRegisters(); - regs[reg(args.a)] = 0x8; + regs[Inst.reg(args.a)] = 0x8; const memo = new MemoryBuilder().build(); @@ -46,7 +46,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.b)], 0xefff_ffff_ffde_adbe); + assert.isEqual(regs[Inst.reg(args.b)], 0xefff_ffff_ffde_adbe); return assert; }), test("rot_l_64", () => { @@ -57,8 +57,8 @@ export const TESTS: Test[] = [ args.b = 0x1; args.c = 0x8; const regs = newRegisters(); - regs[reg(args.a)] = 0x8; - regs[reg(args.b)] = 0xdead_beef; + regs[Inst.reg(args.a)] = 0x8; + regs[Inst.reg(args.b)] = 0xdead_beef; const memo = new MemoryBuilder().build(); @@ -68,7 +68,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], 0x0000_00de_adbe_ef00); + assert.isEqual(regs[Inst.reg(args.c)], 0x0000_00de_adbe_ef00); return assert; }), test("rot_l_32", () => { @@ -79,8 +79,8 @@ export const TESTS: Test[] = [ args.b = 0x1; args.c = 0x8; const regs = newRegisters(); - regs[reg(args.a)] = 0x8; - regs[reg(args.b)] = 0xdead_beef; + regs[Inst.reg(args.a)] = 0x8; + regs[Inst.reg(args.b)] = 0xdead_beef; const memo = new MemoryBuilder().build(); @@ -90,7 +90,7 @@ export const TESTS: Test[] = [ // then const assert = new Assert(); assert.isEqual(ret.outcome, Outcome.Ok, "outcome"); - assert.isEqual(regs[reg(args.c)], 0xffff_ffff_adbe_efde); + assert.isEqual(regs[Inst.reg(args.c)], 0xffff_ffff_adbe_efde); return assert; }), ]; diff --git a/assembly/instructions/rot.ts b/assembly/instructions/rot.ts index 2efddc2..dfef590 100644 --- a/assembly/instructions/rot.ts +++ b/assembly/instructions/rot.ts @@ -1,53 +1,53 @@ import { portable } from "../portable"; -import { InstructionRun, ok } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; // ROT_R_64_IMM export const rot_r_64_imm: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = math.rot_r(regs[reg(args.a)], u32SignExtend(args.c)); - return ok(r); + regs[Inst.reg(args.b)] = math.rot_r(regs[Inst.reg(args.a)], Inst.u32SignExtend(args.c)); + return OutcomeData.ok(r); }; // ROT_R_64_IMM_ALT export const rot_r_64_imm_alt: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = math.rot_r(u32SignExtend(args.c), regs[reg(args.a)]); - return ok(r); + regs[Inst.reg(args.b)] = math.rot_r(Inst.u32SignExtend(args.c), regs[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // ROT_R_32_IMM export const rot_r_32_imm: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = u32SignExtend(math.rot_r_32(u32(regs[reg(args.a)]), u32(args.c))); - return ok(r); + regs[Inst.reg(args.b)] = Inst.u32SignExtend(math.rot_r_32(u32(regs[Inst.reg(args.a)]), u32(args.c))); + return OutcomeData.ok(r); }; // ROT_R_32_IMM_ALT export const rot_r_32_imm_alt: InstructionRun = (r, args, regs) => { - regs[reg(args.b)] = u32SignExtend(math.rot_r_32(u32(args.c), u32(regs[reg(args.a)]))); - return ok(r); + regs[Inst.reg(args.b)] = Inst.u32SignExtend(math.rot_r_32(u32(args.c), u32(regs[Inst.reg(args.a)]))); + return OutcomeData.ok(r); }; // ROT_L_64 export const rot_l_64: InstructionRun = (r, args, regs) => { - regs[reg(args.c)] = math.rot_l(regs[reg(args.b)], regs[reg(args.a)]); - return ok(r); + regs[Inst.reg(args.c)] = math.rot_l(regs[Inst.reg(args.b)], regs[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // ROT_L_32 export const rot_l_32: InstructionRun = (r, args, regs) => { - regs[reg(args.c)] = u32SignExtend(math.rot_l_32(u32(regs[reg(args.b)]), u32(regs[reg(args.a)]))); - return ok(r); + regs[Inst.reg(args.c)] = Inst.u32SignExtend(math.rot_l_32(u32(regs[Inst.reg(args.b)]), u32(regs[Inst.reg(args.a)]))); + return OutcomeData.ok(r); }; // ROT_R_64 export const rot_r_64: InstructionRun = (r, args, regs) => { - regs[reg(args.c)] = math.rot_r(regs[reg(args.b)], regs[reg(args.a)]); - return ok(r); + regs[Inst.reg(args.c)] = math.rot_r(regs[Inst.reg(args.b)], regs[Inst.reg(args.a)]); + return OutcomeData.ok(r); }; // ROT_R_32 export const rot_r_32: InstructionRun = (r, args, regs) => { - regs[reg(args.c)] = u32SignExtend(math.rot_r_32(u32(regs[reg(args.b)]), u32(regs[reg(args.a)]))); - return ok(r); + regs[Inst.reg(args.c)] = Inst.u32SignExtend(math.rot_r_32(u32(regs[Inst.reg(args.b)]), u32(regs[Inst.reg(args.a)]))); + return OutcomeData.ok(r); }; export namespace math { diff --git a/assembly/instructions/set.ts b/assembly/instructions/set.ts index 777ab61..8af0a27 100644 --- a/assembly/instructions/set.ts +++ b/assembly/instructions/set.ts @@ -1,42 +1,42 @@ -import { InstructionRun, ok } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; // SET_LT_U_IMM export const set_lt_u_imm: InstructionRun = (r, args, registers) => { - const cond = registers[reg(args.a)] < u64(u32SignExtend(args.c)); - registers[reg(args.b)] = cond ? u64(1) : u64(0); - return ok(r); + const cond = registers[Inst.reg(args.a)] < u64(Inst.u32SignExtend(args.c)); + registers[Inst.reg(args.b)] = cond ? u64(1) : u64(0); + return OutcomeData.ok(r); }; // SET_LT_S_IMM export const set_lt_s_imm: InstructionRun = (r, args, registers) => { - const cond = i64(registers[reg(args.a)]) < i64(u32SignExtend(args.c)); - registers[reg(args.b)] = cond ? u64(1) : u64(0); - return ok(r); + const cond = i64(registers[Inst.reg(args.a)]) < i64(Inst.u32SignExtend(args.c)); + registers[Inst.reg(args.b)] = cond ? u64(1) : u64(0); + return OutcomeData.ok(r); }; // SET_GT_U_IMM export const set_gt_u_imm: InstructionRun = (r, args, registers) => { - const cond = registers[reg(args.a)] > u64(u32SignExtend(args.c)); - registers[reg(args.b)] = cond ? u64(1) : u64(0); - return ok(r); + const cond = registers[Inst.reg(args.a)] > u64(Inst.u32SignExtend(args.c)); + registers[Inst.reg(args.b)] = cond ? u64(1) : u64(0); + return OutcomeData.ok(r); }; // SET_GT_S_IMM export const set_gt_s_imm: InstructionRun = (r, args, registers) => { - const cond = i64(registers[reg(args.a)]) > i64(u32SignExtend(args.c)); - registers[reg(args.b)] = cond ? u64(1) : u64(0); - return ok(r); + const cond = i64(registers[Inst.reg(args.a)]) > i64(Inst.u32SignExtend(args.c)); + registers[Inst.reg(args.b)] = cond ? u64(1) : u64(0); + return OutcomeData.ok(r); }; // SET_LT_U export const set_lt_u: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = registers[reg(args.b)] < registers[reg(args.a)] ? u64(1) : u64(0); - return ok(r); + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)] < registers[Inst.reg(args.a)] ? u64(1) : u64(0); + return OutcomeData.ok(r); }; // SET_LT_S export const set_lt_s: InstructionRun = (r, args, registers) => { - registers[reg(args.c)] = i64(registers[reg(args.b)]) < i64(registers[reg(args.a)]) ? u64(1) : u64(0); - return ok(r); + registers[Inst.reg(args.c)] = i64(registers[Inst.reg(args.b)]) < i64(registers[Inst.reg(args.a)]) ? u64(1) : u64(0); + return OutcomeData.ok(r); }; diff --git a/assembly/instructions/shift.ts b/assembly/instructions/shift.ts index 43ee1e7..df98fcf 100644 --- a/assembly/instructions/shift.ts +++ b/assembly/instructions/shift.ts @@ -1,5 +1,5 @@ -import { InstructionRun, ok } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; const MAX_SHIFT_64 = 64; const MAX_SHIFT_32 = 32; @@ -7,134 +7,134 @@ const MAX_SHIFT_32 = 32; // SHLO_L_IMM_32 export const shlo_l_imm_32: InstructionRun = (r, args, registers) => { const shift = u32(args.c % MAX_SHIFT_32); - const value = u32(registers[reg(args.a)]); - registers[reg(args.b)] = u32SignExtend(value << shift); - return ok(r); + const value = u32(registers[Inst.reg(args.a)]); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(value << shift); + return OutcomeData.ok(r); }; // SHLO_R_IMM_32 export const shlo_r_imm_32: InstructionRun = (r, args, registers) => { const shift = u32(args.c % MAX_SHIFT_32); - const value = u32(registers[reg(args.a)]); - registers[reg(args.b)] = u32SignExtend(value >>> shift); - return ok(r); + const value = u32(registers[Inst.reg(args.a)]); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(value >>> shift); + return OutcomeData.ok(r); }; // SHAR_R_IMM_32 export const shar_r_imm_32: InstructionRun = (r, args, registers) => { const shift = u32(args.c % MAX_SHIFT_32); - const value = u32SignExtend(u32(registers[reg(args.a)])); - registers[reg(args.b)] = u64(i64(value) >> i64(shift)); - return ok(r); + const value = Inst.u32SignExtend(u32(registers[Inst.reg(args.a)])); + registers[Inst.reg(args.b)] = u64(i64(value) >> i64(shift)); + return OutcomeData.ok(r); }; // SHLO_L_IMM_ALT_32 export const shlo_l_imm_alt_32: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_32)); - registers[reg(args.b)] = u32SignExtend(args.c << shift); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_32)); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(args.c << shift); + return OutcomeData.ok(r); }; // SHLO_R_IMM_ALT_32 export const shlo_r_imm_alt_32: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_32)); - registers[reg(args.b)] = u32SignExtend(args.c >>> shift); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_32)); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(args.c >>> shift); + return OutcomeData.ok(r); }; // SHAR_R_IMM_ALT_32 export const shar_r_imm_alt_32: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_32)); - const imm = u32SignExtend(args.c); - registers[reg(args.b)] = u32SignExtend(u32(i64(imm) >> i64(shift))); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_32)); + const imm = Inst.u32SignExtend(args.c); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(u32(i64(imm) >> i64(shift))); + return OutcomeData.ok(r); }; // SHLO_L_IMM export const shlo_l_imm: InstructionRun = (r, args, registers) => { const shift = u32(args.c % MAX_SHIFT_64); - registers[reg(args.b)] = u64(registers[reg(args.a)] << u64(shift)); - return ok(r); + registers[Inst.reg(args.b)] = u64(registers[Inst.reg(args.a)] << u64(shift)); + return OutcomeData.ok(r); }; // SHLO_R_IMM export const shlo_r_imm: InstructionRun = (r, args, registers) => { const shift = u32(args.c % MAX_SHIFT_64); - registers[reg(args.b)] = registers[reg(args.a)] >> u64(shift); - return ok(r); + registers[Inst.reg(args.b)] = registers[Inst.reg(args.a)] >> u64(shift); + return OutcomeData.ok(r); }; // SHAR_R_IMM export const shar_r_imm: InstructionRun = (r, args, registers) => { const shift = u32(args.c % MAX_SHIFT_64); - const value = i64(registers[reg(args.a)]); - registers[reg(args.b)] = u64(value >> i64(shift)); - return ok(r); + const value = i64(registers[Inst.reg(args.a)]); + registers[Inst.reg(args.b)] = u64(value >> i64(shift)); + return OutcomeData.ok(r); }; // SHLO_L_IMM_ALT export const shlo_l_imm_alt: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_64)); - registers[reg(args.b)] = u64(u32SignExtend(args.c) << i64(shift)); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_64)); + registers[Inst.reg(args.b)] = u64(Inst.u32SignExtend(args.c) << i64(shift)); + return OutcomeData.ok(r); }; // SHLO_R_IMM_ALT export const shlo_r_imm_alt: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_64)); - registers[reg(args.b)] = u64(u32SignExtend(args.c)) >> u64(shift); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_64)); + registers[Inst.reg(args.b)] = u64(Inst.u32SignExtend(args.c)) >> u64(shift); + return OutcomeData.ok(r); }; // SHAR_R_IMM_ALT export const shar_r_imm_alt: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_64)); - const value = u32SignExtend(args.c); - registers[reg(args.b)] = u32SignExtend(u32(value >> i64(shift))); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_64)); + const value = Inst.u32SignExtend(args.c); + registers[Inst.reg(args.b)] = Inst.u32SignExtend(u32(value >> i64(shift))); + return OutcomeData.ok(r); }; // SHLO_L_32 export const shlo_l_32: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_32)); - const value = u32(registers[reg(args.b)]); - registers[reg(args.c)] = u32SignExtend(value << shift); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_32)); + const value = u32(registers[Inst.reg(args.b)]); + registers[Inst.reg(args.c)] = Inst.u32SignExtend(value << shift); + return OutcomeData.ok(r); }; // SHLO_R_32 export const shlo_r_32: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_32)); - const value = u32(registers[reg(args.b)]); - registers[reg(args.c)] = u32SignExtend(value >>> shift); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_32)); + const value = u32(registers[Inst.reg(args.b)]); + registers[Inst.reg(args.c)] = Inst.u32SignExtend(value >>> shift); + return OutcomeData.ok(r); }; // SHAR_R_32 export const shar_r_32: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_32)); - const regValue = u32SignExtend(u32(registers[reg(args.b)])); - registers[reg(args.c)] = u32SignExtend(u32(i64(regValue) >> i64(shift))); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_32)); + const regValue = Inst.u32SignExtend(u32(registers[Inst.reg(args.b)])); + registers[Inst.reg(args.c)] = Inst.u32SignExtend(u32(i64(regValue) >> i64(shift))); + return OutcomeData.ok(r); }; // SHLO_L export const shlo_l: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_64)); - registers[reg(args.c)] = u64(registers[reg(args.b)] << u64(shift)); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_64)); + registers[Inst.reg(args.c)] = u64(registers[Inst.reg(args.b)] << u64(shift)); + return OutcomeData.ok(r); }; // SHLO_R export const shlo_r: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_64)); - registers[reg(args.c)] = registers[reg(args.b)] >> u64(shift); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_64)); + registers[Inst.reg(args.c)] = registers[Inst.reg(args.b)] >> u64(shift); + return OutcomeData.ok(r); }; // SHAR_R export const shar_r: InstructionRun = (r, args, registers) => { - const shift = u32(registers[reg(args.a)] % u64(MAX_SHIFT_64)); - registers[reg(args.c)] = u64(i64(registers[reg(args.b)]) >> i64(shift)); - return ok(r); + const shift = u32(registers[Inst.reg(args.a)] % u64(MAX_SHIFT_64)); + registers[Inst.reg(args.c)] = u64(i64(registers[Inst.reg(args.b)]) >> i64(shift)); + return OutcomeData.ok(r); }; diff --git a/assembly/instructions/store.ts b/assembly/instructions/store.ts index 1775f96..1c61f4e 100644 --- a/assembly/instructions/store.ts +++ b/assembly/instructions/store.ts @@ -1,120 +1,120 @@ import { MaybePageFault } from "../memory"; import { portable } from "../portable"; import { Registers } from "../registers"; -import { InstructionRun, okOrFault } from "./outcome"; -import { reg, u32SignExtend } from "./utils"; +import { InstructionRun, OutcomeData } from "./outcome"; +import { Inst } from "./utils"; const faultRes = new MaybePageFault(); // Helper function to compute effective address from base register and signed 32-bit offset function effectiveAddress(registers: Registers, baseReg: u32, offset: u32): u32 { - return u32(portable.u64_add(registers[reg(u64(baseReg))], u32SignExtend(offset))); + return u32(portable.u64_add(registers[Inst.reg(u64(baseReg))], Inst.u32SignExtend(offset))); } // STORE_IMM_U8 export const store_imm_u8: InstructionRun = (r, args, _registers, memory) => { const address = args.a; memory.setU8(faultRes, address, (args.b & 0xff)); - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IMM_U16 export const store_imm_u16: InstructionRun = (r, args, _registers, memory) => { const address = args.a; memory.setU16(faultRes, address, (args.b & 0xff_ff)); - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IMM_U32 export const store_imm_u32: InstructionRun = (r, args, _registers, memory) => { const address = args.a; memory.setU32(faultRes, address, args.b); - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IMM_U64 export const store_imm_u64: InstructionRun = (r, args, _registers, memory) => { const address = args.a; - memory.setU64(faultRes, address, u32SignExtend(args.b)); - return okOrFault(r, faultRes); + memory.setU64(faultRes, address, Inst.u32SignExtend(args.b)); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_U8 export const store_u8: InstructionRun = (r, args, registers, memory) => { - memory.setU8(faultRes, args.b, (registers[reg(args.a)] & u64(0xff))); - return okOrFault(r, faultRes); + memory.setU8(faultRes, args.b, (registers[Inst.reg(args.a)] & u64(0xff))); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_U16 export const store_u16: InstructionRun = (r, args, registers, memory) => { - memory.setU16(faultRes, args.b, (registers[reg(args.a)] & u64(0xff_ff))); - return okOrFault(r, faultRes); + memory.setU16(faultRes, args.b, (registers[Inst.reg(args.a)] & u64(0xff_ff))); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_U32 export const store_u32: InstructionRun = (r, args, registers, memory) => { - memory.setU32(faultRes, args.b, u32(registers[reg(args.a)])); - return okOrFault(r, faultRes); + memory.setU32(faultRes, args.b, u32(registers[Inst.reg(args.a)])); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_U64 export const store_u64: InstructionRun = (r, args, registers, memory) => { - memory.setU64(faultRes, args.b, registers[reg(args.a)]); - return okOrFault(r, faultRes); + memory.setU64(faultRes, args.b, registers[Inst.reg(args.a)]); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IMM_IND_U8 export const store_imm_ind_u8: InstructionRun = (r, args, registers, memory) => { const address = effectiveAddress(registers, args.a, args.b); memory.setU8(faultRes, address, (args.c & 0xff)); - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IMM_IND_U16 export const store_imm_ind_u16: InstructionRun = (r, args, registers, memory) => { const address = effectiveAddress(registers, args.a, args.b); memory.setU16(faultRes, address, (args.c & 0xff_ff)); - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IMM_IND_U32 export const store_imm_ind_u32: InstructionRun = (r, args, registers, memory) => { const address = effectiveAddress(registers, args.a, args.b); memory.setU32(faultRes, address, args.c); - return okOrFault(r, faultRes); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IMM_IND_U64 export const store_imm_ind_u64: InstructionRun = (r, args, registers, memory) => { const address = effectiveAddress(registers, args.a, args.b); - memory.setU64(faultRes, address, u32SignExtend(args.c)); - return okOrFault(r, faultRes); + memory.setU64(faultRes, address, Inst.u32SignExtend(args.c)); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IND_U8 export const store_ind_u8: InstructionRun = (r, args, registers, memory) => { const address = effectiveAddress(registers, args.a, args.c); - memory.setU8(faultRes, address, (registers[reg(args.b)] & u64(0xff))); - return okOrFault(r, faultRes); + memory.setU8(faultRes, address, (registers[Inst.reg(args.b)] & u64(0xff))); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IND_U16 export const store_ind_u16: InstructionRun = (r, args, registers, memory) => { const address = effectiveAddress(registers, args.a, args.c); - memory.setU16(faultRes, address, (registers[reg(args.b)] & u64(0xff_ff))); - return okOrFault(r, faultRes); + memory.setU16(faultRes, address, (registers[Inst.reg(args.b)] & u64(0xff_ff))); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IND_U32 export const store_ind_u32: InstructionRun = (r, args, registers, memory) => { const address = effectiveAddress(registers, args.a, args.c); - memory.setU32(faultRes, address, u32(registers[reg(args.b)])); - return okOrFault(r, faultRes); + memory.setU32(faultRes, address, u32(registers[Inst.reg(args.b)])); + return OutcomeData.okOrFault(r, faultRes); }; // STORE_IND_U64 export const store_ind_u64: InstructionRun = (r, args, registers, memory) => { const address = effectiveAddress(registers, args.a, args.c); - memory.setU64(faultRes, address, registers[reg(args.b)]); - return okOrFault(r, faultRes); + memory.setU64(faultRes, address, registers[Inst.reg(args.b)]); + return OutcomeData.okOrFault(r, faultRes); }; diff --git a/assembly/instructions/utils.ts b/assembly/instructions/utils.ts index 31c4c95..91486cb 100644 --- a/assembly/instructions/utils.ts +++ b/assembly/instructions/utils.ts @@ -70,23 +70,25 @@ export function mulUpperSignedUnsigned(a: i64, b: u64): u64 { return mulUpperUnsigned(a, b); } -// @inline -export function u8SignExtend(v: u8): i64 { - // u64 wrap ensures unsigned representation in JS BigInt (no-op in AS) - return u64(i64(i32(i16(i8(v))))); -} +export class Inst { + @inline + static u8SignExtend(v: u8): i64 { + // u64 wrap ensures unsigned representation in JS BigInt (no-op in AS) + return u64(i64(i32(i16(i8(v))))); + } -// @inline -export function u16SignExtend(v: u16): i64 { - return u64(i64(i32(i16(v)))); -} + @inline + static u16SignExtend(v: u16): i64 { + return u64(i64(i32(i16(v)))); + } -// @inline -export function u32SignExtend(v: u32): i64 { - return u64(i64(i32(v))); -} + @inline + static u32SignExtend(v: u32): i64 { + return u64(i64(i32(v))); + } -// @inline -export function reg(v: u64): u32 { - return v >= u64(NO_OF_REGISTERS) ? NO_OF_REGISTERS - 1 : u32(v); + @inline + static reg(v: u64): u32 { + return v >= u64(NO_OF_REGISTERS) ? NO_OF_REGISTERS - 1 : u32(v); + } } diff --git a/assembly/math.ts b/assembly/math.ts index 444fbd1..e8ab8e9 100644 --- a/assembly/math.ts +++ b/assembly/math.ts @@ -1,15 +1,13 @@ -/** - * Integer minimum of two i32 values. - */ -// @inline -export function minI32(a: i32, b: i32): i32 { - return a < b ? a : b; -} +export class IntMath { + /** Integer minimum of two i32 values. */ + @inline + static minI32(a: i32, b: i32): i32 { + return a < b ? a : b; + } -/** - * Unsigned integer minimum of two u32 values. - */ -// @inline -export function minU32(a: u32, b: u32): u32 { - return a < b ? a : b; + /** Unsigned integer minimum of two u32 values. */ + @inline + static minU32(a: u32, b: u32): u32 { + return a < b ? a : b; + } } diff --git a/assembly/memory.ts b/assembly/memory.ts index d5a1d5e..445604c 100644 --- a/assembly/memory.ts +++ b/assembly/memory.ts @@ -1,5 +1,5 @@ -import { u8SignExtend, u16SignExtend, u32SignExtend } from "./instructions/utils"; -import { minU32 } from "./math"; +import { Inst } from "./instructions/utils"; +import { IntMath } from "./math"; import { Access, Arena, @@ -251,6 +251,7 @@ export class Memory { const pageIdx = i32(portable.u64_sub(newSbrk, u64(1)) >> u64(PAGE_SIZE_SHIFT)); if (pageIdx === this.lastAllocatedPage) { + faultRes.isFault = false; return freeMemoryStart; } @@ -262,6 +263,7 @@ export class Memory { } this.lastAllocatedPage = pageIdx; + faultRes.isFault = false; return freeMemoryStart; } @@ -282,15 +284,15 @@ export class Memory { } getI8(faultRes: MaybePageFault, address: u32): u64 { - return u8SignExtend(u8(this.getU8(faultRes, address))); + return Inst.u8SignExtend(u8(this.getU8(faultRes, address))); } getI16(faultRes: MaybePageFault, address: u32): u64 { - return u16SignExtend(u16(this.getU16(faultRes, address))); + return Inst.u16SignExtend(u16(this.getU16(faultRes, address))); } getI32(faultRes: MaybePageFault, address: u32): u64 { - return u32SignExtend(u32(this.getU32(faultRes, address))); + return Inst.u32SignExtend(u32(this.getU32(faultRes, address))); } setU8(faultRes: MaybePageFault, address: u32, value: u8): void { @@ -509,7 +511,7 @@ export class Memory { let bytesLeft = u64(value); // write to first page - const firstPageEnd = minU32(PAGE_SIZE, r.firstPageOffset + bytes); + const firstPageEnd = IntMath.minU32(PAGE_SIZE, r.firstPageOffset + bytes); for (let i: u32 = r.firstPageOffset; i < firstPageEnd; i++) { r.firstPageData[i] = u8(bytesLeft); bytesLeft >>= u64(8); @@ -529,7 +531,7 @@ export class Memory { // result (bytes in reverse order) let r: u64 = u64(0); - const firstPageEnd = minU32(PAGE_SIZE, this.chunksResult.firstPageOffset + bytes); + const firstPageEnd = IntMath.minU32(PAGE_SIZE, this.chunksResult.firstPageOffset + bytes); // read from first page for (let i: u32 = this.chunksResult.firstPageOffset; i < firstPageEnd; i++) { diff --git a/assembly/program-build.ts b/assembly/program-build.ts index 069ea3e..dec7f54 100644 --- a/assembly/program-build.ts +++ b/assembly/program-build.ts @@ -1,7 +1,7 @@ import { Arguments, higNibble, lowNibble, REQUIRED_BYTES } from "./arguments"; import { encodeVarU32 } from "./codec"; import { INSTRUCTIONS, MISSING_INSTRUCTION } from "./instructions"; -import { minI32 } from "./math"; +import { IntMath } from "./math"; /** Turn given bytecode into a valid program. Add JumpTable and Mask. */ export function wrapAsProgram(bytecode: Uint8Array): Uint8Array { @@ -60,9 +60,9 @@ function skipBytes(kind: Arguments, data: Uint8Array): i32 { case Arguments.TwoReg: return 1; case Arguments.TwoRegOneImm: - return 1 + minI32(4, data.length); + return 1 + IntMath.minI32(4, data.length); case Arguments.TwoRegOneOff: - return 1 + minI32(4, data.length); + return 1 + IntMath.minI32(4, data.length); case Arguments.TwoRegTwoImm: { const low = lowNibble(data[1]); const split = low + 1; @@ -106,5 +106,5 @@ function immBytes(dataLength: i32, required: i32): i32 { if (dataLength < required) { return 0; } - return minI32(4, dataLength - required); + return IntMath.minI32(4, dataLength - required); } diff --git a/assembly/program.ts b/assembly/program.ts index 0aad11f..1e68761 100644 --- a/assembly/program.ts +++ b/assembly/program.ts @@ -1,7 +1,7 @@ import { Args, Arguments, DECODERS, REQUIRED_BYTES } from "./arguments"; import { Decoder } from "./codec"; import { INSTRUCTIONS, MISSING_INSTRUCTION } from "./instructions"; -import { reg, u32SignExtend } from "./instructions/utils"; +import { Inst } from "./instructions/utils"; import { portable } from "./portable"; import { Registers } from "./registers"; @@ -347,57 +347,57 @@ export function resolveArguments( case Arguments.Zero: return resolved; case Arguments.OneImm: - resolved.a = u32SignExtend(args.a); + resolved.a = Inst.u32SignExtend(args.a); return resolved; case Arguments.TwoImm: - resolved.a = u32SignExtend(args.a); - resolved.b = u32SignExtend(args.b); + resolved.a = Inst.u32SignExtend(args.a); + resolved.b = Inst.u32SignExtend(args.b); return resolved; case Arguments.OneOff: - resolved.a = u32SignExtend(args.a); + resolved.a = Inst.u32SignExtend(args.a); return resolved; case Arguments.OneRegOneImm: - resolved.a = registers[reg(u64(args.a))]; - resolved.b = u32SignExtend(args.b); + resolved.a = registers[Inst.reg(u64(args.a))]; + resolved.b = Inst.u32SignExtend(args.b); return resolved; case Arguments.OneRegOneExtImm: - resolved.a = registers[reg(u64(args.a))]; + resolved.a = registers[Inst.reg(u64(args.a))]; resolved.b = portable.u64_add(u64(args.a) << u64(32), u64(args.b)); return resolved; case Arguments.OneRegTwoImm: - resolved.a = registers[reg(u64(args.a))]; - resolved.b = u32SignExtend(args.b); - resolved.c = u32SignExtend(args.c); + resolved.a = registers[Inst.reg(u64(args.a))]; + resolved.b = Inst.u32SignExtend(args.b); + resolved.c = Inst.u32SignExtend(args.c); return resolved; case Arguments.OneRegOneImmOneOff: - resolved.a = registers[reg(u64(args.a))]; - resolved.b = u32SignExtend(args.b); - resolved.c = u32SignExtend(args.c); + resolved.a = registers[Inst.reg(u64(args.a))]; + resolved.b = Inst.u32SignExtend(args.b); + resolved.c = Inst.u32SignExtend(args.c); return resolved; case Arguments.TwoReg: - resolved.a = registers[reg(u64(args.a))]; - resolved.b = registers[reg(u64(args.b))]; + resolved.a = registers[Inst.reg(u64(args.a))]; + resolved.b = registers[Inst.reg(u64(args.b))]; return resolved; case Arguments.TwoRegOneImm: - resolved.a = registers[reg(u64(args.a))]; - resolved.b = registers[reg(u64(args.b))]; - resolved.c = u32SignExtend(args.c); + resolved.a = registers[Inst.reg(u64(args.a))]; + resolved.b = registers[Inst.reg(u64(args.b))]; + resolved.c = Inst.u32SignExtend(args.c); return resolved; case Arguments.TwoRegOneOff: - resolved.a = registers[reg(u64(args.a))]; - resolved.b = registers[reg(u64(args.b))]; - resolved.c = u32SignExtend(args.c); + resolved.a = registers[Inst.reg(u64(args.a))]; + resolved.b = registers[Inst.reg(u64(args.b))]; + resolved.c = Inst.u32SignExtend(args.c); return resolved; case Arguments.TwoRegTwoImm: - resolved.a = registers[reg(u64(args.a))]; - resolved.b = registers[reg(u64(args.b))]; - resolved.c = u32SignExtend(args.c); - resolved.d = u32SignExtend(args.d); + resolved.a = registers[Inst.reg(u64(args.a))]; + resolved.b = registers[Inst.reg(u64(args.b))]; + resolved.c = Inst.u32SignExtend(args.c); + resolved.d = Inst.u32SignExtend(args.d); return resolved; case Arguments.ThreeReg: - resolved.a = registers[reg(u64(args.a))]; - resolved.b = registers[reg(u64(args.b))]; - resolved.c = registers[reg(u64(args.c))]; + resolved.a = registers[Inst.reg(u64(args.a))]; + resolved.b = registers[Inst.reg(u64(args.b))]; + resolved.c = registers[Inst.reg(u64(args.c))]; return resolved; default: throw new Error(`Unhandled arguments kind: ${kind}`); diff --git a/bench/compare.ts b/bench/compare.ts index 56e8ec5..75624dd 100644 --- a/bench/compare.ts +++ b/bench/compare.ts @@ -70,9 +70,11 @@ type TraceResult = { type SuiteResult = { timestamp: string; + fibonacci?: TraceResult[]; traces: TraceResult[]; w3f: TraceResult | null; summary: { + fibonacciMedianMs?: Record; totalTraceMedianMs: number; w3fMedianMs: number | null; }; @@ -81,10 +83,6 @@ type SuiteResult = { const baselineSuite = baseline as SuiteResult; const resultsSuite = results as SuiteResult; -// Build maps for easy lookup -const baselineTraces = new Map(baselineSuite.traces.map((t) => [t.name, t])); -const resultsTraces = new Map(resultsSuite.traces.map((t) => [t.name, t])); - interface Comparison { name: string; baselineMs: number; @@ -94,63 +92,127 @@ interface Comparison { status: "improvement" | "regression" | "neutral"; } -const comparisons: Comparison[] = []; -const regressions: Comparison[] = []; -const improvements: Comparison[] = []; +interface ComparisonResult { + comparisons: Comparison[]; + regressions: Comparison[]; + improvements: Comparison[]; +} -// Compare traces -for (const [name, baselineTrace] of baselineTraces) { - const currentTrace = resultsTraces.get(name); - if (!currentTrace) { - console.warn(`Warning: No results for trace ${name}, skipping`); - continue; +function compareBenchmarks( + baselineItems: TraceResult[], + currentItems: TraceResult[], + threshold: number, + warnOnMissing = false, +): ComparisonResult { + const baselineMap = new Map(baselineItems.map((t) => [t.name, t])); + const currentMap = new Map(currentItems.map((t) => [t.name, t])); + + const comparisons: Comparison[] = []; + const regressions: Comparison[] = []; + const improvements: Comparison[] = []; + + for (const [name, baseline] of baselineMap) { + const current = currentMap.get(name); + if (!current) { + if (warnOnMissing) { + console.warn(`Warning: No results for ${name}, skipping`); + } + continue; + } + + const diffMs = current.medianMs - baseline.medianMs; + let diffPercent: number; + if (baseline.medianMs === 0) { + diffPercent = diffMs === 0 ? 0 : diffMs > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; + } else { + diffPercent = (diffMs / baseline.medianMs) * 100; + } + + let status: "improvement" | "regression" | "neutral" = "neutral"; + if (diffPercent > threshold) { + status = "regression"; + regressions.push({ + name, + baselineMs: baseline.medianMs, + currentMs: current.medianMs, + diffMs, + diffPercent, + status, + }); + } else if (diffPercent < -threshold) { + status = "improvement"; + improvements.push({ + name, + baselineMs: baseline.medianMs, + currentMs: current.medianMs, + diffMs, + diffPercent, + status, + }); + } + + comparisons.push({ name, baselineMs: baseline.medianMs, currentMs: current.medianMs, diffMs, diffPercent, status }); } - const diffMs = currentTrace.medianMs - baselineTrace.medianMs; + regressions.sort((a, b) => b.diffPercent - a.diffPercent); + improvements.sort((a, b) => a.diffPercent - b.diffPercent); - // Guard against division by zero - let diffPercent: number; - if (baselineTrace.medianMs === 0) { - diffPercent = diffMs === 0 ? 0 : diffMs > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; - } else { - diffPercent = (diffMs / baselineTrace.medianMs) * 100; + return { comparisons, regressions, improvements }; +} + +// Compare fibonacci benchmarks +function getFibResult(): ComparisonResult { + if (baselineSuite.fibonacci && resultsSuite.fibonacci) { + return compareBenchmarks(baselineSuite.fibonacci, resultsSuite.fibonacci, threshold); } - let status: "improvement" | "regression" | "neutral" = "neutral"; - if (diffPercent > threshold) { - status = "regression"; - regressions.push({ - name, - baselineMs: baselineTrace.medianMs, - currentMs: currentTrace.medianMs, - diffMs, - diffPercent, - status, - }); - } else if (diffPercent < -threshold) { - status = "improvement"; - improvements.push({ - name, - baselineMs: baselineTrace.medianMs, - currentMs: currentTrace.medianMs, - diffMs, - diffPercent, - status, - }); + const comparisons: Comparison[] = []; + const regressions: Comparison[] = []; + const improvements: Comparison[] = []; + + // Baseline has fibonacci but results don't - mark as missing + if (baselineSuite.fibonacci) { + for (const fib of baselineSuite.fibonacci) { + const comparison: Comparison = { + name: fib.name, + baselineMs: fib.medianMs, + currentMs: 0, + diffMs: -fib.medianMs, + diffPercent: Number.NEGATIVE_INFINITY, + status: "regression", + }; + comparisons.push(comparison); + regressions.push(comparison); + } + } + + // Results has fibonacci but baseline doesn't - mark as new + if (resultsSuite.fibonacci) { + for (const fib of resultsSuite.fibonacci) { + const comparison: Comparison = { + name: fib.name, + baselineMs: 0, + currentMs: fib.medianMs, + diffMs: fib.medianMs, + diffPercent: Number.POSITIVE_INFINITY, + status: "improvement", + }; + comparisons.push(comparison); + improvements.push(comparison); + } } - comparisons.push({ - name, - baselineMs: baselineTrace.medianMs, - currentMs: currentTrace.medianMs, - diffMs, - diffPercent, - status, - }); + return { comparisons, regressions, improvements }; } -// Sort by absolute diff percentage -regressions.sort((a, b) => b.diffPercent - a.diffPercent); -improvements.sort((a, b) => a.diffPercent - b.diffPercent); +const fibResult = getFibResult(); +const fibComparisons = fibResult.comparisons; +const fibRegressions = fibResult.regressions; +const fibImprovements = fibResult.improvements; +// Compare traces +const traceResult = compareBenchmarks(baselineSuite.traces, resultsSuite.traces, threshold, true); +const comparisons = traceResult.comparisons; +const regressions = traceResult.regressions; +const improvements = traceResult.improvements; // Print results console.log("\n=== Benchmark Comparison ===\n"); @@ -159,6 +221,19 @@ console.log(`Results: ${resultsSuite.timestamp}`); console.log(`Threshold: ${threshold}%\n`); console.log("--- Summary ---\n"); + +// Fibonacci summary +if (fibComparisons.length > 0) { + console.log("Fibonacci benchmarks:"); + for (const c of fibComparisons) { + const sign = c.diffMs >= 0 ? "+" : ""; + console.log( + ` ${c.name.padEnd(20)} ${c.baselineMs.toFixed(1)}ms -> ${c.currentMs.toFixed(1)}ms (${sign}${c.diffPercent.toFixed(1)}%)`, + ); + } + console.log(); +} + console.log( `Total trace time: ${baselineSuite.summary.totalTraceMedianMs.toFixed(1)}ms -> ${resultsSuite.summary.totalTraceMedianMs.toFixed(1)}ms`, ); @@ -189,8 +264,10 @@ if (baselineSuite.w3f && resultsSuite.w3f) { console.log(`Difference: ${w3fDiff >= 0 ? "+" : ""}${w3fDiff.toFixed(1)}ms (${w3fDiffPercent.toFixed(2)}%)`); } -console.log(`\nRegressions: ${regressions.length}`); -console.log(`Improvements: ${improvements.length}`); +const allRegressions = regressions.length + fibRegressions.length; +const allImprovements = improvements.length + fibImprovements.length; +console.log(`\nRegressions: ${allRegressions}`); +console.log(`Improvements: ${allImprovements}`); if (regressions.length > 0) { console.log("\n--- Regressions (worst first) ---\n"); @@ -226,6 +303,9 @@ if (values.markdown) { comparisons, regressions, improvements, + fibComparisons, + fibRegressions, + fibImprovements, totalDiff, totalDiffPercent, threshold, @@ -235,10 +315,10 @@ if (values.markdown) { } // Exit code -if (regressions.length > 0) { +if (allRegressions > 0) { console.log("\n❌ FAILED: Regressions detected above threshold\n"); process.exit(1); -} else if (improvements.length > 0) { +} else if (allImprovements > 0) { console.log("\n✅ PASSED: No regressions (improvements detected)\n"); process.exit(0); } else { @@ -252,15 +332,20 @@ function formatMarkdown( all: Comparison[], regs: Comparison[], imps: Comparison[], + fibAll: Comparison[], + fibRegs: Comparison[], + fibImps: Comparison[], totalDiffMs: number, totalDiffPct: number, thresh: number, ): string { const sign = (n: number) => (n >= 0 ? "+" : ""); + const totalRegs = regs.length + fibRegs.length; + const totalImps = imps.length + fibImps.length; const status = - regs.length > 0 - ? `### :warning: ${regs.length} regression(s) detected (>${thresh}% threshold)` - : imps.length > 0 + totalRegs > 0 + ? `### :warning: ${totalRegs} regression(s) detected (>${thresh}% threshold)` + : totalImps > 0 ? "### :white_check_mark: No regressions (improvements detected)" : "### :white_check_mark: No significant changes"; @@ -270,6 +355,12 @@ function formatMarkdown( // Summary table md += "| Metric | Baseline | Current | Change |\n"; md += "|--------|----------|---------|--------|\n"; + + // Fibonacci summary rows + for (const c of fibAll) { + md += `| **${c.name}** | ${c.baselineMs.toFixed(1)}ms | ${c.currentMs.toFixed(1)}ms | ${sign(c.diffPercent)}${c.diffPercent.toFixed(1)}% |\n`; + } + md += `| **Trace total** | ${base.summary.totalTraceMedianMs.toFixed(1)}ms | ${current.summary.totalTraceMedianMs.toFixed(1)}ms | ${sign(totalDiffMs)}${totalDiffMs.toFixed(1)}ms (${sign(totalDiffPct)}${totalDiffPct.toFixed(1)}%) |\n`; if (base.w3f && current.w3f) { @@ -278,23 +369,25 @@ function formatMarkdown( md += `| **W3F suite** | ${base.w3f.medianMs.toFixed(1)}ms | ${current.w3f.medianMs.toFixed(1)}ms | ${sign(w3fDiff)}${w3fDiff.toFixed(1)}ms (${sign(w3fPct)}${w3fPct.toFixed(1)}%) |\n`; } - // Regressions - if (regs.length > 0) { + // Regressions (combined) + const allRegs = [...fibRegs, ...regs]; + if (allRegs.length > 0) { md += "\n
Regressions (worst first)\n\n"; - md += "| Trace | Baseline | Current | Change |\n"; - md += "|-------|----------|---------|--------|\n"; - for (const r of regs) { + md += "| Benchmark | Baseline | Current | Change |\n"; + md += "|-----------|----------|---------|--------|\n"; + for (const r of allRegs) { md += `| ${r.name} | ${r.baselineMs.toFixed(1)}ms | ${r.currentMs.toFixed(1)}ms | +${r.diffPercent.toFixed(1)}% |\n`; } md += "\n
\n"; } - // Improvements - if (imps.length > 0) { + // Improvements (combined) + const allImps = [...fibImps, ...imps]; + if (allImps.length > 0) { md += "\n
Improvements\n\n"; - md += "| Trace | Baseline | Current | Change |\n"; - md += "|-------|----------|---------|--------|\n"; - for (const i of imps) { + md += "| Benchmark | Baseline | Current | Change |\n"; + md += "|-----------|----------|---------|--------|\n"; + for (const i of allImps) { md += `| ${i.name} | ${i.baselineMs.toFixed(1)}ms | ${i.currentMs.toFixed(1)}ms | ${i.diffPercent.toFixed(1)}% |\n`; } md += "\n
\n"; diff --git a/bench/run.ts b/bench/run.ts index 5a606b7..5907831 100644 --- a/bench/run.ts +++ b/bench/run.ts @@ -15,9 +15,10 @@ import { performance } from "node:perf_hooks"; import { parseArgs } from "node:util"; import "json-bigint-patch"; +import type { PvmApi } from "../bin/src/trace-replay.js"; import { replayTraceFile } from "../bin/src/trace-replay.js"; import { NoOpTracer } from "../bin/src/tracer.js"; -import { HasMetadata, InputKind, prepareProgram, runProgram } from "../build/release.js"; +import * as wasmPvm from "../build/release.js"; // ---- CLI ---- @@ -29,6 +30,7 @@ const { values } = parseArgs({ warmup: { type: "string", default: "1" }, output: { type: "string", default: "" }, "block-gas": { type: "boolean", default: false }, + portable: { type: "boolean", default: false }, help: { type: "boolean", short: "h", default: false }, }, }); @@ -42,6 +44,7 @@ Options: --iterations Number of timed iterations (default: 5) --warmup Number of warmup iterations (default: 1) --output Write JSON results to file + --portable Use portable JS build instead of WASM -h, --help Show this help`); process.exit(0); } @@ -60,6 +63,17 @@ if (!Number.isInteger(WARMUP) || WARMUP < 0 || WARMUP >= ITERATIONS) { ); process.exit(1); } + +// ---- Load PVM API ---- + +async function loadPvm(): Promise { + if (values.portable) { + // The portable build exposes the same API surface but has different nominal types. + return (await import("../dist/build/js/portable-bundle.js")) as unknown as PvmApi; + } + return wasmPvm; +} + // ---- Types ---- type BenchResult = { @@ -73,9 +87,11 @@ type BenchResult = { type SuiteResult = { timestamp: string; + fibonacci: BenchResult[]; traces: BenchResult[]; w3f: BenchResult | null; summary: { + fibonacciMedianMs: Record; totalTraceMedianMs: number; w3fMedianMs: number | null; }; @@ -126,9 +142,45 @@ function formatResult(r: BenchResult): string { return ` ${r.name.padEnd(40)} median=${r.medianMs.toFixed(1)}ms min=${r.minMs.toFixed(1)}ms max=${r.maxMs.toFixed(1)}ms p95=${r.p95Ms.toFixed(1)}ms`; } +// ---- Fibonacci micro-benchmark ---- + +// A compact PVM program that computes fibonacci(reg[7]). +// Register 7 is the input (which fibonacci number to compute). +const FIB_PROGRAM = [ + 0, 0, 33, 51, 8, 1, 51, 9, 1, 40, 3, 0, 149, 119, 255, 81, 7, 12, 100, 138, 200, 152, 8, 100, 169, 40, 243, 100, 135, + 51, 8, 51, 9, 1, 50, 0, 73, 147, 82, 213, 0, +]; + +// Each fib(n) loop iteration uses ~8 gas, so n must be large to stress the interpreter. +const FIB_CASES: Array<{ name: string; n: number; gas: bigint }> = [ + { name: "fib(10k)", n: 10_000, gas: 1_000_000n }, + { name: "fib(100k)", n: 100_000, gas: 10_000_000n }, + { name: "fib(1M)", n: 1_000_000, gas: 100_000_000n }, + { name: "fib(10M)", n: 10_000_000, gas: 1_000_000_000n }, +]; + +function benchFibonacci(pvm: PvmApi): BenchResult[] { + const { prepareProgram, runProgram, InputKind, HasMetadata } = pvm; + const results: BenchResult[] = []; + + for (const { name, n, gas } of FIB_CASES) { + const registers = [4294901760n, 0n, 0n, 0n, 0n, 0n, 0n, BigInt(n), 0n, 0n, 0n, 0n, 0n]; + + const result = benchRun(name, () => { + const exe = prepareProgram(InputKind.Generic, HasMetadata.No, FIB_PROGRAM, registers, [], [], [], 16); + runProgram(exe, gas, 0, false, false); + }); + + console.log(formatResult(result)); + results.push(result); + } + + return results; +} + // ---- Trace benchmarks ---- -function benchTraces(dir: string): BenchResult[] { +function benchTraces(dir: string, pvm: PvmApi): BenchResult[] { const files = readdirSync(dir) .filter((f) => f.endsWith(".log")) .sort(); @@ -148,10 +200,11 @@ function benchTraces(dir: string): BenchResult[] { const result = benchRun(name, () => { replayTraceFile(filePath, { logs: false, - hasMetadata: HasMetadata.Yes, + hasMetadata: pvm.HasMetadata.Yes, verify: false, tracer: new NoOpTracer(), useBlockGas: values["block-gas"], + pvm, }); }); @@ -174,7 +227,9 @@ type W3fTest = { program: number[]; }; -function benchW3f(dir: string): BenchResult | null { +function benchW3f(dir: string, pvm: PvmApi): BenchResult | null { + const { prepareProgram, runProgram, InputKind, HasMetadata } = pvm; + let files: string[]; try { files = readdirSync(dir) @@ -201,15 +256,15 @@ function benchW3f(dir: string): BenchResult | null { const result = benchRun("w3f-all", () => { for (const data of tests) { - const pageMap = (data["initial-page-map"] || []).map((p) => ({ + const pageMap = (data["initial-page-map"] || []).map((p: W3fTest["initial-page-map"][0]) => ({ ...p, access: p["is-writable"] ? 2 : 1, })); - const memory = (data["initial-memory"] || []).map((c) => ({ + const memory = (data["initial-memory"] || []).map((c: W3fTest["initial-memory"][0]) => ({ address: c.address, data: c.contents || [], })); - const registers = (data["initial-regs"] || []).map((x) => BigInt(x)); + const registers = (data["initial-regs"] || []).map((x: bigint | number) => BigInt(x)); const gas = BigInt(data["initial-gas"] || 10000); const pc = data["initial-pc"] || 0; @@ -234,19 +289,31 @@ function benchW3f(dir: string): BenchResult | null { // ---- Main ---- -function main() { - console.log(`\nPVM Benchmark (${ITERATIONS} iterations, ${WARMUP} warmup)\n`); +async function main() { + const pvm = await loadPvm(); + const mode = values.portable ? "portable JS" : "interpreter"; + console.log(`\nPVM Benchmark [${mode}] (${ITERATIONS} iterations, ${WARMUP} warmup)\n`); const suiteResult: SuiteResult = { timestamp: new Date().toISOString(), + fibonacci: [], traces: [], w3f: null, summary: { + fibonacciMedianMs: {}, totalTraceMedianMs: 0, w3fMedianMs: null, }, }; + // Fibonacci micro-benchmarks + console.log("Fibonacci micro-benchmarks:"); + suiteResult.fibonacci = benchFibonacci(pvm); + for (const r of suiteResult.fibonacci) { + suiteResult.summary.fibonacciMedianMs[r.name] = r.medianMs; + } + console.log(); + // Trace benchmarks const traceDirs = [values.traces, "./bench/traces"].filter(Boolean); @@ -261,7 +328,7 @@ function main() { if (traceDir) { console.log(`Trace replays (${traceDir}):`); - suiteResult.traces = benchTraces(traceDir); + suiteResult.traces = benchTraces(traceDir, pvm); suiteResult.summary.totalTraceMedianMs = suiteResult.traces.reduce((sum, r) => sum + r.medianMs, 0); console.log(`\n TOTAL trace median: ${suiteResult.summary.totalTraceMedianMs.toFixed(1)}ms\n`); } else { @@ -282,7 +349,7 @@ function main() { if (w3fDir) { console.log(`W3F test vectors (${w3fDir}):`); - suiteResult.w3f = benchW3f(w3fDir); + suiteResult.w3f = benchW3f(w3fDir, pvm); suiteResult.summary.w3fMedianMs = suiteResult.w3f?.medianMs ?? null; console.log(); } else { @@ -291,6 +358,9 @@ function main() { // Summary console.log("=== Summary ==="); + for (const [name, ms] of Object.entries(suiteResult.summary.fibonacciMedianMs)) { + console.log(` ${name.padEnd(22)} ${ms.toFixed(1)}ms`); + } console.log(` Trace total median: ${suiteResult.summary.totalTraceMedianMs.toFixed(1)}ms`); if (suiteResult.summary.w3fMedianMs !== null) { console.log(` W3F suite median: ${suiteResult.summary.w3fMedianMs.toFixed(1)}ms`); diff --git a/bin/src/trace-replay.ts b/bin/src/trace-replay.ts index 0640d46..ea435db 100644 --- a/bin/src/trace-replay.ts +++ b/bin/src/trace-replay.ts @@ -1,15 +1,5 @@ import { readFileSync } from "node:fs"; -import { - HasMetadata, - InputKind, - prepareProgram, - pvmDestroy, - pvmReadMemory, - pvmResume, - pvmSetRegisters, - pvmStart, - pvmWriteMemory, -} from "../../build/release.js"; +import * as defaultPvm from "../../build/release.js"; import { LOG_HOST_CALL_INDEX, printLogHostCall } from "./log-host-call.js"; import { ARGS_SEGMENT_START, @@ -26,16 +16,34 @@ import { import { ConsoleTracer, Tracer } from "./tracer.js"; import { hexEncode } from "./utils.js"; +export type PvmApi = { + HasMetadata: typeof defaultPvm.HasMetadata; + InputKind: typeof defaultPvm.InputKind; + prepareProgram: typeof defaultPvm.prepareProgram; + runProgram: typeof defaultPvm.runProgram; + pvmStart: typeof defaultPvm.pvmStart; + pvmDestroy: typeof defaultPvm.pvmDestroy; + pvmResume: typeof defaultPvm.pvmResume; + pvmReadMemory: typeof defaultPvm.pvmReadMemory; + pvmWriteMemory: typeof defaultPvm.pvmWriteMemory; + pvmSetRegisters: typeof defaultPvm.pvmSetRegisters; +}; + type ReplayOptions = { logs: boolean; - hasMetadata: HasMetadata; + hasMetadata: defaultPvm.HasMetadata; verify: boolean; logHostCall?: boolean; tracer?: Tracer; useBlockGas?: boolean; + pvm?: PvmApi; }; export function replayTraceFile(filePath: string, options: ReplayOptions): TraceSummary { + const pvm = options.pvm ?? defaultPvm; + const { prepareProgram, pvmStart, pvmDestroy, pvmResume, pvmReadMemory, pvmWriteMemory, pvmSetRegisters, InputKind } = + pvm; + const input = readFileSync(filePath, "utf8"); const trace = parseTrace(input);