Skip to content

Commit 5f76a1e

Browse files
JulianGCalderonDiegoCivi
authored andcommitted
Fix circuit gas diff: Implement u96_limbs_less_than_guarantee_verify (#1084)
* Fix circuit implementation * Add circuit compare test (#1076) * Add more tests * Add compiler assert message * Adapt CircuitOutputs so that it can contain the modulo Instead of an array, now its a struct<array, array>, where the second array is the modulo * Save circuit output in struct representation, instead of integer It is ultimately returned in struct representation, so now it is converted sooner (useful for guarantee representation) * Save modulus to circuit output * Implement limbs_guarantee type with gate value and circuit modulus * Implement guarantee verify * Add function build_aggregate_slice * Use always struct for limbs * Fix comment * Improve documentation * Remove single_limb implementation and use generic noop
1 parent 6d867a5 commit 5f76a1e

File tree

6 files changed

+390
-64
lines changed

6 files changed

+390
-64
lines changed

src/compiler.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,11 @@ fn compile_func(
601601
&helper,
602602
metadata,
603603
)?;
604-
assert!(block.terminator().is_some());
604+
assert!(
605+
block.terminator().is_some(),
606+
"libfunc {} had no terminator",
607+
libfunc_name
608+
);
605609

606610
if let Some(tailrec_meta) = metadata.remove::<TailRecursionMeta>() {
607611
if let Some(return_block) = tailrec_meta.return_target() {

src/libfuncs/circuit.rs

Lines changed: 145 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
error::{Result, SierraAssertError},
88
libfuncs::r#struct::build_struct_value,
99
metadata::MetadataStorage,
10-
types::TypeBuilder,
10+
types::{circuit::build_u384_struct_type, TypeBuilder},
1111
utils::{get_integer_layout, layout_repeat, BlockExt, ProgramRegistryExt},
1212
};
1313
use cairo_lang_sierra::{
@@ -28,8 +28,8 @@ use melior::{
2828
cf, llvm,
2929
},
3030
ir::{
31-
attribute::DenseI32ArrayAttribute, r#type::IntegerType, Block, BlockLike, Location, Value,
32-
ValueLike,
31+
attribute::DenseI32ArrayAttribute, r#type::IntegerType, Block, BlockLike, Location, Type,
32+
Value, ValueLike,
3333
},
3434
Context,
3535
};
@@ -70,6 +70,9 @@ pub fn build<'ctx, 'this>(
7070
signature,
7171
..
7272
})
73+
| CircuitConcreteLibfunc::U96SingleLimbLessThanGuaranteeVerify(
74+
SignatureOnlyConcreteLibfunc { signature, .. },
75+
)
7376
| CircuitConcreteLibfunc::U96GuaranteeVerify(SignatureOnlyConcreteLibfunc { signature }) => {
7477
super::build_noop::<1, true>(
7578
context,
@@ -86,11 +89,6 @@ pub fn build<'ctx, 'this>(
8689
context, registry, entry, location, helper, metadata, info,
8790
)
8891
}
89-
CircuitConcreteLibfunc::U96SingleLimbLessThanGuaranteeVerify(info) => {
90-
build_u96_single_limb_less_than_guarantee_verify(
91-
context, registry, entry, location, helper, metadata, info,
92-
)
93-
}
9492
}
9593
}
9694

@@ -373,7 +371,6 @@ fn build_eval<'ctx, 'this>(
373371
// let zero = entry.argument(5)?;
374372
// let one = entry.argument(6)?;
375373

376-
// We multiply the amount of gates evaluated by 4 (the amount of u96s in each gate)
377374
let add_mod = increment_builtin_counter_by(
378375
context,
379376
entry,
@@ -402,6 +399,21 @@ fn build_eval<'ctx, 'this>(
402399
circuit_info.mul_offsets.len() * MOD_BUILTIN_INSTANCE_SIZE,
403400
)?;
404401

402+
// convert circuit output from integer representation to struct representation
403+
let gates = gates
404+
.into_iter()
405+
.map(|value| u384_integer_to_struct(context, ok_block, location, value))
406+
.collect::<Result<Vec<_>>>()?;
407+
408+
let n_gates = circuit_info.values.len();
409+
let gates_array = ok_block.append_op_result(llvm::undef(
410+
llvm::r#type::array(build_u384_struct_type(context), n_gates as u32),
411+
location,
412+
))?;
413+
let gates_array = ok_block.insert_values(context, location, gates_array, &gates)?;
414+
415+
let modulus_struct = u384_integer_to_struct(context, ok_block, location, circuit_modulus)?;
416+
405417
// Build output struct
406418
let outputs_type_id = &info.branch_signatures()[0].vars[2].ty;
407419
let outputs = build_struct_value(
@@ -412,7 +424,7 @@ fn build_eval<'ctx, 'this>(
412424
helper,
413425
metadata,
414426
outputs_type_id,
415-
&gates,
427+
&[gates_array, modulus_struct],
416428
)?;
417429

418430
ok_block.append_operation(helper.br(0, &[add_mod, mul_mod, outputs], location));
@@ -719,7 +731,6 @@ fn build_failure_guarantee_verify<'ctx, 'this>(
719731
}
720732

721733
/// Generate MLIR operations for the `u96_limbs_less_than_guarantee_verify` libfunc.
722-
/// NOOP
723734
#[allow(clippy::too_many_arguments)]
724735
fn build_u96_limbs_less_than_guarantee_verify<'ctx, 'this>(
725736
context: &'ctx Context,
@@ -730,45 +741,81 @@ fn build_u96_limbs_less_than_guarantee_verify<'ctx, 'this>(
730741
metadata: &mut MetadataStorage,
731742
info: &ConcreteU96LimbsLessThanGuaranteeVerifyLibfunc,
732743
) -> Result<()> {
733-
let guarantee_type_id = &info.branch_signatures()[0].vars[0].ty;
734-
let guarantee_type = registry.build_type(context, helper, metadata, guarantee_type_id)?;
744+
let guarantee = entry.arg(0)?;
745+
let limb_count = info.limb_count;
735746

736-
let guarantee = entry.append_op_result(llvm::undef(guarantee_type, location))?;
747+
let u96_type = IntegerType::new(context, 96).into();
748+
let limb_struct_type = llvm::r#type::r#struct(context, &vec![u96_type; limb_count], false);
749+
750+
// extract gate and modulus from input value
751+
let gate = entry.extract_value(context, location, guarantee, limb_struct_type, 0)?;
752+
let modulus = entry.extract_value(context, location, guarantee, limb_struct_type, 1)?;
737753

738-
let u96_type_id = &info.branch_signatures()[1].vars[0].ty;
739-
let u96_type = registry.build_type(context, helper, metadata, u96_type_id)?;
754+
// extract last limb from gate and modulus
755+
let gate_last_limb = entry.extract_value(context, location, gate, u96_type, limb_count - 1)?;
756+
let modulus_last_limb =
757+
entry.extract_value(context, location, modulus, u96_type, limb_count - 1)?;
740758

741-
let u96 = entry.append_op_result(llvm::undef(u96_type, location))?;
759+
// calcualte diff between limbs
760+
let diff = entry.append_op_result(arith::subi(modulus_last_limb, gate_last_limb, location))?;
761+
let k0 = entry.const_int_from_type(context, location, 0, u96_type)?;
762+
let has_diff = entry.cmpi(context, CmpiPredicate::Ne, diff, k0, location)?;
742763

743-
let kfalse = entry.const_int(context, location, 0, 64)?;
744-
entry.append_operation(helper.cond_br(
764+
let diff_block = helper.append_block(Block::new(&[]));
765+
let next_block = helper.append_block(Block::new(&[]));
766+
entry.append_operation(cf::cond_br(
745767
context,
746-
kfalse,
747-
[0, 1],
748-
[&[guarantee], &[u96]],
768+
has_diff,
769+
diff_block,
770+
next_block,
771+
&[],
772+
&[],
749773
location,
750774
));
751775

752-
Ok(())
753-
}
776+
{
777+
// if there is diff, return it
778+
diff_block.append_operation(helper.br(1, &[diff], location));
779+
}
780+
{
781+
// if there is no diff, build a new guarantee, skipping last limb
782+
let new_limb_struct_type =
783+
llvm::r#type::r#struct(context, &vec![u96_type; limb_count - 1], false);
784+
let new_gate = build_array_slice(
785+
context,
786+
next_block,
787+
location,
788+
gate,
789+
u96_type,
790+
new_limb_struct_type,
791+
0,
792+
limb_count - 1,
793+
)?;
794+
let new_modulus = build_array_slice(
795+
context,
796+
next_block,
797+
location,
798+
modulus,
799+
u96_type,
800+
new_limb_struct_type,
801+
0,
802+
limb_count - 1,
803+
)?;
754804

755-
/// Generate MLIR operations for the `u96_single_limb_less_than_guarantee_verify` libfunc.
756-
/// NOOP
757-
#[allow(clippy::too_many_arguments)]
758-
fn build_u96_single_limb_less_than_guarantee_verify<'ctx, 'this>(
759-
context: &'ctx Context,
760-
registry: &ProgramRegistry<CoreType, CoreLibfunc>,
761-
entry: &'this Block<'ctx>,
762-
location: Location<'ctx>,
763-
helper: &LibfuncHelper<'ctx, 'this>,
764-
metadata: &mut MetadataStorage,
765-
info: &SignatureOnlyConcreteLibfunc,
766-
) -> Result<()> {
767-
let u96_type_id = &info.branch_signatures()[0].vars[0].ty;
768-
let u96_type = registry.build_type(context, helper, metadata, u96_type_id)?;
769-
let u96 = entry.append_op_result(llvm::undef(u96_type, location))?;
805+
let guarantee_type_id = &info.branch_signatures()[0].vars[0].ty;
806+
let new_guarantee = build_struct_value(
807+
context,
808+
registry,
809+
next_block,
810+
location,
811+
helper,
812+
metadata,
813+
guarantee_type_id,
814+
&[new_gate, new_modulus],
815+
)?;
770816

771-
entry.append_operation(helper.br(0, &[u96], location));
817+
next_block.append_operation(helper.br(0, &[new_guarantee], location));
818+
}
772819

773820
Ok(())
774821
}
@@ -798,18 +845,41 @@ fn build_get_output<'ctx, 'this>(
798845
let output_idx = output_offset_idx - circuit_info.n_inputs - 1;
799846

800847
let outputs = entry.arg(0)?;
801-
let output_integer = entry.extract_value(
848+
849+
let n_gates = circuit_info.values.len();
850+
let output_gates = entry.extract_value(
802851
context,
803852
location,
804853
outputs,
805-
IntegerType::new(context, 384).into(),
854+
llvm::r#type::array(build_u384_struct_type(context), n_gates as u32),
855+
0,
856+
)?;
857+
let modulus_struct = entry.extract_value(
858+
context,
859+
location,
860+
outputs,
861+
build_u384_struct_type(context),
862+
1,
863+
)?;
864+
let output_struct = entry.extract_value(
865+
context,
866+
location,
867+
output_gates,
868+
build_u384_struct_type(context),
806869
output_idx,
807870
)?;
808-
let output_struct = u384_integer_to_struct(context, entry, location, output_integer)?;
809871

810872
let guarantee_type_id = &info.branch_signatures()[0].vars[1].ty;
811-
let guarantee_type = registry.build_type(context, helper, metadata, guarantee_type_id)?;
812-
let guarantee = entry.append_op_result(llvm::undef(guarantee_type, location))?;
873+
let guarantee = build_struct_value(
874+
context,
875+
registry,
876+
entry,
877+
location,
878+
helper,
879+
metadata,
880+
guarantee_type_id,
881+
&[output_struct, modulus_struct],
882+
)?;
813883

814884
entry.append_operation(helper.br(0, &[output_struct, guarantee], location));
815885

@@ -892,16 +962,7 @@ fn u384_integer_to_struct<'a>(
892962
block.trunci(limb, u96_type, location)?
893963
};
894964

895-
let struct_type = llvm::r#type::r#struct(
896-
context,
897-
&[
898-
IntegerType::new(context, 96).into(),
899-
IntegerType::new(context, 96).into(),
900-
IntegerType::new(context, 96).into(),
901-
IntegerType::new(context, 96).into(),
902-
],
903-
false,
904-
);
965+
let struct_type = build_u384_struct_type(context);
905966
let struct_value = block.append_op_result(llvm::undef(struct_type, location))?;
906967

907968
block.insert_values(
@@ -993,6 +1054,35 @@ fn build_euclidean_algorithm<'ctx, 'this>(
9931054
Ok(end_block)
9941055
}
9951056

1057+
/// Extracts values from indexes `from` - `to` (exclusive) and builds a new value of type `result_type`
1058+
///
1059+
/// Can be used with arrays, or structs with multiple elements of a single type.
1060+
#[allow(clippy::too_many_arguments)]
1061+
fn build_array_slice<'ctx>(
1062+
context: &'ctx Context,
1063+
block: &'ctx Block<'ctx>,
1064+
location: Location<'ctx>,
1065+
aggregate: Value<'ctx, 'ctx>,
1066+
element_type: Type<'ctx>,
1067+
result_type: Type<'ctx>,
1068+
from: usize,
1069+
to: usize,
1070+
) -> Result<Value<'ctx, 'ctx>> {
1071+
let mut values = Vec::with_capacity(to - from);
1072+
1073+
for i in from..to {
1074+
let value = block.extract_value(context, location, aggregate, element_type, i)?;
1075+
values.push(value);
1076+
}
1077+
1078+
block.insert_values(
1079+
context,
1080+
location,
1081+
block.append_op_result(llvm::undef(result_type, location))?,
1082+
&values,
1083+
)
1084+
}
1085+
9961086
#[cfg(test)]
9971087
mod test {
9981088

src/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ mod bounded_int;
3535
mod r#box;
3636
mod builtin_costs;
3737
mod bytes31;
38-
mod circuit;
38+
pub mod circuit;
3939
mod coupon;
4040
mod ec_op;
4141
mod ec_point;

0 commit comments

Comments
 (0)