Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(avm)!: byte indexed PC #9582

Merged
merged 17 commits into from
Nov 4, 2024
Merged

feat(avm)!: byte indexed PC #9582

merged 17 commits into from
Nov 4, 2024

Conversation

fcarreiro
Copy link
Contributor

@fcarreiro fcarreiro commented Oct 30, 2024

This PR moves the AVM to use byte-indexed PCs

  • Modifies the transpiler to remap brillig PCs
  • Modifies the simulator to use byte indexed PCs
  • Modifies witgen and circuit to use byte indexed PCs

Why are we doing this?

  • Needed for bytecode decomposition in the circuit.
  • Allow storing other stuff besides code in a contract, and then be able to use it in memory with an opcode "CODECOPY" or similar.

A note on how PCs are mapped in the transpiler: we do 2 passes. First we translate all instructions and leave brillig location operands as BRILLIG_LOCATION. On a second pass, since now we know the structure of the program and the brillig=>AVM pcs, we replace those.

There are a few big caveats

  1. Since the JUMP(I) and INTERNALCALL operands are U16, we cannot jump or call a location bigger than 2^16. This effectively constrains the contract size to 65kB. We use 32 bit jumps now.
  2. We can do the transformation in (only) 2 passes because we only have 1 variant of JUMP etc. Suppose we had an 8 bit variant, or a 32 bit variant, then we wouldn't know which one to use until the original PC has been mapped, but that itself can change the size of the instructions and trigger a remapping!

Solutions?

  • For (1) I might propose having relative jumps JUMP(I)R with 8 and 16 bit variants, and an absolute JUMP with 32 bits.
  • For (2) we might just need to remap until there is no change.

Part of #9059.

Copy link
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

Join @fcarreiro and the rest of your teammates on Graphite Graphite

@fcarreiro fcarreiro marked this pull request as ready for review November 4, 2024 12:19
Copy link
Collaborator

@dbanks12 dbanks12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Some comments, but they shouldn't block merge!

Comment on lines 115 to 118
FF { value: FieldElement },
// Unresolved brillig pc that needs translation to a 16 bit byte-indexed PC.
BRILLIG_LOCATION { brillig_pc: u32 },
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clever solution!

Comment on lines +302 to +304
const auto last_mul_pc =
2 * Deserialization::get_pc_increment(OpCode::SET_8) + 11 * Deserialization::get_pc_increment(OpCode::MUL_8);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

magic number 11?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a constant for the number of iterations and replaced the related magic constants. This will be shipped as part of my next PR.

Comment on lines +244 to +248
const auto opcode = static_cast<OpCode>(opcode_byte);
const auto iter = OPCODE_WIRE_FORMAT.find(opcode);
if (iter == OPCODE_WIRE_FORMAT.end()) {
throw_or_abort("Opcode not found in OPCODE_WIRE_FORMAT: " + to_hex(opcode) + " name " + to_string(opcode));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would it mean for the wire format not to include the opcode? Is this basically a redundant check that "opcode is valid"?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see now that this isn't new code and that the only diff for it is that it was pulled into a helper function and indentation changed

Comment on lines 427 to 430
expect(results.reverted).toBe(false);
const expectedResults = Buffer.concat('0010101011'.split('').map(c => new Fr(Number(c)).toBuffer()));
const resultBuffer = Buffer.concat(results.output.map(f => f.toBuffer()));

expect(resultBuffer.equals(expectedResults)).toBe(true);
expect(results.output.map(f => f.toNumber().toString()).join('')).toEqual('0010101011');
});

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much cleaner, nice

Comment on lines 1083 to 1086
// but otherwise will run out of gas
new Jump(/*jumpOffset*/ 2),
// Note: 15 is the byte index, calculated as 3*size(Set.wireFormat8)
new Jump(/*jumpOffset*/ 15),
]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably just use size of set here instead of having a magic 15 (even with the comment)?

@fcarreiro fcarreiro merged commit 29724f3 into master Nov 4, 2024
64 checks passed
@fcarreiro fcarreiro deleted the fc/avm-byte-pc branch November 4, 2024 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants