From 2ed328879ac72e00cb9758fe046433690ec75826 Mon Sep 17 00:00:00 2001 From: Leo-Besancon Date: Fri, 9 Aug 2024 10:48:38 +0200 Subject: [PATCH] refactor: eliminate exponentiations in the IR (#352) --- air-script/tests/binary/binary.masm | 32 +--- air-script/tests/binary/binary.rs | 4 +- air-script/tests/bitwise/bitwise.masm | 144 ++---------------- air-script/tests/bitwise/bitwise.rs | 18 +-- air-script/tests/evaluators/evaluators.masm | 64 +------- air-script/tests/evaluators/evaluators.rs | 8 +- .../tests/functions/functions_complex.air | 2 +- .../tests/functions/functions_complex.masm | 30 +--- .../tests/functions/functions_complex.rs | 4 +- air-script/tests/variables/variables.masm | 16 +- air-script/tests/variables/variables.rs | 2 +- codegen/masm/src/codegen.rs | 51 ------- codegen/masm/src/utils.rs | 9 -- codegen/winterfell/src/air/graph.rs | 31 ---- ir/src/graph/mod.rs | 5 - ir/src/ir/operation.rs | 7 - ir/src/passes/translate.rs | 19 ++- 17 files changed, 54 insertions(+), 392 deletions(-) diff --git a/air-script/tests/binary/binary.masm b/air-script/tests/binary/binary.masm index bae41e14..5e19b881 100644 --- a/air-script/tests/binary/binary.masm +++ b/air-script/tests/binary/binary.masm @@ -75,39 +75,11 @@ end # END PROC compute_integrity_constraint_divisor # This procedure pushes 2 quadratic extension field elements to the stack proc.compute_integrity_constraints # integrity constraint 0 for main - padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900200 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 1 for main - padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900200 drop drop ext2mul end # END PROC compute_integrity_constraints diff --git a/air-script/tests/binary/binary.rs b/air-script/tests/binary/binary.rs index 1865c187..65bb44f0 100644 --- a/air-script/tests/binary/binary.rs +++ b/air-script/tests/binary/binary.rs @@ -75,8 +75,8 @@ impl Air for BinaryAir { fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { let main_current = frame.current(); let main_next = frame.next(); - result[0] = main_current[0].exp(E::PositiveInteger::from(2_u64)) - main_current[0] - E::ZERO; - result[1] = main_current[1].exp(E::PositiveInteger::from(2_u64)) - main_current[1] - E::ZERO; + result[0] = main_current[0] * main_current[0] - main_current[0] - E::ZERO; + result[1] = main_current[1] * main_current[1] - main_current[1] - E::ZERO; } fn evaluate_aux_transition(&self, main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], aux_rand_elements: &AuxTraceRandElements, result: &mut [E]) diff --git a/air-script/tests/bitwise/bitwise.masm b/air-script/tests/bitwise/bitwise.masm index f123c7be..86d20ac8 100644 --- a/air-script/tests/bitwise/bitwise.masm +++ b/air-script/tests/bitwise/bitwise.masm @@ -187,21 +187,7 @@ end # END PROC compute_integrity_constraint_divisor # This procedure pushes 17 quadratic extension field elements to the stack proc.compute_integrity_constraints # integrity constraint 0 for main - padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900200 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 1 for main @@ -209,147 +195,35 @@ proc.compute_integrity_constraints # Multiply by the composition coefficient padw mem_loadw.4294900200 drop drop ext2mul # integrity constraint 2 for main - padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900201 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 3 for main - padw mem_loadw.4294900004 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900004 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900004 movdn.3 movdn.3 drop drop padw mem_loadw.4294900004 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900004 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900201 drop drop ext2mul # integrity constraint 4 for main - padw mem_loadw.4294900005 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900005 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900005 movdn.3 movdn.3 drop drop padw mem_loadw.4294900005 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900005 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900202 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 5 for main - padw mem_loadw.4294900006 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900006 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900006 movdn.3 movdn.3 drop drop padw mem_loadw.4294900006 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900006 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900202 drop drop ext2mul # integrity constraint 6 for main - padw mem_loadw.4294900007 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900007 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900007 movdn.3 movdn.3 drop drop padw mem_loadw.4294900007 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900007 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900203 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 7 for main - padw mem_loadw.4294900008 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900008 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900008 movdn.3 movdn.3 drop drop padw mem_loadw.4294900008 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900008 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900203 drop drop ext2mul # integrity constraint 8 for main - padw mem_loadw.4294900009 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900009 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900009 movdn.3 movdn.3 drop drop padw mem_loadw.4294900009 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900009 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900204 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 9 for main - padw mem_loadw.4294900010 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900010 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub + padw mem_loadw.4294900010 movdn.3 movdn.3 drop drop padw mem_loadw.4294900010 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900010 movdn.3 movdn.3 drop drop ext2sub push.0 push.0 ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900204 drop drop ext2mul # integrity constraint 10 for main diff --git a/air-script/tests/bitwise/bitwise.rs b/air-script/tests/bitwise/bitwise.rs index 764bb329..e8598218 100644 --- a/air-script/tests/bitwise/bitwise.rs +++ b/air-script/tests/bitwise/bitwise.rs @@ -75,16 +75,16 @@ impl Air for BitwiseAir { fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { let main_current = frame.current(); let main_next = frame.next(); - result[0] = main_current[0].exp(E::PositiveInteger::from(2_u64)) - main_current[0] - E::ZERO; + result[0] = main_current[0] * main_current[0] - main_current[0] - E::ZERO; result[1] = periodic_values[1] * (main_next[0] - main_current[0]) - E::ZERO; - result[2] = main_current[3].exp(E::PositiveInteger::from(2_u64)) - main_current[3] - E::ZERO; - result[3] = main_current[4].exp(E::PositiveInteger::from(2_u64)) - main_current[4] - E::ZERO; - result[4] = main_current[5].exp(E::PositiveInteger::from(2_u64)) - main_current[5] - E::ZERO; - result[5] = main_current[6].exp(E::PositiveInteger::from(2_u64)) - main_current[6] - E::ZERO; - result[6] = main_current[7].exp(E::PositiveInteger::from(2_u64)) - main_current[7] - E::ZERO; - result[7] = main_current[8].exp(E::PositiveInteger::from(2_u64)) - main_current[8] - E::ZERO; - result[8] = main_current[9].exp(E::PositiveInteger::from(2_u64)) - main_current[9] - E::ZERO; - result[9] = main_current[10].exp(E::PositiveInteger::from(2_u64)) - main_current[10] - E::ZERO; + result[2] = main_current[3] * main_current[3] - main_current[3] - E::ZERO; + result[3] = main_current[4] * main_current[4] - main_current[4] - E::ZERO; + result[4] = main_current[5] * main_current[5] - main_current[5] - E::ZERO; + result[5] = main_current[6] * main_current[6] - main_current[6] - E::ZERO; + result[6] = main_current[7] * main_current[7] - main_current[7] - E::ZERO; + result[7] = main_current[8] * main_current[8] - main_current[8] - E::ZERO; + result[8] = main_current[9] * main_current[9] - main_current[9] - E::ZERO; + result[9] = main_current[10] * main_current[10] - main_current[10] - E::ZERO; result[10] = periodic_values[0] * (main_current[1] - (E::ONE * main_current[3] + E::from(2_u64) * main_current[4] + E::from(4_u64) * main_current[5] + E::from(8_u64) * main_current[6])) - E::ZERO; result[11] = periodic_values[0] * (main_current[2] - (E::ONE * main_current[7] + E::from(2_u64) * main_current[8] + E::from(4_u64) * main_current[9] + E::from(8_u64) * main_current[10])) - E::ZERO; result[12] = periodic_values[1] * (main_next[1] - (main_current[1] * E::from(16_u64) + E::ONE * main_current[3] + E::from(2_u64) * main_current[4] + E::from(4_u64) * main_current[5] + E::from(8_u64) * main_current[6])) - E::ZERO; diff --git a/air-script/tests/evaluators/evaluators.masm b/air-script/tests/evaluators/evaluators.masm index f2c16264..9aeed180 100644 --- a/air-script/tests/evaluators/evaluators.masm +++ b/air-script/tests/evaluators/evaluators.masm @@ -87,75 +87,19 @@ proc.compute_integrity_constraints # Multiply by the composition coefficient padw mem_loadw.4294900201 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 3 for main - padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub + padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900201 drop drop ext2mul # integrity constraint 4 for main - padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2sub + padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900202 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 5 for main - padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2sub + padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900202 drop drop ext2mul # integrity constraint 6 for main - padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2sub + padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900203 movdn.3 movdn.3 drop drop ext2mul end # END PROC compute_integrity_constraints diff --git a/air-script/tests/evaluators/evaluators.rs b/air-script/tests/evaluators/evaluators.rs index d4bd0a12..d8ebb9cc 100644 --- a/air-script/tests/evaluators/evaluators.rs +++ b/air-script/tests/evaluators/evaluators.rs @@ -78,10 +78,10 @@ impl Air for EvaluatorsAir { result[0] = main_next[0] - main_current[0]; result[1] = main_next[2] - main_current[2]; result[2] = main_next[6] - main_current[6]; - result[3] = main_current[0].exp(E::PositiveInteger::from(2_u64)) - main_current[0]; - result[4] = main_current[1].exp(E::PositiveInteger::from(2_u64)) - main_current[1]; - result[5] = main_current[2].exp(E::PositiveInteger::from(2_u64)) - main_current[2]; - result[6] = main_current[3].exp(E::PositiveInteger::from(2_u64)) - main_current[3]; + result[3] = main_current[0] * main_current[0] - main_current[0]; + result[4] = main_current[1] * main_current[1] - main_current[1]; + result[5] = main_current[2] * main_current[2] - main_current[2]; + result[6] = main_current[3] * main_current[3] - main_current[3]; } fn evaluate_aux_transition(&self, main_frame: &EvaluationFrame, aux_frame: &EvaluationFrame, _periodic_values: &[F], aux_rand_elements: &AuxTraceRandElements, result: &mut [E]) diff --git a/air-script/tests/functions/functions_complex.air b/air-script/tests/functions/functions_complex.air index f4b0607f..192ed62d 100644 --- a/air-script/tests/functions/functions_complex.air +++ b/air-script/tests/functions/functions_complex.air @@ -35,7 +35,7 @@ boundary_constraints { integrity_constraints { # let val = $alpha[0] + v let f = get_multiplicity_flags(s0, s1) - let z = v^4 * f[3] + v^2 * f[2] + v * f[1] + f[0] + let z = v^7 * f[3] + v^2 * f[2] + v * f[1] + f[0] # let folded_value = fold_scalar_and_vec(v, b) enf b_range' = b_range * (z * t - t + 1) # enf b_range' = b_range * 2 diff --git a/air-script/tests/functions/functions_complex.masm b/air-script/tests/functions/functions_complex.masm index 93dd497d..7f10fa8e 100644 --- a/air-script/tests/functions/functions_complex.masm +++ b/air-script/tests/functions/functions_complex.masm @@ -79,35 +79,7 @@ proc.compute_integrity_constraints # Multiply by the composition coefficient padw mem_loadw.4294900200 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 0 for aux - padw mem_loadw.4294900072 drop drop padw mem_loadw.4294900072 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 2 times - dup.1 dup.1 ext2mul dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2mul ext2mul padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - push.1 push.0 padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2sub padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2mul ext2mul ext2add padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop push.1 push.0 padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2sub ext2mul ext2mul ext2add push.1 push.0 padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2sub push.1 push.0 padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2sub ext2mul ext2add padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub push.1 push.0 ext2add ext2mul ext2sub + padw mem_loadw.4294900072 drop drop padw mem_loadw.4294900072 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2mul ext2mul ext2mul ext2mul padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2mul ext2mul padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop ext2mul push.1 push.0 padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2sub padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2mul ext2mul ext2add padw mem_loadw.4294900003 movdn.3 movdn.3 drop drop padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop push.1 push.0 padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2sub ext2mul ext2mul ext2add push.1 push.0 padw mem_loadw.4294900001 movdn.3 movdn.3 drop drop ext2sub push.1 push.0 padw mem_loadw.4294900002 movdn.3 movdn.3 drop drop ext2sub ext2mul ext2add padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub push.1 push.0 ext2add ext2mul ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900200 drop drop ext2mul end # END PROC compute_integrity_constraints diff --git a/air-script/tests/functions/functions_complex.rs b/air-script/tests/functions/functions_complex.rs index baa2cad5..8d9b745e 100644 --- a/air-script/tests/functions/functions_complex.rs +++ b/air-script/tests/functions/functions_complex.rs @@ -41,7 +41,7 @@ impl Air for FunctionsAir { fn new(trace_info: TraceInfo, public_inputs: PublicInputs, options: WinterProofOptions) -> Self { let main_degrees = vec![TransitionConstraintDegree::new(1)]; - let aux_degrees = vec![TransitionConstraintDegree::new(8)]; + let aux_degrees = vec![TransitionConstraintDegree::new(11)]; let num_main_assertions = 1; let num_aux_assertions = 0; @@ -86,6 +86,6 @@ impl Air for FunctionsAir { let main_next = main_frame.next(); let aux_current = aux_frame.current(); let aux_next = aux_frame.next(); - result[0] = aux_next[0] - aux_current[0] * ((E::from(main_current[3]).exp(E::PositiveInteger::from(4_u64)) * E::from(main_current[1]) * E::from(main_current[2]) + E::from(main_current[3]).exp(E::PositiveInteger::from(2_u64)) * (E::ONE - E::from(main_current[1])) * E::from(main_current[2]) + E::from(main_current[3]) * E::from(main_current[1]) * (E::ONE - E::from(main_current[2])) + (E::ONE - E::from(main_current[1])) * (E::ONE - E::from(main_current[2]))) * E::from(main_current[0]) - E::from(main_current[0]) + E::ONE); + result[0] = aux_next[0] - aux_current[0] * ((E::from(main_current[3]) * E::from(main_current[3]) * E::from(main_current[3]) * E::from(main_current[3]) * E::from(main_current[3]) * E::from(main_current[3]) * E::from(main_current[3]) * E::from(main_current[1]) * E::from(main_current[2]) + E::from(main_current[3]) * E::from(main_current[3]) * (E::ONE - E::from(main_current[1])) * E::from(main_current[2]) + E::from(main_current[3]) * E::from(main_current[1]) * (E::ONE - E::from(main_current[2])) + (E::ONE - E::from(main_current[1])) * (E::ONE - E::from(main_current[2]))) * E::from(main_current[0]) - E::from(main_current[0]) + E::ONE); } } \ No newline at end of file diff --git a/air-script/tests/variables/variables.masm b/air-script/tests/variables/variables.masm index e3796862..b9124b14 100644 --- a/air-script/tests/variables/variables.masm +++ b/air-script/tests/variables/variables.masm @@ -143,21 +143,7 @@ end # END PROC compute_integrity_constraint_divisor # This procedure pushes 5 quadratic extension field elements to the stack proc.compute_integrity_constraints # integrity constraint 0 for main - padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop - # push the accumulator to the stack - push.1 movdn.2 push.0 movdn.2 - # => [b1, b0, r1, r0, ...] - # square 1 times - dup.1 dup.1 ext2mul - # multiply - dup.1 dup.1 movdn.5 movdn.5 - # => [b1, b0, r1, r0, b1, b0, ...] (4 cycles) - ext2mul movdn.3 movdn.3 - # => [b1, b0, r1', r0', ...] (5 cycles) - # clean stack - drop drop - # => [r1, r0, ...] (2 cycles) - padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub + padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2mul padw mem_loadw.4294900000 movdn.3 movdn.3 drop drop ext2sub # Multiply by the composition coefficient padw mem_loadw.4294900200 movdn.3 movdn.3 drop drop ext2mul # integrity constraint 1 for main diff --git a/air-script/tests/variables/variables.rs b/air-script/tests/variables/variables.rs index 2c7197c8..dc8c7b64 100644 --- a/air-script/tests/variables/variables.rs +++ b/air-script/tests/variables/variables.rs @@ -79,7 +79,7 @@ impl Air for VariablesAir { fn evaluate_transition>(&self, frame: &EvaluationFrame, periodic_values: &[E], result: &mut [E]) { let main_current = frame.current(); let main_next = frame.next(); - result[0] = main_current[0].exp(E::PositiveInteger::from(2_u64)) - main_current[0]; + result[0] = main_current[0] * main_current[0] - main_current[0]; result[1] = periodic_values[0] * (main_next[0] - main_current[0]) - E::ZERO; result[2] = (E::ONE - main_current[0]) * (main_current[3] - main_current[1] - main_current[2]) - (E::from(6_u64) - main_current[0]); result[3] = main_current[0] * (main_current[3] - main_current[1] * main_current[2]) - (main_next[0] - E::from(3_u64) - E::from(2_u64)); diff --git a/codegen/masm/src/codegen.rs b/codegen/masm/src/codegen.rs index 741d75aa..588dd883 100644 --- a/codegen/masm/src/codegen.rs +++ b/codegen/masm/src/codegen.rs @@ -3,7 +3,6 @@ use crate::constants::{AUX_TRACE, MAIN_TRACE}; use crate::error::CodegenError; use crate::utils::{ boundary_group_to_procedure_name, load_quadratic_element, periodic_group_to_memory_offset, - quadratic_element_square, }; use crate::visitor::{ walk_boundary_constraints, walk_integrity_constraints, walk_periodic_columns, AirVisitor, @@ -903,56 +902,6 @@ impl<'ast> AirVisitor<'ast> for Backend<'ast> { self.visit_node_index(right)?; self.writer.ext2mul(); } - Operation::Exp(left, exp) => { - // NOTE: The VM doesn't support exponentiation of extension elements. - // - // Ref: https://github.com/facebook/winterfell/blob/0acb2a148e2e8445d5f6a3511fa9d852e54818dd/math/src/field/traits.rs#L124-L150 - - self.visit_node_index(left)?; - - self.writer.header("push the accumulator to the stack"); - self.writer.push(1); - self.writer.movdn(2); - self.writer.push(0); - self.writer.movdn(2); - self.writer.header("=> [b1, b0, r1, r0, ...]"); - - // emitted code computes exponentiation via square-and-multiply - let mut e: usize = *exp; - while e != 0 { - self.writer - .header(format!("square {} times", e.trailing_zeros())); - quadratic_element_square(&mut self.writer, e.trailing_zeros()); - - // account for the exponentiations done above - e = e >> e.trailing_zeros(); - - self.writer.header("multiply"); - self.writer.dup(1); - self.writer.dup(1); - self.writer.movdn(5); - self.writer.movdn(5); - self.writer - .header("=> [b1, b0, r1, r0, b1, b0, ...] (4 cycles)"); - - self.writer.ext2mul(); - self.writer.movdn(3); - self.writer.movdn(3); - self.writer.header("=> [b1, b0, r1', r0', ...] (5 cycles)"); - - // account for the multiply done above - assert!( - e & 1 == 1, - "this loop is only executed if the number is non-zero" - ); - e ^= 1; - } - - self.writer.header("clean stack"); - self.writer.drop(); - self.writer.drop(); - self.writer.header("=> [r1, r0, ...] (2 cycles)"); - } }; Ok(()) diff --git a/codegen/masm/src/utils.rs b/codegen/masm/src/utils.rs index f5e94f45..0a893fdc 100644 --- a/codegen/masm/src/utils.rs +++ b/codegen/masm/src/utils.rs @@ -62,15 +62,6 @@ pub fn load_quadratic_element( Ok(()) } -/// Assumes a quadratic extension field element is at the top of the stack and square it `n` times. -pub fn quadratic_element_square(writer: &mut Writer, n: u32) { - for _ in 0..n { - writer.dup(1); - writer.dup(1); - writer.ext2mul(); - } -} - pub fn boundary_group_to_procedure_name( trace: TraceSegmentId, domain: ConstraintDomain, diff --git a/codegen/winterfell/src/air/graph.rs b/codegen/winterfell/src/air/graph.rs index 0bf21830..72f43a5e 100644 --- a/codegen/winterfell/src/air/graph.rs +++ b/codegen/winterfell/src/air/graph.rs @@ -68,29 +68,6 @@ impl Codegen for Operation { Operation::Add(_, _) => binary_op_to_string(ir, self, elem_type, trace_segment), Operation::Sub(_, _) => binary_op_to_string(ir, self, elem_type, trace_segment), Operation::Mul(_, _) => binary_op_to_string(ir, self, elem_type, trace_segment), - // TODO: move this logic to a helper function - Operation::Exp(l_idx, r_idx) => { - let lhs = l_idx.to_string(ir, elem_type, trace_segment); - let lhs = if is_leaf(l_idx, ir) { - lhs - } else { - format!("({lhs})") - }; - match r_idx { - 0 => match elem_type { - // x^0 = 1 - ElemType::Base => "Felt::ONE".to_string(), - ElemType::Ext => "E::ONE".to_string(), - }, - 1 => lhs, // x^1 = x - _ => match elem_type { - ElemType::Base => format!("{lhs}.exp(Felt::new({r_idx}))"), - ElemType::Ext => { - format!("{lhs}.exp(E::PositiveInteger::from({r_idx}_u64))") - } - }, - } - } } } } @@ -132,14 +109,6 @@ impl Codegen for Value { } } -/// Returns true if the operation at the specified node index is a leaf node in the constraint graph. -fn is_leaf(idx: &NodeIndex, ir: &Air) -> bool { - !matches!( - ir.constraint_graph().node(idx).op(), - Operation::Add(_, _) | Operation::Sub(_, _) | Operation::Mul(_, _) | Operation::Exp(_, _) - ) -} - /// Returns a string representation of a binary operation. fn binary_op_to_string( ir: &Air, diff --git a/ir/src/graph/mod.rs b/ir/src/graph/mod.rs index 488aa81c..5714c73c 100644 --- a/ir/src/graph/mod.rs +++ b/ir/src/graph/mod.rs @@ -131,7 +131,6 @@ impl AlgebraicGraph { Ok((trace_segment, domain)) } - Operation::Exp(lhs, _) => self.node_details(lhs, default_domain), } } @@ -183,10 +182,6 @@ impl AlgebraicGraph { let rhs_base = self.accumulate_degree(cycles, rhs); lhs_base + rhs_base } - Operation::Exp(lhs, rhs) => { - let lhs_base = self.accumulate_degree(cycles, lhs); - lhs_base * rhs - } } } } diff --git a/ir/src/ir/operation.rs b/ir/src/ir/operation.rs index bb6d3c67..017a81be 100644 --- a/ir/src/ir/operation.rs +++ b/ir/src/ir/operation.rs @@ -16,13 +16,6 @@ pub enum Operation { Sub(NodeIndex, NodeIndex), /// Evaluates by multiplication over two operands (given as nodes in the graph) Mul(NodeIndex, NodeIndex), - /// Evaluates by exponentiation over two operands (the first given as a node - /// in the graph, the second as a constant). - /// - /// NOTE: The exponent _must_ be a constant value currently. In the future, - /// it may be possible to support non-constant exponents, but it is not - /// supported at this time. - Exp(NodeIndex, usize), } impl Operation { /// Corresponds to the binding power of this [Operation] diff --git a/ir/src/passes/translate.rs b/ir/src/passes/translate.rs index 28876523..ee1f319a 100644 --- a/ir/src/passes/translate.rs +++ b/ir/src/passes/translate.rs @@ -448,13 +448,30 @@ impl<'a> AirBuilder<'a> { } } + // Use square and multiply algorithm to expand the exp into a series of multiplications + fn expand_exp(&mut self, lhs: NodeIndex, rhs: u64) -> NodeIndex { + match rhs { + 0 => self.insert_constant(1), + 1 => lhs, + n if n % 2 == 0 => { + let square = self.insert_op(Operation::Mul(lhs, lhs)); + self.expand_exp(square, n / 2) + } + n => { + let square = self.insert_op(Operation::Mul(lhs, lhs)); + let rec = self.expand_exp(square, (n - 1) / 2); + self.insert_op(Operation::Mul(lhs, rec)) + } + } + } + fn insert_binary_expr(&mut self, expr: &ast::BinaryExpr) -> Result { if expr.op == ast::BinaryOp::Exp { let lhs = self.insert_scalar_expr(expr.lhs.as_ref())?; let ast::ScalarExpr::Const(rhs) = expr.rhs.as_ref() else { unreachable!(); }; - return Ok(self.insert_op(Operation::Exp(lhs, rhs.item as usize))); + return Ok(self.expand_exp(lhs, rhs.item)); } let lhs = self.insert_scalar_expr(expr.lhs.as_ref())?;