diff --git a/src/asm_compiler/mod.rs b/src/asm_compiler/mod.rs index dd0ff04cf..b7322644c 100644 --- a/src/asm_compiler/mod.rs +++ b/src/asm_compiler/mod.rs @@ -161,12 +161,12 @@ impl ASMPILConverter { fn handle_instruction_def( &mut self, start: &usize, - body: &Vec, + body: &Vec, name: &String, params: &Vec, ) { - let col_name = format!("instr_{name}"); - self.create_witness_fixed_pair(*start, &col_name); + let instruction_flag = format!("instr_{name}"); + self.create_witness_fixed_pair(*start, &instruction_flag); // it's part of the lookup! //self.pil.push(constrain_zero_one(&col_name)); @@ -181,19 +181,40 @@ impl ASMPILConverter { } for expr in body { - let expr = substitute(expr, &substitutions); - match extract_update(expr) { - (Some(var), expr) => { - self.registers - .get_mut(&var) - .unwrap() - .conditioned_updates - .push((direct_reference(&col_name), expr)); + match expr { + InstructionBodyElement::Expression(expr) => { + let expr = substitute(expr, &substitutions); + match extract_update(expr) { + (Some(var), expr) => { + self.registers + .get_mut(&var) + .unwrap() + .conditioned_updates + .push((direct_reference(&instruction_flag), expr)); + } + (None, expr) => self.pil.push(Statement::PolynomialIdentity( + 0, + build_mul(direct_reference(&instruction_flag), expr.clone()), + )), + } + } + InstructionBodyElement::PlookupIdentity(left, right) => { + assert!(left.selector.is_none(), "LHS selector not supported, could and-combine with instruction flag later."); + self.pil.push(Statement::PlookupIdentity( + *start, + SelectedExpressions { + selector: Some(direct_reference(&instruction_flag)), + expressions: substitute_vec(&left.expressions, &substitutions), + }, + SelectedExpressions { + selector: right + .selector + .as_ref() + .map(|s| substitute(s, &substitutions)), + expressions: substitute_vec(&right.expressions, &substitutions), + }, + )); } - (None, expr) => self.pil.push(Statement::PolynomialIdentity( - 0, - build_mul(direct_reference(&col_name), expr.clone()), - )), } } let instr = Instruction { @@ -223,30 +244,42 @@ impl ASMPILConverter { let instr = &self.instructions[instr_name]; assert_eq!(instr.params.len(), args.len()); let mut value = vec![]; - let instruction_literal_args = instr - .params - .iter() - .zip(args) - .map(|(p, a)| { - if p.assignment_reg.0.is_some() || p.assignment_reg.1.is_some() { - // TODO this ignores which param it is, but it's ok - // TODO if we have more than one assignment op, we cannot just use - // value here anymore. But I guess the same is for assignment. - // TODO check that we do not use the same assignment var twice - value = self.process_assignment_value(a); - None - } else if p.param_type == Some("label".to_string()) { - if let Expression::PolynomialReference(r) = a { - Some(r.name.clone()) - } else { - panic!(); - } + let mut instruction_literal_args = vec![]; + let mut write_reg = None; + for (p, a) in instr.params.iter().zip(args) { + // TODO literal arguments can actually only be passed in. + if p.assignment_reg.0.is_some() { + // TODO this ignores which param it is, but it's ok + // TODO if we have more than one assignment op, we cannot just use + // value here anymore. But I guess the same is for assignment. + // TODO check that we do not use the same assignment var twice + assert!(value.is_empty()); + value = self.process_assignment_value(a); + instruction_literal_args.push(None); + } else if p.assignment_reg.1.is_some() { + // TODO this ignores which param it is, but it's ok + // TODO if we have more than one assignment op, we cannot just use + // value here anymore. But I guess the same is for assignment. + if let Expression::PolynomialReference(r) = a { + assert!(write_reg.is_none()); + write_reg = Some(r.name.clone()); } else { - todo!("Param type not supported."); + panic!("Expected direct register to assign to in instruction call."); } - }) - .collect(); + instruction_literal_args.push(None); + } else if p.param_type == Some("label".to_string()) { + if let Expression::PolynomialReference(r) = a { + instruction_literal_args.push(Some(r.name.clone())) + } else { + panic!(); + } + } else { + todo!("Param type not supported."); + } + } + assert_eq!(instruction_literal_args.len(), instr.params.len()); self.code_lines.push(CodeLine { + write_reg, instruction: Some(instr_name.clone()), value, instruction_literal_args, @@ -633,6 +666,10 @@ fn substitute(input: &Expression, substitution: &HashMap) -> Exp } } +fn substitute_vec(input: &[Expression], substitution: &HashMap) -> Vec { + input.iter().map(|e| substitute(e, substitution)).collect() +} + fn substitute_string(input: &String, substitution: &HashMap) -> String { substitution.get(input).unwrap_or(input).clone() } diff --git a/src/commit_evaluator/mod.rs b/src/commit_evaluator/mod.rs index 89adab7b9..3db5c2210 100644 --- a/src/commit_evaluator/mod.rs +++ b/src/commit_evaluator/mod.rs @@ -188,9 +188,11 @@ where break; } } - if identity_failed && self.next.iter().any(|v| v.is_none()) { + // Identity check failure on the first row is not fatal. We will proceed with + // "unknown", report zero and re-check the wrap-around against the zero values at the end. + if identity_failed && next_row != 0 { eprintln!( - "\nError: Row {next_row}: Unable to derive values for committed polynomials: {}\n", + "\nError: Row {next_row}: Identity check failer or unable to derive values for committed polynomials: {}\n", self.next .iter() .enumerate() @@ -310,7 +312,23 @@ where } fn process_plookup(&mut self, identity: &Identity) -> EvalResult { - if identity.left.selector.is_some() || identity.right.selector.is_some() { + if let Some(left_selector) = &identity.left.selector { + let value = self.evaluate(left_selector, EvaluationRow::Next)?; + match value.constant_value() { + Some(v) if v == 0.into() => { + return Ok(vec![]); + } + Some(v) if v == 1.into() => {} + _ => { + return Err(format!( + "Value of the selector on the left hand side unknown or not boolean: {}", + self.format_affine_expression(&value) + ) + .into()) + } + }; + } + if identity.right.selector.is_some() { return Err("Selectors not yet supported.".to_string().into()); } let left = identity diff --git a/src/constant_evaluator/mod.rs b/src/constant_evaluator/mod.rs index b3ebf8b2c..8e38da3aa 100644 --- a/src/constant_evaluator/mod.rs +++ b/src/constant_evaluator/mod.rs @@ -88,7 +88,8 @@ impl<'a> Evaluator<'a> { Expression::FunctionCall(name, args) => { let arg_values = args.iter().map(|a| self.evaluate(a)).collect::>(); assert!(arg_values.len() == 1); - self.other_constants[name][abstract_to_degree(&arg_values[0]) as usize].clone() + let values = &self.other_constants[name]; + values[abstract_to_degree(&arg_values[0]) as usize % values.len()].clone() } } } diff --git a/src/parser/asm_ast.rs b/src/parser/asm_ast.rs index 55500d805..c4ad9b2f2 100644 --- a/src/parser/asm_ast.rs +++ b/src/parser/asm_ast.rs @@ -1,4 +1,4 @@ -use super::ast::{Expression, Statement}; +use super::ast::{Expression, SelectedExpressions, Statement}; #[derive(Debug, PartialEq, Eq)] pub struct ASMFile(pub Vec); @@ -6,7 +6,12 @@ pub struct ASMFile(pub Vec); #[derive(Debug, PartialEq, Eq, Clone)] pub enum ASMStatement { RegisterDeclaration(usize, String, Option), - InstructionDeclaration(usize, String, Vec, Vec), + InstructionDeclaration( + usize, + String, + Vec, + Vec, + ), InlinePil(usize, Vec), Assignment(usize, Vec, Option, Box), Instruction(usize, String, Vec), @@ -28,3 +33,9 @@ pub struct InstructionParam { /// assign register inside the arrow is optional as well. pub assignment_reg: (Option>, Option>), } + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum InstructionBodyElement { + Expression(Expression), + PlookupIdentity(SelectedExpressions, SelectedExpressions), +} diff --git a/src/parser/powdr.lalrpop b/src/parser/powdr.lalrpop index a7fa596d1..9668553ba 100644 --- a/src/parser/powdr.lalrpop +++ b/src/parser/powdr.lalrpop @@ -158,15 +158,16 @@ RegisterFlag: RegisterFlag = { } InstructionDeclaration: ASMStatement = { - <@L> "instr" "{" "}" => ASMStatement::InstructionDeclaration(<>) + <@L> "instr" "{" "}" => ASMStatement::InstructionDeclaration(<>) } -EqualityExpressionList: Vec = { - "," )*> => { list.push(end); list } +InstructionBodyElements: Vec = { + "," )*> => { list.push(end); list } } -EqualityExpression: Expression = { - "=" => Expression::BinaryOperation(l, BinaryOperator::Sub, r) +InstructionBodyElement: InstructionBodyElement = { + "=" => InstructionBodyElement::Expression(Expression::BinaryOperation(l, BinaryOperator::Sub, r)), + "in" => InstructionBodyElement::PlookupIdentity(<>), } InstructionParamList: Vec = { diff --git a/tests/integration.rs b/tests/integration.rs index 7eda69bc5..3a6414953 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -133,3 +133,12 @@ fn simple_sum_asm() { [16, 4, 1, 2, 8, 5].iter().map(|&x| x.into()).collect(), ); } + +#[test] +#[ignore] +fn palindrome() { + verify_asm( + "palindrome.asm", + [7, 1, 7, 3, 9, 3, 7, 1].iter().map(|&x| x.into()).collect(), + ); +}