mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-04-20 03:03:25 -04:00
infer the assignment register when assigning the output of an instruction
This commit is contained in:
176
analysis/src/inference.rs
Normal file
176
analysis/src/inference.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
use ast::asm_analysis::{AnalysisASMFile, Expression, FunctionStatement, Machine};
|
||||
use number::FieldElement;
|
||||
|
||||
pub fn infer<T: FieldElement>(file: AnalysisASMFile<T>) -> Result<AnalysisASMFile<T>, Vec<String>> {
|
||||
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<T: FieldElement>(mut machine: Machine<T>) -> Result<Machine<T>, Vec<String>> {
|
||||
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::<Bn254Field>(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::<Bn254Field>(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::<Bn254Field>(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::<Bn254Field>(file).unwrap_err(), vec!["Impossible to infer the assignment register for `A <== 1;`. Try being more explicit.".to_string()]);
|
||||
}
|
||||
}
|
||||
@@ -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<T: FieldElement>(file: ASMFile<T>) -> Result<AnalysisASMFile<T>, Vec<String>> {
|
||||
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<T: FieldElement>(source: &str) -> ASMFile<T> {
|
||||
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<T: FieldElement>(source: &str) -> Result<AnalysisASMFile<T>, Vec<String>> {
|
||||
type_check::check(expand_str(source))
|
||||
}
|
||||
|
||||
/// A test utility to process a source file until after inference
|
||||
pub fn infer_str<T: FieldElement>(source: &str) -> Result<AnalysisASMFile<T>, Vec<String>> {
|
||||
inference::infer(check_str(source).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<RegisterFlag>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InstructionDefinitionStatement<T> {
|
||||
pub start: usize,
|
||||
pub name: String,
|
||||
@@ -24,12 +26,12 @@ pub struct InstructionDefinitionStatement<T> {
|
||||
pub body: InstructionBody<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionBody<T> {
|
||||
pub statements: Vec<FunctionStatement<T>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionDefinitionStatement<T> {
|
||||
pub start: usize,
|
||||
pub name: String,
|
||||
@@ -37,12 +39,12 @@ pub struct FunctionDefinitionStatement<T> {
|
||||
pub body: FunctionBody<T>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DegreeStatement {
|
||||
pub degree: BigUint,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FunctionStatement<T> {
|
||||
Assignment(AssignmentStatement<T>),
|
||||
Instruction(InstructionStatement<T>),
|
||||
@@ -74,7 +76,7 @@ impl<T> From<DebugDirective> for FunctionStatement<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AssignmentStatement<T> {
|
||||
pub start: usize,
|
||||
pub lhs: Vec<String>,
|
||||
@@ -82,32 +84,32 @@ pub struct AssignmentStatement<T> {
|
||||
pub rhs: Box<Expression<T>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InstructionStatement<T> {
|
||||
pub start: usize,
|
||||
pub instruction: String,
|
||||
pub inputs: Vec<Expression<T>>,
|
||||
}
|
||||
|
||||
#[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<T> {
|
||||
pub start: usize,
|
||||
pub statements: Vec<PilStatement<T>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Machine<T> {
|
||||
pub degree: Option<DegreeStatement>,
|
||||
pub registers: Vec<RegisterDeclarationStatement>,
|
||||
@@ -130,12 +132,13 @@ impl<T> Machine<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Rom<T> {
|
||||
pub statements: Vec<FunctionStatement<T>>,
|
||||
pub batches: Option<Vec<BatchMetadata>>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct AnalysisASMFile<T> {
|
||||
pub machines: BTreeMap<String, Machine<T>>,
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<String>, reg: Register) -> Vec<S
|
||||
}
|
||||
|
||||
static COPROCESSOR_SUBSTITUTIONS: &[(&str, &str)] =
|
||||
&[("poseidon_coprocessor", "x10 <=X= poseidon(x10, x11);")];
|
||||
&[("poseidon_coprocessor", "x10 <== poseidon(x10, x11);")];
|
||||
|
||||
fn try_coprocessor_substitution(label: &str) -> Option<&'static str> {
|
||||
COPROCESSOR_SUBSTITUTIONS
|
||||
@@ -909,27 +909,27 @@ fn process_instruction(instr: &str, args: &[Argument]) -> Vec<String> {
|
||||
// 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<String> {
|
||||
// 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<String> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
// 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<String> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
"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<String> {
|
||||
"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<String> {
|
||||
"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<String> {
|
||||
// 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<String> {
|
||||
// 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<String> {
|
||||
// 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<String> {
|
||||
// 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<String> {
|
||||
// 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<String> {
|
||||
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<String> {
|
||||
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<String> {
|
||||
"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<String> {
|
||||
|
||||
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<String> {
|
||||
// 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<String> {
|
||||
// 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)
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user