11//! # `u256`-related libfuncs
22
3- use super :: { increment_builtin_counter_conditionally_by , LibfuncHelper } ;
3+ use super :: { increment_builtin_counter_by , LibfuncHelper } ;
44use crate :: { error:: Result , metadata:: MetadataStorage , utils:: ProgramRegistryExt } ;
55use cairo_lang_sierra:: {
66 extensions:: {
@@ -14,7 +14,7 @@ use cairo_lang_sierra::{
1414use melior:: {
1515 dialect:: {
1616 arith:: { self , CmpiPredicate } ,
17- llvm, ods, scf,
17+ cf , llvm, ods, scf,
1818 } ,
1919 helpers:: BuiltinBlockExt ,
2020 ir:: {
@@ -761,6 +761,61 @@ pub fn build_u256_guarantee_inv_mod_n<'ctx, 'this>(
761761 . result ( 0 ) ?
762762 . into ( ) ;
763763
764+ // `a` (= lhs) is not constrained to be non-zero by the signature
765+ // `u256_guarantee_inv_mod_n(a, n: NonZero<u256>)`, and the extended Euclidean loop
766+ // below divides by the current divisor (initially `a`) on its first iteration, so
767+ // `a == 0` would be a udiv-by-zero (UB at the LLVM IR level). Since gcd(0, n) = n has
768+ // no inverse, short-circuit the whole calculation when `a == 0` and branch straight to
769+ // the "no inverse" output, matching casm_vm's U256InvModN hint. The range check builtin
770+ // is incremented 7 times on this path, same as the regular not-invertible branch.
771+ let guarantee_type = registry. build_type (
772+ context,
773+ helper,
774+ metadata,
775+ info. output_types ( ) . next ( ) . unwrap ( ) . nth ( 2 ) . unwrap ( ) ,
776+ ) ?;
777+ let range_check = entry. arg ( 0 ) ?;
778+
779+ // The "no inverse" branch (output index 1) is reached from two places: the `a == 0`
780+ // short-circuit below and the regular `gcd != 1` exit of the Euclidean loop. The
781+ // LibfuncHelper reuses the values passed to `br`/`cond_br` directly in the shared
782+ // landing block, so they must dominate it. We therefore build them here in the entry
783+ // block (a common dominator of both predecessors): an `undef` guarantee and the range
784+ // check incremented 7 times.
785+ let guarantee = entry
786+ . append_operation ( llvm:: undef ( guarantee_type, location) )
787+ . result ( 0 ) ?
788+ . into ( ) ;
789+ let range_check_no_inverse =
790+ increment_builtin_counter_by ( context, entry, location, range_check, 7 ) ?;
791+
792+ let lhs_is_zero = entry
793+ . append_operation ( arith:: cmpi ( context, CmpiPredicate :: Eq , lhs, k0, location) )
794+ . result ( 0 ) ?
795+ . into ( ) ;
796+ let no_inverse_block = helper. append_block ( Block :: new ( & [ ] ) ) ;
797+ let compute_block = helper. append_block ( Block :: new ( & [ ] ) ) ;
798+ entry. append_operation ( cf:: cond_br (
799+ context,
800+ lhs_is_zero,
801+ no_inverse_block,
802+ compute_block,
803+ & [ ] ,
804+ & [ ] ,
805+ location,
806+ ) ) ;
807+
808+ helper. br (
809+ no_inverse_block,
810+ 1 ,
811+ & [ range_check_no_inverse, guarantee, guarantee] ,
812+ location,
813+ ) ?;
814+
815+ // `a != 0`: the inverse may exist, so run the extended Euclidean algorithm. From here
816+ // on everything is emitted into `compute_block` instead of the original entry block.
817+ let entry = compute_block;
818+
764819 let result = entry. append_operation ( scf:: r#while (
765820 & [ lhs, rhs, k1, k0] ,
766821 & [ i256_ty, i256_ty, i256_ty, i256_ty] ,
@@ -932,27 +987,12 @@ pub fn build_u256_guarantee_inv_mod_n<'ctx, 'this>(
932987 . result ( 0 ) ?
933988 . into ( ) ;
934989
935- let guarantee_type = registry. build_type (
936- context,
937- helper,
938- metadata,
939- info. output_types ( ) . next ( ) . unwrap ( ) . nth ( 2 ) . unwrap ( ) ,
940- ) ?;
941- let op = entry. append_operation ( llvm:: undef ( guarantee_type, location) ) ;
942- let guarantee = op. result ( 0 ) ?. into ( ) ;
943-
944990 // The sierra-to-casm compiler uses the range check builtin a total of 9 times if the inverse is
945- // not equal to 0 and lhs is invertible. Otherwise it will be used 7 times.
991+ // not equal to 0 and lhs is invertible. Otherwise it will be used 7 times (handled by
992+ // `range_check_no_inverse`, computed in the entry block).
946993 // https://github.com/starkware-libs/cairo/blob/v2.12.0-dev.1/crates/cairo-lang-sierra-to-casm/src/invocations/int/unsigned256.rs#L21
947- let range_check = increment_builtin_counter_conditionally_by (
948- context,
949- entry,
950- location,
951- entry. arg ( 0 ) ?,
952- 9 ,
953- 7 ,
954- inverse_exists_and_is_not_zero,
955- ) ?;
994+ let range_check_inverse =
995+ increment_builtin_counter_by ( context, entry, location, range_check, 9 ) ?;
956996
957997 helper. cond_br (
958998 context,
@@ -961,7 +1001,7 @@ pub fn build_u256_guarantee_inv_mod_n<'ctx, 'this>(
9611001 [ 0 , 1 ] ,
9621002 [
9631003 & [
964- range_check ,
1004+ range_check_inverse ,
9651005 result_inv,
9661006 guarantee,
9671007 guarantee,
@@ -972,7 +1012,7 @@ pub fn build_u256_guarantee_inv_mod_n<'ctx, 'this>(
9721012 guarantee,
9731013 guarantee,
9741014 ] ,
975- & [ range_check , guarantee, guarantee] ,
1015+ & [ range_check_no_inverse , guarantee, guarantee] ,
9761016 ] ,
9771017 location,
9781018 )
0 commit comments