mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-05-13 03:00:26 -04:00
Merge pull request #76 from chriseth/lookups_in_instructions
Support lookups in instructions.
This commit is contained in:
@@ -161,12 +161,12 @@ impl ASMPILConverter {
|
||||
fn handle_instruction_def(
|
||||
&mut self,
|
||||
start: &usize,
|
||||
body: &Vec<Expression>,
|
||||
body: &Vec<InstructionBodyElement>,
|
||||
name: &String,
|
||||
params: &Vec<InstructionParam>,
|
||||
) {
|
||||
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<String, String>) -> Exp
|
||||
}
|
||||
}
|
||||
|
||||
fn substitute_vec(input: &[Expression], substitution: &HashMap<String, String>) -> Vec<Expression> {
|
||||
input.iter().map(|e| substitute(e, substitution)).collect()
|
||||
}
|
||||
|
||||
fn substitute_string(input: &String, substitution: &HashMap<String, String>) -> String {
|
||||
substitution.get(input).unwrap_or(input).clone()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -88,7 +88,8 @@ impl<'a> Evaluator<'a> {
|
||||
Expression::FunctionCall(name, args) => {
|
||||
let arg_values = args.iter().map(|a| self.evaluate(a)).collect::<Vec<_>>();
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::ast::{Expression, Statement};
|
||||
use super::ast::{Expression, SelectedExpressions, Statement};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ASMFile(pub Vec<ASMStatement>);
|
||||
@@ -6,7 +6,12 @@ pub struct ASMFile(pub Vec<ASMStatement>);
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum ASMStatement {
|
||||
RegisterDeclaration(usize, String, Option<RegisterFlag>),
|
||||
InstructionDeclaration(usize, String, Vec<InstructionParam>, Vec<Expression>),
|
||||
InstructionDeclaration(
|
||||
usize,
|
||||
String,
|
||||
Vec<InstructionParam>,
|
||||
Vec<InstructionBodyElement>,
|
||||
),
|
||||
InlinePil(usize, Vec<Statement>),
|
||||
Assignment(usize, Vec<String>, Option<String>, Box<Expression>),
|
||||
Instruction(usize, String, Vec<Expression>),
|
||||
@@ -28,3 +33,9 @@ pub struct InstructionParam {
|
||||
/// assign register inside the arrow is optional as well.
|
||||
pub assignment_reg: (Option<Option<String>>, Option<Option<String>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum InstructionBodyElement {
|
||||
Expression(Expression),
|
||||
PlookupIdentity(SelectedExpressions, SelectedExpressions),
|
||||
}
|
||||
|
||||
@@ -158,15 +158,16 @@ RegisterFlag: RegisterFlag = {
|
||||
}
|
||||
|
||||
InstructionDeclaration: ASMStatement = {
|
||||
<@L> "instr" <Identifier> <InstructionParamList> "{" <EqualityExpressionList> "}" => ASMStatement::InstructionDeclaration(<>)
|
||||
<@L> "instr" <Identifier> <InstructionParamList> "{" <InstructionBodyElements> "}" => ASMStatement::InstructionDeclaration(<>)
|
||||
}
|
||||
|
||||
EqualityExpressionList: Vec<Expression> = {
|
||||
<mut list:( <EqualityExpression> "," )*> <end:EqualityExpression> => { list.push(end); list }
|
||||
InstructionBodyElements: Vec<InstructionBodyElement> = {
|
||||
<mut list:( <InstructionBodyElement> "," )*> <end:InstructionBodyElement> => { list.push(end); list }
|
||||
}
|
||||
|
||||
EqualityExpression: Expression = {
|
||||
<l:BoxedExpression> "=" <r:BoxedExpression> => Expression::BinaryOperation(l, BinaryOperator::Sub, r)
|
||||
InstructionBodyElement: InstructionBodyElement = {
|
||||
<l:BoxedExpression> "=" <r:BoxedExpression> => InstructionBodyElement::Expression(Expression::BinaryOperation(l, BinaryOperator::Sub, r)),
|
||||
<SelectedExpressions> "in" <SelectedExpressions> => InstructionBodyElement::PlookupIdentity(<>),
|
||||
}
|
||||
|
||||
InstructionParamList: Vec<InstructionParam> = {
|
||||
|
||||
@@ -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(),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user