Merge pull request #1095 from mir-protocol/jacqui/push0-opcode

PUSH0
This commit is contained in:
Jacqueline Nabaglo 2023-06-14 22:06:18 -07:00 committed by GitHub
commit 23bc390a83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 65 additions and 17 deletions

View File

@ -41,6 +41,7 @@ pub struct OpsColumnsView<T: Copy> {
pub jumpi: T, // Note: This column must be 0 when is_cpu_cycle = 0.
pub pc: T,
pub jumpdest: T,
pub push0: T,
pub push: T,
pub dup: T,
pub swap: T,

View File

@ -8,7 +8,7 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer
use crate::cpu::columns::{CpuColumnsView, COL_MAP};
use crate::cpu::kernel::aggregator::KERNEL;
const NATIVE_INSTRUCTIONS: [usize; 31] = [
const NATIVE_INSTRUCTIONS: [usize; 32] = [
COL_MAP.op.add,
COL_MAP.op.mul,
COL_MAP.op.sub,
@ -36,6 +36,7 @@ const NATIVE_INSTRUCTIONS: [usize; 31] = [
// not JUMPI (possible need to jump)
COL_MAP.op.pc,
COL_MAP.op.jumpdest,
COL_MAP.op.push0,
// not PUSH (need to increment by more than 1)
COL_MAP.op.dup,
COL_MAP.op.swap,

View File

@ -14,7 +14,7 @@ use crate::cpu::columns::{CpuColumnsView, COL_MAP, NUM_CPU_COLUMNS};
use crate::cpu::membus::NUM_GP_CHANNELS;
use crate::cpu::{
bootstrap_kernel, contextops, control_flow, decode, dup_swap, exceptions, gas, jumps, membus,
memio, modfp254, pc, shift, simple_logic, stack, stack_bounds, syscalls,
memio, modfp254, pc, push0, shift, simple_logic, stack, stack_bounds, syscalls,
};
use crate::cross_table_lookup::{Column, TableWithColumns};
use crate::memory::segments::Segment;
@ -197,6 +197,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
memio::eval_packed(local_values, yield_constr);
modfp254::eval_packed(local_values, yield_constr);
pc::eval_packed(local_values, yield_constr);
push0::eval_packed(local_values, yield_constr);
shift::eval_packed(local_values, yield_constr);
simple_logic::eval_packed(local_values, yield_constr);
stack::eval_packed(local_values, yield_constr);
@ -224,6 +225,7 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for CpuStark<F, D
memio::eval_ext_circuit(builder, local_values, yield_constr);
modfp254::eval_ext_circuit(builder, local_values, yield_constr);
pc::eval_ext_circuit(builder, local_values, yield_constr);
push0::eval_ext_circuit(builder, local_values, yield_constr);
shift::eval_ext_circuit(builder, local_values, yield_constr);
simple_logic::eval_ext_circuit(builder, local_values, yield_constr);
stack::eval_ext_circuit(builder, local_values, yield_constr);

View File

@ -22,7 +22,7 @@ use crate::cpu::columns::{CpuColumnsView, COL_MAP};
/// behavior.
/// Note: invalid opcodes are not represented here. _Any_ opcode is permitted to decode to
/// `is_invalid`. The kernel then verifies that the opcode was _actually_ invalid.
const OPCODES: [(u8, usize, bool, usize); 36] = [
const OPCODES: [(u8, usize, bool, usize); 37] = [
// (start index of block, number of top bits to check (log2), kernel-only, flag column)
(0x01, 0, false, COL_MAP.op.add),
(0x02, 0, false, COL_MAP.op.mul),
@ -52,6 +52,7 @@ const OPCODES: [(u8, usize, bool, usize); 36] = [
(0x57, 0, false, COL_MAP.op.jumpi),
(0x58, 0, false, COL_MAP.op.pc),
(0x5b, 0, false, COL_MAP.op.jumpdest),
(0x5f, 0, false, COL_MAP.op.push0),
(0x60, 5, false, COL_MAP.op.push), // 0x60-0x7f
(0x80, 4, false, COL_MAP.op.dup), // 0x80-0x8f
(0x90, 4, false, COL_MAP.op.swap), // 0x90-0x9f

View File

@ -47,6 +47,7 @@ const SIMPLE_OPCODES: OpsColumnsView<Option<u32>> = OpsColumnsView {
jumpi: G_HIGH,
pc: G_BASE,
jumpdest: G_JUMPDEST,
push0: G_BASE,
push: G_VERYLOW,
dup: G_VERYLOW,
swap: G_VERYLOW,

View File

@ -227,11 +227,11 @@ min_stack_len_for_opcode:
BYTES 0 // 0x59, MSIZE
BYTES 0 // 0x5a, GAS
BYTES 0 // 0x5b, JUMPDEST
%rep 4 // 0x5c-0x5f, invalid
%rep 3 // 0x5c-0x5e, invalid
BYTES 0
%endrep
%rep 32 // 0x60-0x7f, PUSH1-PUSH32
%rep 33 // 0x5f-0x7f, PUSH0-PUSH32
BYTES 0
%endrep

View File

@ -37,7 +37,7 @@ pub const STACK_LENGTH_INCREASING_OPCODES_USER: U256 = u256_from_set_index_range
0x3d..=0x3d, // RETURNDATASIZE
0x41..=0x48, // COINBASE, TIMESTAMP, NUMBER, DIFFICULTY, GASLIMIT, CHAINID, SELFBALANCE, BASEFEE
0x58..=0x5a, // PC, MSIZE, GAS
0x60..=0x8f, // PUSH*, DUP*
0x5f..=0x8f, // PUSH*, DUP*
]);
pub const INVALID_OPCODES_USER: U256 = u256_from_set_index_ranges([
@ -45,7 +45,7 @@ pub const INVALID_OPCODES_USER: U256 = u256_from_set_index_ranges([
0x1e..=0x1f,
0x21..=0x2f,
0x49..=0x4f,
0x5c..=0x5f,
0x5c..=0x5e,
0xa5..=0xef,
0xf6..=0xf9,
0xfb..=0xfc,

View File

@ -369,7 +369,7 @@ impl<'a> Interpreter<'a> {
0x59 => self.run_msize(), // "MSIZE",
0x5a => todo!(), // "GAS",
0x5b => self.run_jumpdest(), // "JUMPDEST",
x if (0x60..0x80).contains(&x) => self.run_push(x - 0x5f), // "PUSH"
x if (0x5f..0x80).contains(&x) => self.run_push(x - 0x5f), // "PUSH"
x if (0x80..0x90).contains(&x) => self.run_dup(x - 0x7f), // "DUP"
x if (0x90..0xa0).contains(&x) => self.run_swap(x - 0x8f)?, // "SWAP"
0xa0 => todo!(), // "LOG0",
@ -1189,6 +1189,7 @@ fn get_mnemonic(opcode: u8) -> &'static str {
0x59 => "MSIZE",
0x5a => "GAS",
0x5b => "JUMPDEST",
0x5f => "PUSH0",
0x60 => "PUSH1",
0x61 => "PUSH2",
0x62 => "PUSH3",

View File

@ -1,8 +1,7 @@
/// The opcode of the `PUSH[n]` instruction, given a byte count `n`.
pub fn get_push_opcode(n: u8) -> u8 {
assert!(n > 0);
assert!(n <= 32);
0x60 + n - 1
0x5f + n
}
/// The opcode of a standard instruction (not a `PUSH`).

View File

@ -26,7 +26,7 @@ where
}
pub(crate) fn u256_to_trimmed_be_bytes(u256: &U256) -> Vec<u8> {
let num_bytes = ceil_div_usize(u256.bits(), 8).max(1);
let num_bytes = ceil_div_usize(u256.bits(), 8);
// `byte` is little-endian, so we manually reverse it.
(0..num_bytes).rev().map(|i| u256.byte(i)).collect()
}
@ -60,7 +60,9 @@ mod tests {
#[test]
fn literal_to_be_bytes() {
assert_eq!(u256_to_trimmed_be_bytes(&0.into()), vec![0x00]);
assert_eq!(u256_to_trimmed_be_bytes(&0.into()), Vec::<u8>::new());
assert_eq!(u256_to_trimmed_be_bytes(&1.into()), vec![0x01]);
assert_eq!(u256_to_trimmed_be_bytes(&768.into()), vec![0x03, 0x00]);

View File

@ -13,6 +13,7 @@ pub(crate) mod membus;
mod memio;
mod modfp254;
mod pc;
mod push0;
mod shift;
pub(crate) mod simple_logic;
mod stack;

32
evm/src/cpu/push0.rs Normal file
View File

@ -0,0 +1,32 @@
use plonky2::field::extension::Extendable;
use plonky2::field::packed::PackedField;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
use crate::cpu::columns::CpuColumnsView;
use crate::cpu::membus::NUM_GP_CHANNELS;
pub fn eval_packed<P: PackedField>(
lv: &CpuColumnsView<P>,
yield_constr: &mut ConstraintConsumer<P>,
) {
let filter = lv.op.push0;
let push_value = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
for limb in push_value {
yield_constr.constraint(filter * limb);
}
}
pub fn eval_ext_circuit<F: RichField + Extendable<D>, const D: usize>(
builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder<F, D>,
lv: &CpuColumnsView<ExtensionTarget<D>>,
yield_constr: &mut RecursiveConstraintConsumer<F, D>,
) {
let filter = lv.op.push0;
let push_value = lv.mem_channels[NUM_GP_CHANNELS - 1].value;
for limb in push_value {
let constr = builder.mul_extension(filter, limb);
yield_constr.constraint(builder, constr);
}
}

View File

@ -97,6 +97,11 @@ const STACK_BEHAVIORS: OpsColumnsView<Option<StackBehavior>> = OpsColumnsView {
pushes: false,
disable_other_channels: true,
}),
push0: Some(StackBehavior {
num_pops: 0,
pushes: true,
disable_other_channels: true,
}),
push: None, // TODO
dup: None,
swap: None,

View File

@ -38,7 +38,8 @@ pub(crate) fn gas_to_charge(op: Operation) -> u64 {
Jumpi => G_HIGH,
Pc => G_BASE,
Jumpdest => G_JUMPDEST,
Push(_) => G_VERYLOW,
Push(0) => G_BASE,
Push(1..) => G_VERYLOW,
Dup(_) => G_VERYLOW,
Swap(_) => G_VERYLOW,
GetContext => KERNEL_ONLY_INSTR,

View File

@ -335,7 +335,7 @@ pub(crate) fn generate_push<F: Field>(
mut row: CpuColumnsView<F>,
) -> Result<(), ProgramError> {
let code_context = state.registers.code_context();
let num_bytes = n as usize + 1;
let num_bytes = n as usize;
let initial_offset = state.registers.program_counter + 1;
// First read val without going through `mem_read_with_log` type methods, so we can pass it

View File

@ -113,7 +113,7 @@ fn decode(registers: RegistersState, opcode: u8) -> Result<Operation, ProgramErr
(0x59, _) => Ok(Operation::Syscall(opcode, 0, true)), // MSIZE
(0x5a, _) => Ok(Operation::Syscall(opcode, 0, true)), // GAS
(0x5b, _) => Ok(Operation::Jumpdest),
(0x60..=0x7f, _) => Ok(Operation::Push(opcode & 0x1f)),
(0x5f..=0x7f, _) => Ok(Operation::Push(opcode - 0x5f)),
(0x80..=0x8f, _) => Ok(Operation::Dup(opcode & 0xf)),
(0x90..=0x9f, _) => Ok(Operation::Swap(opcode & 0xf)),
(0xa0, _) => Ok(Operation::Syscall(opcode, 2, false)), // LOG0
@ -152,7 +152,8 @@ fn decode(registers: RegistersState, opcode: u8) -> Result<Operation, ProgramErr
fn fill_op_flag<F: Field>(op: Operation, row: &mut CpuColumnsView<F>) {
let flags = &mut row.op;
*match op {
Operation::Push(_) => &mut flags.push,
Operation::Push(0) => &mut flags.push0,
Operation::Push(1..) => &mut flags.push,
Operation::Dup(_) => &mut flags.dup,
Operation::Swap(_) => &mut flags.swap,
Operation::Iszero => &mut flags.iszero,
@ -231,7 +232,7 @@ fn perform_op<F: Field>(
state.registers.program_counter += match op {
Operation::Syscall(_, _, _) | Operation::ExitKernel => 0,
Operation::Push(n) => n as usize + 2,
Operation::Push(n) => n as usize + 1,
Operation::Jump | Operation::Jumpi => 0,
_ => 1,
};