Skip to content

Commit

Permalink
Alternative approach to args decoder. (#21)
Browse files Browse the repository at this point in the history
* Fix args decoder.

* Fix according to GP.

* Fix immediate limit.
  • Loading branch information
tomusdrw authored Jan 6, 2025
1 parent e4336c0 commit 034713d
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 62 deletions.
19 changes: 7 additions & 12 deletions assembly/api-generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,15 @@ export function getAssembly(p: Program): string {
v += changetype<string>(iData.namePtr);
v += `(${instruction})`;

const argsLen = p.mask.argsLen(i);
const end = i + 1 + argsLen;
if (end > len) {
const name = changetype<string>(iData.namePtr);
const intro = "Invalid program - code is not long enough";
throw new Error(`${intro} Expected: ${argsLen} for ${name} at ${i} (${end} > ${len})`);
}

const args = decodeArguments(iData.kind, p.code.subarray(i + 1, end));
const argsArray = args === null ? [0, 0, 0, 0] : [args.a, args.b, args.c, args.d];
const skipBytes = p.mask.bytesToNextInstruction(i);
const args = decodeArguments(iData.kind, p.code.subarray(i + 1), skipBytes);
const argsArray = [args.a, args.b, args.c, args.d];
const relevantArgs = RELEVANT_ARGS[iData.kind];
for (let i = 0; i < relevantArgs; i++) {
v += ` ${argsArray[i]}, `;
}
i += argsLen;
i += skipBytes;
console.log(`${v}, nextPC: ${i}`);
}
return v;
}
Expand Down Expand Up @@ -101,9 +95,10 @@ export function runVm(input: VmInput, logs: boolean = false): VmOutput {
if (logs) {
const instruction = int.pc < u32(int.program.code.length) ? int.program.code[int.pc] : 0;
const iData = instruction >= <u8>INSTRUCTIONS.length ? MISSING_INSTRUCTION : INSTRUCTIONS[instruction];
const skipBytes = p.mask.bytesToNextInstruction(int.pc);
const name = changetype<string>(iData.namePtr);
console.log(`INSTRUCTION = ${name} (${instruction})`);
const args = resolveArguments(iData.kind, int.program.code.subarray(int.pc + 1), int.registers);
const args = resolveArguments(iData.kind, int.program.code.subarray(int.pc + 1), skipBytes, int.registers);
if (args !== null) {
console.log(`ARGUMENTS:
${args.a} (${args.decoded.a}) = 0x${u64(args.a).toString(16)},
Expand Down
57 changes: 29 additions & 28 deletions assembly/arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,81 +36,81 @@ function asArgs(a: u32, b: u32, c: u32, d: u32): Args {
return x;
}

type ArgsDecoder = (data: Uint8Array) => Args;
type ArgsDecoder = (data: Uint8Array, immLimit: u32) => Args;

function twoImm(data: Uint8Array): Args {
function twoImm(data: Uint8Array, lim: u32): Args {
const n = nibbles(data[0]);
const split = <i32>Math.min(4, n.low) + 1;
const first = decodeI32(data.subarray(1, split));
const second = decodeI32(data.subarray(split));
const first = decodeI32(data, 1, split);
const second = decodeI32(data, split, lim);
return asArgs(first, second, 0, 0);
}

export const DECODERS: ArgsDecoder[] = [
// DECODERS[Arguments.Zero] =
(_) => {
(_d, _l) => {
return asArgs(0, 0, 0, 0);
},
// DECODERS[Arguments.OneImm] =
(data: Uint8Array) => {
return asArgs(decodeI32(data), 0, 0, 0);
(data, lim) => {
return asArgs(decodeI32(data, 0, lim), 0, 0, 0);
},
// DECODERS[Arguments.TwoImm] =
(data: Uint8Array) => twoImm(data),
(data, lim) => twoImm(data, lim),
// DECODERS[Arguments.OneOff] =
(data: Uint8Array) => {
return asArgs(decodeI32(data), 0, 0, 0);
(data, lim) => {
return asArgs(decodeI32(data, 0, lim), 0, 0, 0);
},
// DECODERS[Arguments.OneRegOneImm] =
(data: Uint8Array) => {
return asArgs(nibbles(data[0]).low, decodeI32(data.subarray(1)), 0, 0);
(data, lim) => {
return asArgs(nibbles(data[0]).low, decodeI32(data, 1, lim), 0, 0);
},
// DECODERS[Arguments.OneRegOneExtImm] =
(data: Uint8Array) => {
(data, _lim) => {
const a = nibbles(data[0]).low;
const b = decodeU32(data.subarray(1));
const c = decodeU32(data.subarray(5));
return asArgs(a, b, c, 0);
},
//DECODERS[Arguments.OneRegTwoImm] =
(data: Uint8Array) => {
(data, lim) => {
const n = nibbles(data[0]);
const split = <i32>Math.min(4, n.hig) + 1;
const immA = decodeI32(data.subarray(1, split));
const immB = decodeI32(data.subarray(split));
const immA = decodeI32(data, 1, split);
const immB = decodeI32(data, split, lim);
return asArgs(n.low, immA, immB, 0);
},
// DECODERS[Arguments.OneRegOneImmOneOff] =
(data: Uint8Array) => {
(data, lim) => {
const n = nibbles(data[0]);
const split = <i32>Math.min(4, n.hig) + 1;
const immA = decodeI32(data.subarray(1, split));
const offs = decodeI32(data.subarray(split));
const immA = decodeI32(data, 1, split);
const offs = decodeI32(data, split, lim);
return asArgs(n.low, immA, offs, 0);
},
// DECODERS[Arguments.TwoReg] =
(data: Uint8Array) => {
(data, _lim) => {
const n = nibbles(data[0]);
return asArgs(n.hig, n.low, 0, 0);
},
// DECODERS[Arguments.TwoRegOneImm] =
(data: Uint8Array) => {
(data, lim) => {
const n = nibbles(data[0]);
return asArgs(n.hig, n.low, decodeI32(data.subarray(1)), 0);
return asArgs(n.hig, n.low, decodeI32(data, 1, lim), 0);
},
// DECODERS[Arguments.TwoRegOneOff] =
(data: Uint8Array) => {
(data, lim) => {
const n = nibbles(data[0]);
return asArgs(n.hig, n.low, decodeI32(data.subarray(1)), 0);
return asArgs(n.hig, n.low, decodeI32(data, 1, lim), 0);
},
// DECODERS[Arguments.TwoRegTwoImm] =
(data: Uint8Array) => {
(data, lim) => {
const n = nibbles(data[0]);
const result = twoImm(data.subarray(1));
const result = twoImm(data.subarray(1), lim > 1 ? lim - 1 : 0);
return asArgs(n.hig, n.low, result.a, result.b);
},
// DECODERS[Arguments.ThreeReg] =
(data: Uint8Array) => {
(data, _lim) => {
const a = nibbles(data[0]);
const b = nibbles(data[1]);
return asArgs(a.hig, a.low, b.low, 0);
Expand All @@ -134,7 +134,8 @@ export function nibbles(byte: u8): Nibbles {
}

//@inline
function decodeI32(data: Uint8Array): u32 {
function decodeI32(input: Uint8Array, start: u32, end: u32): u32 {
const data = input.subarray(start, end > start ? end : start);
const len = <u32>Math.min(4, data.length);
let num = 0;
for (let i: u32 = 0; i < len; i++) {
Expand Down
16 changes: 3 additions & 13 deletions assembly/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,8 @@ export class Interpreter {
}

// get args and invoke instruction
const argsLen = this.program.mask.argsLen(pc);
const end = pc + 1 + argsLen;
if (end > <u32>this.program.code.length) {
this.status = Status.PANIC;
return false;
}

const args = decodeArguments(iData.kind, this.program.code.subarray(pc + 1, end));
if (args === null) {
this.status = Status.PANIC;
return false;
}
const skipBytes = this.program.mask.bytesToNextInstruction(pc);
const args = decodeArguments(iData.kind, this.program.code.subarray(pc + 1), skipBytes);

// additional gas cost of sbrk
if (iData === SBRK) {
Expand Down Expand Up @@ -157,7 +147,7 @@ export class Interpreter {
}
case Outcome.Ok: {
// by default move to next instruction.
this.pc += 1 + argsLen;
this.pc += 1 + skipBytes;
return true;
}
}
Expand Down
29 changes: 20 additions & 9 deletions assembly/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ export function decodeProgram(program: Uint8Array): Program {

export class Mask {
// NOTE: might be longer than code (bit-alignment)
readonly bytesToSkip: StaticArray<u8>;
readonly bytesToSkip: StaticArray<u32>;

constructor(packedMask: Uint8Array, codeLength: i32) {
this.bytesToSkip = new StaticArray<u8>(codeLength);
let lastInstructionOffset: u8 = 0;
this.bytesToSkip = new StaticArray<u32>(codeLength);
let lastInstructionOffset: u32 = 0;
for (let i: i32 = packedMask.length - 1; i >= 0; i -= 1) {
let bits = packedMask[i];
const index = i * 8;
Expand All @@ -91,10 +91,11 @@ export class Mask {
return this.bytesToSkip[index] === 0;
}

argsLen(i: u32): u8 {
bytesToNextInstruction(i: u32): u32 {
if (i + 1 < <u32>this.bytesToSkip.length) {
return this.bytesToSkip[i + 1];
}

return 0;
}

Expand Down Expand Up @@ -198,11 +199,16 @@ export class Program {
}
}

export function decodeArguments(kind: Arguments, data: Uint8Array): Args | null {
export function decodeArguments(kind: Arguments, data: Uint8Array, lim: u32): Args {
if (data.length < REQUIRED_BYTES[kind]) {
return null;
// in case we have less data than needed we extend the data with zeros.
const extended = new Uint8Array(REQUIRED_BYTES[kind]);
for (let i = 0; i < data.length; i++) {
extended[i] = data[i];
}
return DECODERS[kind](extended, lim);
}
return DECODERS[kind](data);
return DECODERS[kind](data, lim);
}

class ResolvedArguments {
Expand All @@ -213,8 +219,13 @@ class ResolvedArguments {
decoded: Args = new Args();
}

export function resolveArguments(kind: Arguments, data: Uint8Array, registers: Registers): ResolvedArguments | null {
const args = decodeArguments(kind, data);
export function resolveArguments(
kind: Arguments,
data: Uint8Array,
lim: u32,
registers: Registers,
): ResolvedArguments | null {
const args = decodeArguments(kind, data, lim);
if (args === null) {
return null;
}
Expand Down

0 comments on commit 034713d

Please sign in to comment.