diff --git a/analysis/src/inference.rs b/analysis/src/inference.rs new file mode 100644 index 000000000..89f26d7cb --- /dev/null +++ b/analysis/src/inference.rs @@ -0,0 +1,176 @@ +use ast::asm_analysis::{AnalysisASMFile, Expression, FunctionStatement, Machine}; +use number::FieldElement; + +pub fn infer(file: AnalysisASMFile) -> Result, Vec> { + let mut errors = vec![]; + let mut res = AnalysisASMFile::default(); + + for (name, m) in file.machines { + match infer_machine(m) { + Ok(m) => { + res.machines.insert(name, m); + } + Err(e) => { + errors.extend(e); + } + } + } + + if !errors.is_empty() { + Err(errors) + } else { + Ok(res) + } +} + +fn infer_machine(mut machine: Machine) -> Result, Vec> { + let mut errors = vec![]; + + for f in machine.functions.iter_mut() { + for s in f.body.statements.iter_mut() { + if let FunctionStatement::Assignment(a) = s { + let expr_reg = match &*a.rhs { + Expression::FunctionCall(c) => { + let instr = machine + .instructions + .iter() + .find(|i| i.name == c.id) + .unwrap(); + let output = { + let outputs = instr.params.outputs.as_ref().unwrap(); + assert!(outputs.params.len() == 1); + &outputs.params[0] + }; + assert!(output.ty.is_none()); + Some(output.name.clone()) + } + _ => None, + }; + + match (&mut a.using_reg, expr_reg) { + (Some(using_reg), Some(expr_reg)) if *using_reg != expr_reg => { + errors.push(format!("Assignment register `{}` is incompatible with `{}`. Try replacing `<={}=` by `<==`.", using_reg, a.rhs, using_reg)); + } + (Some(_), _) => {} + (None, Some(expr_reg)) => { + // infer the assignment register to that of the rhs + a.using_reg = Some(expr_reg); + } + (None, None) => { + errors.push(format!("Impossible to infer the assignment register for `{a}`. Try being more explicit.")); + } + } + } + } + } + + if !errors.is_empty() { + Err(errors) + } else { + Ok(machine) + } +} + +#[cfg(test)] +mod tests { + use ast::asm_analysis::AssignmentStatement; + use number::Bn254Field; + + use crate::test_util::infer_str; + + use super::*; + + #[test] + fn inferred() { + let file = r#" + machine Incompatible { + reg pc[@pc]; + reg X[<=]; + reg Y[<=]; + reg A; + + instr foo -> X {} + + function main { + A <== foo(); + } + } + "#; + + let file = infer_str::(file).unwrap(); + + if let FunctionStatement::Assignment(AssignmentStatement { using_reg, .. }) = + &file.machines["Incompatible"].functions[0].body.statements[0] + { + assert_eq!(*using_reg, Some("X".to_string())); + } else { + panic!() + } + } + + #[test] + fn compatible() { + let file = r#" + machine Incompatible { + reg pc[@pc]; + reg X[<=]; + reg Y[<=]; + reg A; + + instr foo -> X {} + + function main { + A <=X= foo(); + } + } + "#; + + let file = infer_str::(file).unwrap(); + + if let FunctionStatement::Assignment(AssignmentStatement { using_reg, .. }) = + &file.machines["Incompatible"].functions[0].body.statements[0] + { + assert_eq!(*using_reg, Some("X".to_string())); + } else { + panic!() + } + } + + #[test] + fn incompatible() { + let file = r#" + machine Incompatible { + reg pc[@pc]; + reg X[<=]; + reg Y[<=]; + reg A; + + instr foo -> X {} + + function main { + A <=Y= foo(); + } + } + "#; + + assert_eq!(infer_str::(file).unwrap_err(), vec!["Assignment register `Y` is incompatible with `foo()`. Try replacing `<=Y=` by `<==`."]); + } + + #[test] + fn unclear() { + let file = r#" + machine Incompatible { + reg pc[@pc]; + reg X[<=]; + reg Y[<=]; + reg A; + + function main { + A <== 1; + } + } + "#; + + assert_eq!(infer_str::(file).unwrap_err(), vec!["Impossible to infer the assignment register for `A <== 1;`. Try being more explicit.".to_string()]); + } +} diff --git a/analysis/src/lib.rs b/analysis/src/lib.rs index c366f4d65..0878d7864 100644 --- a/analysis/src/lib.rs +++ b/analysis/src/lib.rs @@ -1,4 +1,5 @@ mod batcher; +mod inference; mod macro_expansion; mod romgen; mod type_check; @@ -12,7 +13,33 @@ use number::FieldElement; pub fn analyze(file: ASMFile) -> Result, Vec> { let expanded = macro_expansion::expand(file); let checked = type_check::check(expanded)?; - let rommed = romgen::generate_rom(checked); + let inferred = inference::infer(checked)?; + let rommed = romgen::generate_rom(inferred); let batched = batcher::batch(rommed); Ok(batched) } + +#[cfg(test)] +mod test_util { + use ast::{asm_analysis::AnalysisASMFile, parsed::asm::ASMFile}; + use number::FieldElement; + use parser::parse_asm; + + use crate::{inference, macro_expansion, type_check}; + + /// A test utility to process a source file until after macro expansion + pub fn expand_str(source: &str) -> ASMFile { + let file = parse_asm(None, source).unwrap(); + macro_expansion::expand(file) + } + + /// A test utility to process a source file until after type checking + pub fn check_str(source: &str) -> Result, Vec> { + type_check::check(expand_str(source)) + } + + /// A test utility to process a source file until after inference + pub fn infer_str(source: &str) -> Result, Vec> { + inference::infer(check_str(source).unwrap()) + } +} diff --git a/ast/src/asm_analysis/mod.rs b/ast/src/asm_analysis/mod.rs index 446a799fa..52e2e6f0c 100644 --- a/ast/src/asm_analysis/mod.rs +++ b/ast/src/asm_analysis/mod.rs @@ -6,17 +6,19 @@ use num_bigint::BigUint; use crate::parsed::{ asm::{InstructionBody, Params, RegisterFlag}, - Expression, PilStatement, + PilStatement, }; -#[derive(Clone)] +pub use crate::parsed::Expression; + +#[derive(Clone, Debug)] pub struct RegisterDeclarationStatement { pub start: usize, pub name: String, pub flag: Option, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct InstructionDefinitionStatement { pub start: usize, pub name: String, @@ -24,12 +26,12 @@ pub struct InstructionDefinitionStatement { pub body: InstructionBody, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FunctionBody { pub statements: Vec>, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct FunctionDefinitionStatement { pub start: usize, pub name: String, @@ -37,12 +39,12 @@ pub struct FunctionDefinitionStatement { pub body: FunctionBody, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct DegreeStatement { pub degree: BigUint, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum FunctionStatement { Assignment(AssignmentStatement), Instruction(InstructionStatement), @@ -74,7 +76,7 @@ impl From for FunctionStatement { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct AssignmentStatement { pub start: usize, pub lhs: Vec, @@ -82,32 +84,32 @@ pub struct AssignmentStatement { pub rhs: Box>, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct InstructionStatement { pub start: usize, pub instruction: String, pub inputs: Vec>, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct LabelStatement { pub start: usize, pub name: String, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct DebugDirective { pub start: usize, pub directive: crate::parsed::asm::DebugDirective, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PilBlock { pub start: usize, pub statements: Vec>, } -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] pub struct Machine { pub degree: Option, pub registers: Vec, @@ -130,12 +132,13 @@ impl Machine { } } -#[derive(Clone, Default)] +#[derive(Clone, Default, Debug)] pub struct Rom { pub statements: Vec>, pub batches: Option>, } +#[derive(Default, Debug)] pub struct AnalysisASMFile { pub machines: BTreeMap>, } diff --git a/book/src/asm/functions.md b/book/src/asm/functions.md index 59814f1dd..e67e9c481 100644 --- a/book/src/asm/functions.md +++ b/book/src/asm/functions.md @@ -38,8 +38,6 @@ One important requirement is for the assignment register of the assignment to be {{#include ../../../test_data/asm/book/function.asm:square}} ``` -> Specifying the assignment register of the assignment is currently required even in cases where it could be inferred. That restriction may be lifted in the future. - ### Instructions Instructions which do not return outputs can be used as statements. diff --git a/riscv/src/compiler.rs b/riscv/src/compiler.rs index 904917050..5546ef097 100644 --- a/riscv/src/compiler.rs +++ b/riscv/src/compiler.rs @@ -150,7 +150,7 @@ fn replace_dynamic_label_references( load_dynamic s10, LABEL which is then turned into - s10 <=X= load_label(LABEL) + s10 <== load_label(LABEL) It gets more complicated by the fact that sometimes, labels and debugging directives occur between the two statements @@ -280,7 +280,7 @@ fn store_data_objects<'a>( // code reference // TODO should be possible without temporary object_code.extend([ - format!("tmp1 <=X= load_label({});", escape_label(sym)), + format!("tmp1 <== load_label({});", escape_label(sym)), "mstore tmp1;".to_string(), ]); } @@ -876,7 +876,7 @@ fn only_if_no_write_to_zero_vec(statements: Vec, reg: Register) -> Vec Option<&'static str> { COPROCESSOR_SUBSTITUTIONS @@ -909,27 +909,27 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // Arithmetic "add" => { let (rd, r1, r2) = rrr(args); - only_if_no_write_to_zero(format!("{rd} <=X= wrap({r1} + {r2});"), rd) + only_if_no_write_to_zero(format!("{rd} <== wrap({r1} + {r2});"), rd) } "addi" => { let (rd, rs, imm) = rri(args); - only_if_no_write_to_zero(format!("{rd} <=X= wrap({rs} + {imm});"), rd) + only_if_no_write_to_zero(format!("{rd} <== wrap({rs} + {imm});"), rd) } "sub" => { let (rd, r1, r2) = rrr(args); - only_if_no_write_to_zero(format!("{rd} <=X= wrap_signed({r1} - {r2});"), rd) + only_if_no_write_to_zero(format!("{rd} <== wrap_signed({r1} - {r2});"), rd) } "neg" => { let (rd, r1) = rr(args); - only_if_no_write_to_zero(format!("{rd} <=X= wrap_signed(0 - {r1});"), rd) + only_if_no_write_to_zero(format!("{rd} <== wrap_signed(0 - {r1});"), rd) } "mul" => { let (rd, r1, r2) = rrr(args); - only_if_no_write_to_zero(format!("{rd} <=X= mul({r1}, {r2});"), rd) + only_if_no_write_to_zero(format!("{rd} <== mul({r1}, {r2});"), rd) } "mulhu" => { let (rd, r1, r2) = rrr(args); - only_if_no_write_to_zero(format!("{rd} <=X= mulhu({r1}, {r2});"), rd) + only_if_no_write_to_zero(format!("{rd} <== mulhu({r1}, {r2});"), rd) } "divu" => { let (rd, r1, r2) = rrr(args); @@ -939,31 +939,31 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // bitwise "xor" => { let (rd, r1, r2) = rrr(args); - only_if_no_write_to_zero(format!("{rd} <=X= xor({r1}, {r2});"), rd) + only_if_no_write_to_zero(format!("{rd} <== xor({r1}, {r2});"), rd) } "xori" => { let (rd, r1, imm) = rri(args); - only_if_no_write_to_zero(format!("{rd} <=X= xor({r1}, {imm});"), rd) + only_if_no_write_to_zero(format!("{rd} <== xor({r1}, {imm});"), rd) } "and" => { let (rd, r1, r2) = rrr(args); - only_if_no_write_to_zero(format!("{rd} <=X= and({r1}, {r2});"), rd) + only_if_no_write_to_zero(format!("{rd} <== and({r1}, {r2});"), rd) } "andi" => { let (rd, r1, imm) = rri(args); - only_if_no_write_to_zero(format!("{rd} <=X= and({r1}, {imm});"), rd) + only_if_no_write_to_zero(format!("{rd} <== and({r1}, {imm});"), rd) } "or" => { let (rd, r1, r2) = rrr(args); - only_if_no_write_to_zero(format!("{rd} <=X= or({r1}, {r2});"), rd) + only_if_no_write_to_zero(format!("{rd} <== or({r1}, {r2});"), rd) } "ori" => { let (rd, r1, imm) = rri(args); - only_if_no_write_to_zero(format!("{rd} <=X= or({r1}, {imm});"), rd) + only_if_no_write_to_zero(format!("{rd} <== or({r1}, {imm});"), rd) } "not" => { let (rd, rs) = rr(args); - only_if_no_write_to_zero(format!("{rd} <=X= wrap_signed(-{rs} - 1);"), rd) + only_if_no_write_to_zero(format!("{rd} <== wrap_signed(-{rs} - 1);"), rd) } // shift @@ -972,11 +972,11 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { assert!(amount <= 31); only_if_no_write_to_zero_vec( if amount <= 16 { - vec![format!("{rd} <=X= wrap16({rs} * {});", 1 << amount)] + vec![format!("{rd} <== wrap16({rs} * {});", 1 << amount)] } else { vec![ - format!("tmp1 <=X= wrap16({rs} * {});", 1 << 16), - format!("{rd} <=X= wrap16(tmp1 * {});", 1 << (amount - 16)), + format!("tmp1 <== wrap16({rs} * {});", 1 << 16), + format!("{rd} <== wrap16(tmp1 * {});", 1 << (amount - 16)), ] }, rd, @@ -986,8 +986,8 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { let (rd, r1, r2) = rrr(args); only_if_no_write_to_zero_vec( vec![ - format!("tmp1 <=X= and({r2}, 0x1f);"), - format!("{rd} <=X= shl({r1}, tmp1);"), + format!("tmp1 <== and({r2}, 0x1f);"), + format!("{rd} <== shl({r1}, tmp1);"), ], rd, ) @@ -996,15 +996,15 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // logical shift right let (rd, rs, amount) = rri(args); assert!(amount <= 31); - only_if_no_write_to_zero(format!("{rd} <=X= shr({rs}, {amount});"), rd) + only_if_no_write_to_zero(format!("{rd} <== shr({rs}, {amount});"), rd) } "srl" => { // logical shift right let (rd, r1, r2) = rrr(args); only_if_no_write_to_zero_vec( vec![ - format!("tmp1 <=X= and({r2}, 0x1f);"), - format!("{rd} <=X= shr({r1}, tmp1);"), + format!("tmp1 <== and({r2}, 0x1f);"), + format!("{rd} <== shr({r1}, tmp1);"), ], rd, ) @@ -1018,14 +1018,14 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { assert!(amount <= 31); only_if_no_write_to_zero_vec( vec![ - format!("tmp1 <=X= to_signed({rs});"), - format!("tmp1 <=Y= is_positive(0 - tmp1);"), + format!("tmp1 <== to_signed({rs});"), + format!("tmp1 <== is_positive(0 - tmp1);"), format!("tmp1 <=X= tmp1 * 0xffffffff;"), // Here, tmp1 is the full bit mask if rs is negative // and zero otherwise. - format!("{rd} <=X= xor(tmp1, {rs});"), - format!("{rd} <=X= shr({rd}, {amount});"), - format!("{rd} <=X= xor(tmp1, {rd});"), + format!("{rd} <== xor(tmp1, {rs});"), + format!("{rd} <== shr({rd}, {amount});"), + format!("{rd} <== xor(tmp1, {rd});"), ], rd, ) @@ -1044,7 +1044,7 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { let (rd, rs, imm) = rri(args); only_if_no_write_to_zero_vec( vec![ - format!("tmp1 <=X= to_signed({rs});"), + format!("tmp1 <== to_signed({rs});"), format!("{rd} <=Y= is_positive({} - tmp1);", imm as i32), ], rd, @@ -1053,8 +1053,8 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { "slt" => { let (rd, r1, r2) = rrr(args); vec![ - format!("tmp1 <=X= to_signed({r1});"), - format!("tmp2 <=X= to_signed({r2});"), + format!("tmp1 <== to_signed({r1});"), + format!("tmp2 <== to_signed({r2});"), format!("{rd} <=Y= is_positive(tmp2 - tmp1);"), ] } @@ -1069,7 +1069,7 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { "sgtz" => { let (rd, rs) = rr(args); vec![ - format!("tmp1 <=X= to_signed({rs});"), + format!("tmp1 <== to_signed({rs});"), format!("{rd} <=Y= is_positive(tmp1);"), ] } @@ -1091,7 +1091,7 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { "bgez" => { let (r1, label) = rl(args); vec![ - format!("tmp1 <=X= to_signed({r1});"), + format!("tmp1 <== to_signed({r1});"), format!("branch_if_positive tmp1 + 1, {label};"), ] } @@ -1104,8 +1104,8 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // Branch if r1 < r2 (signed). // TODO does this fulfill the input requirements for branch_if_positive? vec![ - format!("tmp1 <=X= to_signed({r1});"), - format!("tmp2 <=X= to_signed({r2});"), + format!("tmp1 <== to_signed({r1});"), + format!("tmp2 <== to_signed({r2});"), format!("branch_if_positive tmp2 - tmp1, {label};"), ] } @@ -1114,8 +1114,8 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // Branch if r1 >= r2 (signed). // TODO does this fulfill the input requirements for branch_if_positive? vec![ - format!("tmp1 <=X= to_signed({r1});"), - format!("tmp2 <=X= to_signed({r2});"), + format!("tmp1 <== to_signed({r1});"), + format!("tmp2 <== to_signed({r2});"), format!("branch_if_positive tmp1 - tmp2 + 1, {label};"), ] } @@ -1129,7 +1129,7 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // branch less or equal zero let (r1, label) = rl(args); vec![ - format!("tmp1 <=X= to_signed({r1});"), + format!("tmp1 <== to_signed({r1});"), format!("branch_if_positive -tmp1 + 1, {label};"), ] } @@ -1137,7 +1137,7 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // branch if 0 < r1 < 2**31 let (r1, label) = rl(args); vec![ - format!("tmp1 <=X= to_signed({r1});"), + format!("tmp1 <== to_signed({r1});"), format!("branch_if_positive tmp1, {label};"), ] } @@ -1226,8 +1226,8 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // TODO we need to consider misaligned loads / stores only_if_no_write_to_zero_vec( vec![ - format!("addr <=X= wrap({rs} + {off});"), - format!("{rd} <=X= mload();"), + format!("addr <== wrap({rs} + {off});"), + format!("{rd} <== mload();"), ], rd, ) @@ -1237,12 +1237,12 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { let (rd, rs, off) = rro(args); only_if_no_write_to_zero_vec( vec![ - format!("tmp1 <=X= wrap({rs} + {off});"), - "addr <=X= and(tmp1, 0xfffffffc);".to_string(), - "tmp2 <=X= and(tmp1, 0x3);".to_string(), - format!("{rd} <=X= mload();"), - format!("{rd} <=X= shr({rd}, 8 * tmp2);"), - format!("{rd} <=X= sign_extend_byte({rd});"), + format!("tmp1 <== wrap({rs} + {off});"), + "addr <== and(tmp1, 0xfffffffc);".to_string(), + "tmp2 <== and(tmp1, 0x3);".to_string(), + format!("{rd} <== mload();"), + format!("{rd} <== shr({rd}, 8 * tmp2);"), + format!("{rd} <== sign_extend_byte({rd});"), ], rd, ) @@ -1252,12 +1252,12 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { let (rd, rs, off) = rro(args); only_if_no_write_to_zero_vec( vec![ - format!("tmp1 <=X= wrap({rs} + {off});"), - "addr <=X= and(tmp1, 0xfffffffc);".to_string(), - "tmp2 <=X= and(tmp1, 0x3);".to_string(), - format!("{rd} <=X= mload();"), - format!("{rd} <=X= shr({rd}, 8 * tmp2);"), - format!("{rd} <=X= and({rd}, 0xff);"), + format!("tmp1 <== wrap({rs} + {off});"), + "addr <== and(tmp1, 0xfffffffc);".to_string(), + "tmp2 <== and(tmp1, 0x3);".to_string(), + format!("{rd} <== mload();"), + format!("{rd} <== shr({rd}, 8 * tmp2);"), + format!("{rd} <== and({rd}, 0xff);"), ], rd, ) @@ -1265,7 +1265,7 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { "sw" => { let (r1, r2, off) = rro(args); vec![ - format!("addr <=X= wrap({r2} + {off});"), + format!("addr <== wrap({r2} + {off});"), format!("mstore {r1};"), ] } @@ -1276,16 +1276,16 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { let (rs, rd, off) = rro(args); vec![ - format!("tmp1 <=X= wrap({rd} + {off});"), - "addr <=X= and(tmp1, 0xfffffffc);".to_string(), - "tmp2 <=X= and(tmp1, 0x3);".to_string(), - "tmp1 <=X= mload();".to_string(), - "tmp3 <=X= shl(0xffff, 8 * tmp2);".to_string(), - "tmp3 <=X= xor(tmp3, 0xffffffff);".to_string(), - "tmp1 <=X= and(tmp1, tmp3);".to_string(), - format!("tmp3 <=X= and({rs}, 0xffff);"), - "tmp3 <=X= shl(tmp3, 8 * tmp2);".to_string(), - "tmp1 <=X= or(tmp1, tmp3);".to_string(), + format!("tmp1 <== wrap({rd} + {off});"), + "addr <== and(tmp1, 0xfffffffc);".to_string(), + "tmp2 <== and(tmp1, 0x3);".to_string(), + "tmp1 <== mload();".to_string(), + "tmp3 <== shl(0xffff, 8 * tmp2);".to_string(), + "tmp3 <== xor(tmp3, 0xffffffff);".to_string(), + "tmp1 <== and(tmp1, tmp3);".to_string(), + format!("tmp3 <== and({rs}, 0xffff);"), + "tmp3 <== shl(tmp3, 8 * tmp2);".to_string(), + "tmp1 <== or(tmp1, tmp3);".to_string(), "mstore tmp1;".to_string(), ] } @@ -1293,16 +1293,16 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // store byte let (rs, rd, off) = rro(args); vec![ - format!("tmp1 <=X= wrap({rd} + {off});"), - "addr <=X= and(tmp1, 0xfffffffc);".to_string(), - "tmp2 <=X= and(tmp1, 0x3);".to_string(), - "tmp1 <=X= mload();".to_string(), - "tmp3 <=X= shl(0xff, 8 * tmp2);".to_string(), - "tmp3 <=X= xor(tmp3, 0xffffffff);".to_string(), - "tmp1 <=X= and(tmp1, tmp3);".to_string(), - format!("tmp3 <=X= and({rs}, 0xff);"), - "tmp3 <=X= shl(tmp3, 8 * tmp2);".to_string(), - "tmp1 <=X= or(tmp1, tmp3);".to_string(), + format!("tmp1 <== wrap({rd} + {off});"), + "addr <== and(tmp1, 0xfffffffc);".to_string(), + "tmp2 <== and(tmp1, 0x3);".to_string(), + "tmp1 <== mload();".to_string(), + "tmp3 <== shl(0xff, 8 * tmp2);".to_string(), + "tmp3 <== xor(tmp3, 0xffffffff);".to_string(), + "tmp1 <== and(tmp1, tmp3);".to_string(), + format!("tmp3 <== and({rs}, 0xff);"), + "tmp3 <== shl(tmp3, 8 * tmp2);".to_string(), + "tmp1 <== or(tmp1, tmp3);".to_string(), "mstore tmp1;".to_string(), ] } @@ -1312,7 +1312,7 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec { // Special instruction that is inserted to allow dynamic label references "load_dynamic" => { let (rd, label) = rl(args); - only_if_no_write_to_zero(format!("{rd} <=X= load_label({label});"), rd) + only_if_no_write_to_zero(format!("{rd} <== load_label({label});"), rd) } _ => { diff --git a/test_data/asm/book/function.asm b/test_data/asm/book/function.asm index 64ed7c58e..517cad4ea 100644 --- a/test_data/asm/book/function.asm +++ b/test_data/asm/book/function.asm @@ -50,7 +50,7 @@ machine Machine { // ANCHOR_END: read_register // square `A` // ANCHOR: instruction - A <=Y= square(A); + A <== square(A); // ANCHOR_END: instruction // jump back to `start` jmp start; diff --git a/test_data/asm/book/hello_world.asm b/test_data/asm/book/hello_world.asm index 9868140b5..1f9ce1f91 100644 --- a/test_data/asm/book/hello_world.asm +++ b/test_data/asm/book/hello_world.asm @@ -33,8 +33,8 @@ machine HelloWorld { // the main function assigns the first prover input to A, increments it, decrements it, and loops forever function main { A <=X= ${ ("input", 0) }; - A <=Y= incr(A); - A <=Y= decr(A); + A <== incr(A); + A <== decr(A); assert_zero A; loop; }