Skip to content

Commit

Permalink
Fix sbrk gas calculation. (#15)
Browse files Browse the repository at this point in the history
* Fix sbrk gas calculation.

* wrap as program.:

* mul upper test program

* Fix mul_upper

* Fix mul upper and mask generation.

* Fix mul_upper_s_s

---------

Co-authored-by: Mateusz Sikora <[email protected]>
  • Loading branch information
tomusdrw and mateuszsikora authored Dec 31, 2024
1 parent cbdcb4c commit 4f3bc13
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 53 deletions.
2 changes: 2 additions & 0 deletions assembly/api-generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,15 @@ export function runVm(input: VmInput, logs: boolean = false): VmOutput {
for (;;) {
if (!isOk) {
if (logs) console.log(`REGISTERS = ${registers.join(", ")} (final)`);
if (logs) console.log(`REGISTERS = ${registers.map((x: u64) => `0x${x.toString(16)}`).join(", ")} (final)`);
if (logs) console.log(`Finished with status: ${int.status}`);
break;
}

if (logs) console.log(`PC = ${int.pc}`);
if (logs) console.log(`STATUS = ${int.status}`);
if (logs) console.log(`REGISTERS = ${registers.join(", ")}`);
if (logs) console.log(`REGISTERS = ${registers.map((x: u64) => `0x${x.toString(16)}`).join(", ")}`);
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];
Expand Down
2 changes: 1 addition & 1 deletion assembly/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function nextStep(): boolean {
return false;
}

export function run(steps: u32): boolean {
export function nSteps(steps: u32): boolean {
if (interpreter !== null) {
const int = <Interpreter>interpreter;
let isOk = true;
Expand Down
2 changes: 1 addition & 1 deletion assembly/instructions-exe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const RUN: InstructionRun[] = [
// 20
// LOAD_IMM_64
(args, registers) => {
registers[reg(args.a)] = u64(args.c) + (u64(args.b) << 32);
registers[reg(args.a)] = u64(args.b) + (u64(args.c) << 32);
return ok();
},
INVALID,
Expand Down
3 changes: 1 addition & 2 deletions assembly/interpreter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class Interpreter {

// additional gas cost of sbrk
if (iData === SBRK) {
const alloc = u32(this.registers[reg(args.a)]);
const alloc = u64(u32(this.registers[reg(args.a)]));
const gas = ((alloc + PAGE_SIZE - 1) >> PAGE_SIZE_SHIFT) * 16;
if (this.gas.sub(gas)) {
this.status = Status.OOG;
Expand Down Expand Up @@ -158,7 +158,6 @@ export class Interpreter {
case Outcome.Ok: {
// by default move to next instruction.
this.pc += 1 + argsLen;

return true;
}
}
Expand Down
32 changes: 21 additions & 11 deletions assembly/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,33 @@ export function mulUpperUnsigned(a: u64, b: u64): u64 {
* Same as [mulUpperUnsigned] but treat the arguments as signed (two-complement) 64-bit numbers and the result alike.
*/
export function mulUpperSigned(a: i64, b: i64): u64 {
const aSign = a < 0 ? 1 : -1;
const bSign = b < 0 ? 1 : -1;
const sign = aSign * bSign;
const aAbs = a < 0 ? ~a + 1 : a;
const bAbs = b < 0 ? ~b + 1 : b;
let isResultNegative = false;
let aAbs = a;
let bAbs = b;
if (a < 0) {
isResultNegative = !isResultNegative;
aAbs = ~a + 1;
}
if (b < 0) {
isResultNegative = !isResultNegative;
bAbs = ~b + 1;
}

if (sign < 0) {
return ~mulUpperUnsigned(aAbs, bAbs) + 1;
if (isResultNegative) {
const upper = mulUpperUnsigned(aAbs, bAbs);
const lower = aAbs * bAbs;
return ~upper + (lower === 0 ? 1 : 0);
}

return mulUpperUnsigned(aAbs, bAbs);
}

export function mulUpperSignedUnsigned(a: i64, b: u64): u64 {
const aSign = a < 0 ? 1 : -1;
if (aSign < 0) {
const aAbs = ~a + 1;
return ~mulUpperUnsigned(aAbs, b) + 1;
if (a < 0) {
const aAbs: u64 = ~a + 1;
const upper = mulUpperUnsigned(aAbs, b);
const lower = aAbs * b;
return ~upper + (lower === 0 ? 1 : 0);
}
return mulUpperUnsigned(a, b);
}
Expand Down
11 changes: 4 additions & 7 deletions assembly/program-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,21 +83,18 @@ function buildMask(bytecode: Uint8Array): u8[] {

const requiredBytes = REQUIRED_BYTES[iData.kind];
if (i + 1 + requiredBytes <= bytecode.length) {
const skip = skipBytes(iData.kind, bytecode.subarray(i + 1));
i += skip;
i += skipBytes(iData.kind, bytecode.subarray(i + 1));
}
}
// pack mask
const packed: u8[] = [];
for (let i = 0; i < mask.length; i += 8) {
let byte: u8 = 0;
for (let j = i; j < i + 8; j++) {
if (j < mask.length) {
byte |= mask[j] ? 1 : 0;
} else {
byte |= 1;
byte >>= 1;
if (j < mask.length && mask[j]) {
byte |= 0b1000_0000;
}
byte << 1;
}
packed.push(byte);
}
Expand Down
85 changes: 64 additions & 21 deletions bin/fuzz.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function fuzz(data) {
pc,
gas,
);
pvm.run(100);
while(pvm.nSteps(100)) {}

const printDebugInfo = false;
const registers = Array(13).join(',').split(',').map(() => BigInt(0));
Expand All @@ -30,25 +30,31 @@ export function fuzz(data) {
program,
}, printDebugInfo);

assert(pvm.getStatus(), normalizeStatus(output.status), 'status');
assert(pvm.getGasLeft(), output.gas, 'gas');
assert(pvm.getRegisters().toString(), output.registers.toString(), 'registers');
// assert(pvm.getProgramCounter(), output.pc, 'pc');
collectErrors((assert) => {
assert(pvm.getStatus(), normalizeStatus(output.status), 'status');
assert(pvm.getGasLeft(), output.gas, 'gas');
assert(Array.from(pvm.getRegisters()), output.registers, 'registers');
assert(pvm.getProgramCounter(), output.pc, 'pc');
});

writeTestCase(
program,
{
pc,
gas,
registers,
},
{
status: pvm.getStatus(),
gasLeft: pvm.getGasLeft(),
pc: pvm.getProgramCounter(),
registers: pvm.getRegisters()
},
);
try {
writeTestCase(
program,
{
pc,
gas,
registers,
},
{
status: pvm.getStatus(),
gasLeft: pvm.getGasLeft(),
pc: pvm.getProgramCounter(),
registers: pvm.getRegisters()
},
);
} catch (e) {
console.warn('Unable to write file', e);
}
} catch (e) {
const hex = programHex(program);
console.log(program);
Expand All @@ -73,13 +79,50 @@ function normalizeStatus(status) {
}

function assert(tb, an, comment = '') {
if (tb !== an) {
throw new Error(`Diverging value: (typeberry) ${tb} vs ${an} (ananas). ${comment}`);
let condition = tb !== an;
if (Array.isArray(tb) && Array.isArray(an)) {
condition = tb.toString() !== an.toString();
}

if (condition) {
const alsoAsHex = (f) => {
if (Array.isArray(f)) {
return `${f.map(alsoAsHex).join(', ')}`;
}

if (typeof f === 'number' || typeof f === 'bigint') {
if (BigInt(f) !== 0n) {
return `${f} | 0x${f.toString(16)}`;
}
return `${f}`;
}
return f;
};

throw new Error(`Diverging value: ${comment}
\t(typeberry) ${alsoAsHex(tb)}
\t(ananas) ${alsoAsHex(an)}`);
}
}

function collectErrors(cb) {
const errors = [];
cb((...args) => {
try {
assert(...args);
} catch (e) {
errors.push(e);
}
});

if (errors.length > 0) {
throw new Error(errors.join('\n'));
}
}

function writeTestCase(program, initial, expected) {
const hex = programHex(program);
fs.mkdirSync(`../tests/length_${hex.length}`, { recursive: true });
fs.writeFileSync(`../tests/length_${hex.length}/${hex}.json`, JSON.stringify({
name: linkTo(hex),
"initial-regs": initial.registers,
Expand Down
8 changes: 4 additions & 4 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function main() {
if (status.fail.length) {
console.error('Failures:');
for (const e of status.fail) {
console.error(`❗ ${e}`);
console.error(`❗ ${e.filePath} (${e.name})`);
}
process.exit(-1)
}
Expand All @@ -151,7 +151,7 @@ function processFile(IS_DEBUG, status, filePath) {
// Parse the JSON content
jsonData = JSON.parse(fileContent);
} catch (error) {
status.fail.push(filePath);
status.fail.push({ filePath, name: '<unknown>' });
console.error(`Error reading file: ${filePath}`);
console.error(error.message);
return;
Expand All @@ -160,9 +160,9 @@ function processFile(IS_DEBUG, status, filePath) {
try {
// Process the parsed JSON
processJson(jsonData, IS_DEBUG);
status.ok.push(filePath);
status.ok.push({ filePath, name: jsonData.name });
} catch (error) {
status.fail.push(filePath);
status.fail.push({ filePath, name: jsonData.name });
console.error(`Error running test: ${filePath}`);
console.error(error.message);
}
Expand Down
11 changes: 11 additions & 0 deletions bin/wrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env node

// wrap input as program

import {fuzz} from './fuzz.js'

const program = fuzz([
20,8,0, 0, 0, 0, 255, 255, 255, 255,20,7,0, 0, 0, 0, 1, 0, 0, 0,193,135,9,194,135,10,195,135,11
]);

console.log(program);
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"prestart": "npm run build",
"preweb": "npm run build",
"web": "npx live-server ./web",
"start": "node ./bin/index.js"
"start": "node ./bin/index.js",
"fuzz": "npx jazzer --sync ./bin/fuzz"
},
"keywords": [],
"author": "Fluffy Labs",
Expand All @@ -24,7 +25,7 @@
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@jazzer.js/core": "^2.1.0",
"@typeberry/pvm-debugger-adapter": "^0.0.1-1bce842",
"@typeberry/pvm-debugger-adapter": "0.1.0-5b611f4",
"assemblyscript": "^0.27.31"
},
"type": "module",
Expand Down

0 comments on commit 4f3bc13

Please sign in to comment.