Merge pull request #76 from chriseth/lookups_in_instructions

Support lookups in instructions.
This commit is contained in:
chriseth
2023-03-06 12:23:19 +01:00
committed by GitHub
6 changed files with 124 additions and 47 deletions

View File

@@ -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()
}

View File

@@ -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

View File

@@ -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()
}
}
}

View File

@@ -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),
}

View File

@@ -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> = {

View File

@@ -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(),
);
}