Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: parse result error #500

Closed
greged93 opened this issue Mar 25, 2024 · 18 comments · Fixed by #546
Closed

BUG: parse result error #500

greged93 opened this issue Mar 25, 2024 · 18 comments · Fixed by #546
Labels
bug Something isn't working integration

Comments

@greged93
Copy link
Collaborator

greged93 commented Mar 25, 2024

I'm getting an issue on the parse_result call at the end of the invocation of invoke_dynamic. Here is the backtrace:

thread 'evm_sequencer::evm_state::v1::tests::test_execute_simple_contract' panicked at /Users/greg/code/rust/cairo_native/src/values.rs:536:21:
assertion failed: length_value >= offset_value
stack backtrace:
   0: rust_begin_unwind
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
   2: core::panicking::panic
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:144:5
   3: cairo_native::values::JitValue::from_jit
             at /Users/greg/code/rust/cairo_native/src/values.rs:536:21
   4: cairo_native::values::JitValue::from_jit
             at /Users/greg/code/rust/cairo_native/src/values.rs:662:38
   5: cairo_native::executor::parse_result
             at /Users/greg/code/rust/cairo_native/src/executor.rs:706:37
   6: cairo_native::executor::invoke_dynamic
             at /Users/greg/code/rust/cairo_native/src/executor.rs:267:24
   7: cairo_native::executor::jit::JitNativeExecutor::invoke_contract_dynamic
             at /Users/greg/code/rust/cairo_native/src/executor/jit.rs:102:13
   8: starknet_in_rust::execution::execution_entry_point::ExecutionEntryPoint::native_execute
             at /Users/greg/code/rust/starknet_in_rust/src/execution/execution_entry_point.rs:777:46
   9: starknet_in_rust::execution::execution_entry_point::ExecutionEntryPoint::execute
             at /Users/greg/code/rust/starknet_in_rust/src/execution/execution_entry_point.rs:168:23
  10: starknet_in_rust::transaction::invoke_function::InvokeFunction::run_execute_entrypoint
             at /Users/greg/code/rust/starknet_in_rust/src/transaction/invoke_function.rs:335:9
  11: starknet_in_rust::transaction::invoke_function::InvokeFunction::apply
             at /Users/greg/code/rust/starknet_in_rust/src/transaction/invoke_function.rs:388:13
  12: starknet_in_rust::transaction::invoke_function::InvokeFunction::execute::{{closure}}
             at /Users/greg/code/rust/starknet_in_rust/src/transaction/invoke_function.rs:461:28
  13: starknet_in_rust::transaction::invoke_function::InvokeFunction::execute
             at /Users/greg/code/rust/starknet_in_rust/src/transaction/invoke_function.rs:425:5
  14: starknet_in_rust::transaction::Transaction::execute
             at /Users/greg/code/rust/starknet_in_rust/src/transaction/mod.rs:224:48
  15: <sequencer::sequencer::Sequencer<S> as sequencer::execution::Execution>::execute
             at /Users/greg/code/rust/ef-tests/crates/sequencer/src/sequencer.rs:73:19
  16: ef_testing::evm_sequencer::evm_state::v1::<impl ef_testing::evm_sequencer::evm_state::Evm for ef_testing::evm_sequencer::sequencer::KakarotSequencer>::execute_transaction
             at ./src/evm_sequencer/evm_state/v1.rs:260:9
  17: ef_testing::evm_sequencer::evm_state::v1::tests::test_execute_simple_contract
             at ./src/evm_sequencer/evm_state/v1.rs:357:9
  18: ef_testing::evm_sequencer::evm_state::v1::tests::test_execute_simple_contract::{{closure}}
             at ./src/evm_sequencer/evm_state/v1.rs:322:38
  19: core::ops::function::FnOnce::call_once
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
  20: core::ops::function::FnOnce::call_once
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
libunwind: malformed __unwind_info at 0x18DB5BCA8 bad second level page

This is from executing the test test_execute_simple_contract from the ef-tests repo on Kakarot.

@igaray igaray added bug Something isn't working integration labels Mar 26, 2024
@azteca1998
Copy link
Collaborator

Hello, I'm not able to attempt to replicate the error on x86_64 because the project won't compile. I'll try using a different architecture, but this should probably be fixed sooner rather than later.

error[E0658]: use of unstable library feature 'stdsimd'
  --> /home/esteve/.cargo/registry/src/index.crates.io-6f17d22bba15001f/curve25519-dalek-4.1.2/src/backend/vector/ifma/field.rs:26:5
   |
26 |     _mm256_madd52lo_epu64(z.into(), x.into(), y.into()).into()
   |     ^^^^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #48556 <https://github.com/rust-lang/rust/issues/48556> for more information
   = help: add `#![feature(stdsimd)]` to the crate attributes to enable

error[E0658]: use of unstable library feature 'stdsimd'
  --> /home/esteve/.cargo/registry/src/index.crates.io-6f17d22bba15001f/curve25519-dalek-4.1.2/src/backend/vector/ifma/field.rs:25:9
   |
25 |     use core::arch::x86_64::_mm256_madd52lo_epu64;
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #48556 <https://github.com/rust-lang/rust/issues/48556> for more information
   = help: add `#![feature(stdsimd)]` to the crate attributes to enable

error[E0658]: use of unstable library feature 'stdsimd'
  --> /home/esteve/.cargo/registry/src/index.crates.io-6f17d22bba15001f/curve25519-dalek-4.1.2/src/backend/vector/ifma/field.rs:34:5
   |
34 |     _mm256_madd52hi_epu64(z.into(), x.into(), y.into()).into()
   |     ^^^^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #48556 <https://github.com/rust-lang/rust/issues/48556> for more information
   = help: add `#![feature(stdsimd)]` to the crate attributes to enable

error[E0658]: use of unstable library feature 'stdsimd'
  --> /home/esteve/.cargo/registry/src/index.crates.io-6f17d22bba15001f/curve25519-dalek-4.1.2/src/backend/vector/ifma/field.rs:33:9
   |
33 |     use core::arch::x86_64::_mm256_madd52hi_epu64;
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #48556 <https://github.com/rust-lang/rust/issues/48556> for more information
   = help: add `#![feature(stdsimd)]` to the crate attributes to enable

error[E0635]: unknown feature `stdarch_x86_avx512`
  --> /home/esteve/.cargo/registry/src/index.crates.io-6f17d22bba15001f/curve25519-dalek-4.1.2/src/lib.rs:19:13
   |
19 |     feature(stdarch_x86_avx512)
   |             ^^^^^^^^^^^^^^^^^^

@azteca1998
Copy link
Collaborator

I cannot run the tests using an aarch64 architecture either:

error[E0432]: unresolved import `cairo_vm::felt`
  --> crates/ef-testing/src/evm_sequencer/account/mod.rs:52:19
   |
52 |     use cairo_vm::felt::Felt252;
   |                   ^^^^ could not find `felt` in `cairo_vm`

error[E0603]: struct `Address` is private
  --> crates/ef-testing/src/evm_sequencer/account/mod.rs:56:34
   |
56 |     use starknet_in_rust::utils::Address as StarknetAddress;
   |                                  ^^^^^^^ private struct
   |
note: the struct `Address` is defined here
  --> /Users/esteve/.cargo/git/checkouts/starknet_in_rust-2186759e39c626a1/8704fd3/src/utils.rs:4:26
   |
4  | use crate::transaction::{Address, ClassHash};
   |                          ^^^^^^^

@greged93
Copy link
Collaborator Author

greged93 commented Mar 28, 2024

@azteca1998 did you try from the starknet in rust repo? I have a PR up normally to add the test to the SiR suite.

lambdaclass/starknet_in_rust#1260

@azteca1998
Copy link
Collaborator

I managed to get it working with your SiR PR after tweaking it a bit (mostly running makefile rules until it had the necessary files). I think that specific error may have been fixed indirectly by the other bug's fix.

Right now it's failing with the following:

---- integration_tests::complex_contracts::kakarot::test_kakarot_contract stdout ----
thread 'integration_tests::complex_contracts::kakarot::test_kakarot_contract' panicked at tests/integration_tests/cairo_native.rs:1082:14:
called `Result::unwrap()` on an `Err` value: WrongValidateRetdata
stack backtrace:
   0: rust_begin_unwind
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/panicking.rs:72:14
   2: core::result::unwrap_failed
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/result.rs:1653:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/result.rs:1077:23
   4: tests::integration_tests::cairo_native::TestState::execute_transaction
             at ./tests/integration_tests/cairo_native.rs:1074:39
   5: tests::integration_tests::complex_contracts::kakarot::test_kakarot_contract
             at ./tests/integration_tests/complex_contracts/kakarot/mod.rs:299:28
   6: tests::integration_tests::complex_contracts::kakarot::test_kakarot_contract::{{closure}}
             at ./tests/integration_tests/complex_contracts/kakarot/mod.rs:22:27
   7: core::ops::function::FnOnce::call_once
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5
   8: core::ops::function::FnOnce::call_once
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Could you confirm if that's the case?

@greged93
Copy link
Collaborator Author

That was one of the error, the parse result error comes from the execution part so you need to run the transaction skipping the validation

@azteca1998
Copy link
Collaborator

I see, I commented out the validation and now it just segfaults on a memcpy operation within the contract trying to read or write from/to a null pointer. Do you have the Cairo source for the ExternallyOwnedAccount contract? More specifically I'd need the contracts::eoa::ExternallyOwnedAccount::AccountContractImpl::__execute__() function.

@greged93
Copy link
Collaborator Author

Weird, maybe I was on another commit, can check tmr. Here is the execute

https://github.com/kkrt-labs/kakarot-ssj/blob/main/crates/contracts/src/eoa.cairo

@azteca1998
Copy link
Collaborator

I've been debugging for a few hours now and I've found the following:

  • The program below crashes so hard that not even RUST_BACKTRACE=1 works, so I can't know if the stack trace differs between a normal and a debugger run. I'll assume they both crash at the same instruction.
  • It crashes in the try_into_bytes helper method, outside the loop (I know this because the loop gets its own function).
  • A register with a pointer saved in the stack is being overwritten, causing it to have the wrong value (lower 32 bits replaced with the array length, maybe the final i) once restored at the end of the function (again, yes).
  • The offending store instruction is located in the generated expr29 function that contains the loop.
  • That instruction only gets executed when the loop ends. That is, after (or when) a break statement is executed. Only the first
    break statement can be executed because every array item fits within a u8.
  • If the array has data, then the data is extracted (the debugger executes the array extraction logic successfully) before the value is overwritten. That's why I know it's not caused by the loop function initialization.
  • An empty array also crashes the program in the same way (same register gets overwritten, this time with a zero because the array length is zero). That's why I know it's not caused by anything within the loop directly.

I tried simplifying the program, but:

  • If I remove the unreachable break (the one in the match), then the program works as expected.
  • If I remove the bytes.append(v); statement, then the program works as expected.
  • I can't remove anything else without causing an infinite loop or breaking the program (as in the program no longer compiles kind of breaking, nothing to do with native).
fn try_into_bytes(self: Span<felt252>) {
    let mut i = 0;
    let mut bytes: Array<u8> = Default::default();

    loop {
        if (i == self.len()) {
            break ();
        };

        let v: Option<u8> = (*self[i]).try_into();

        match v {
            Option::Some(v) => { bytes.append(v); },
            Option::None => { break (); }
        }

        i += 1;
    };
}

fn main() {
    let mut data = array![0, 1, 2, 3, 4, 5, 6, 7].span();
    try_into_bytes(data);
}

I'll include here the loop function's MLIR and LLVM IR. Of course I've also tried reading those IRs but I haven't been able to find anything strange there.

Loop function's MLIR
  llvm.func @"simple::simple::try_into_bytes[expr29](f0)"(%arg0: i64, %arg1: i128, %arg2: i32, %arg3: !llvm.struct<(struct<(ptr, i32, i32, i32)>)>, %arg4: !llvm.struct<(ptr, i32, i32, i32)>) -> !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> attributes {llvm.emit_c_interface, sym_visibility = "public"} {
    %0 = llvm.mlir.constant(256 : i64) : i64
    %1 = llvm.mlir.constant(375233589013918064796019 : i252) : i252
    %2 = llvm.mlir.constant(true) : i1
    %3 = llvm.mlir.constant(1 : i64) : i64
    %4 = llvm.mlir.constant(1024 : i32) : i32
    %5 = llvm.mlir.constant(8 : i32) : i32
    %6 = llvm.mlir.constant(0 : i32) : i32
    %7 = llvm.mlir.constant(1 : i32) : i32
    %8 = llvm.mlir.constant(false) : i1
    %9 = llvm.mlir.constant(9120 : i128) : i128
    %10 = llvm.mlir.constant(0 : i64) : i64
    %11 = llvm.mlir.constant(1 : index) : i64
    %12 = llvm.alloca %11 x i64 : (i64) -> !llvm.ptr
    llvm.store %10, %12 : i64, !llvm.ptr
    %13 = llvm.alloca %3 x !llvm.struct<(i64, array<24 x i8>)> {alignment = 8 : i64} : (i64) -> !llvm.ptr
    %14 = llvm.alloca %3 x !llvm.struct<(i64, array<24 x i8>)> {alignment = 8 : i64} : (i64) -> !llvm.ptr
    %15 = llvm.alloca %3 x !llvm.struct<(i64, array<20 x i8>)> {alignment = 8 : i64} : (i64) -> !llvm.ptr
    %16 = llvm.alloca %3 x !llvm.struct<(i64, array<24 x i8>)> {alignment = 8 : i64} : (i64) -> !llvm.ptr
    %17 = llvm.alloca %3 x !llvm.struct<(i8, array<1 x i8>)> {alignment = 1 : i64} : (i64) -> !llvm.ptr
    %18 = llvm.alloca %3 x !llvm.struct<(i64, array<24 x i8>)> {alignment = 8 : i64} : (i64) -> !llvm.ptr
    %19 = llvm.alloca %3 x !llvm.struct<(i64, array<20 x i8>)> {alignment = 8 : i64} : (i64) -> !llvm.ptr
    %20 = llvm.alloca %3 x !llvm.struct<(i64, array<24 x i8>)> {alignment = 8 : i64} : (i64) -> !llvm.ptr
    llvm.br ^bb1(%arg0, %arg1, %arg2, %arg3, %arg4 : i64, i128, i32, !llvm.struct<(struct<(ptr, i32, i32, i32)>)>, !llvm.struct<(ptr, i32, i32, i32)>)
  ^bb1(%21: i64, %22: i128, %23: i32, %24: !llvm.struct<(struct<(ptr, i32, i32, i32)>)>, %25: !llvm.struct<(ptr, i32, i32, i32)>):  // 2 preds: ^bb0, ^bb14
    %26 = llvm.add %21, %3  : i64
    %27 = llvm.icmp "uge" %22, %9 : i128
    %28 = llvm.call_intrinsic "llvm.usub.sat"(%22, %9) : (i128, i128) -> i128  {intrin = "llvm.usub.sat"}
    llvm.cond_br %27, ^bb2(%24 : !llvm.struct<(struct<(ptr, i32, i32, i32)>)>), ^bb15(%25 : !llvm.struct<(ptr, i32, i32, i32)>)
  ^bb2(%29: !llvm.struct<(struct<(ptr, i32, i32, i32)>)>):  // pred: ^bb1
    %30 = llvm.extractvalue %29[0] : !llvm.struct<(struct<(ptr, i32, i32, i32)>)> 
    %31 = llvm.extractvalue %30[1] : !llvm.struct<(ptr, i32, i32, i32)> 
    %32 = llvm.extractvalue %30[2] : !llvm.struct<(ptr, i32, i32, i32)> 
    %33 = llvm.sub %32, %31  : i32
    %34 = llvm.icmp "eq" %23, %33 : i32
    llvm.cond_br %34, ^bb9(%14, %14, %26 : !llvm.ptr, !llvm.ptr, i64), ^bb3(%29 : !llvm.struct<(struct<(ptr, i32, i32, i32)>)>)
  ^bb3(%35: !llvm.struct<(struct<(ptr, i32, i32, i32)>)>):  // pred: ^bb2
    %36 = llvm.extractvalue %35[0] : !llvm.struct<(struct<(ptr, i32, i32, i32)>)> 
    %37 = llvm.call @"core::array::array_at::<core::felt252>(f6)"(%26, %36, %23) : (i64, !llvm.struct<(ptr, i32, i32, i32)>, i32) -> !llvm.struct<(i64, struct<(i64, array<20 x i8>)>)>
    %38 = llvm.extractvalue %37[0] : !llvm.struct<(i64, struct<(i64, array<20 x i8>)>)> 
    %39 = llvm.extractvalue %37[1] : !llvm.struct<(i64, struct<(i64, array<20 x i8>)>)> 
    llvm.store %39, %15 {alignment = 8 : i64} : !llvm.struct<(i64, array<20 x i8>)>, !llvm.ptr
    %40 = llvm.load %15 {alignment = 8 : i64} : !llvm.ptr -> i1
    llvm.switch %40 : i1, ^bb4 [
      0: ^bb6,
      1: ^bb7(%15, %25, %16, %16, %38 : !llvm.ptr, !llvm.struct<(ptr, i32, i32, i32)>, !llvm.ptr, !llvm.ptr, i64)
    ]
  ^bb4:  // 3 preds: ^bb3, ^bb6, ^bb13
    llvm.cond_br %8, ^bb5, ^bb16
  ^bb5:  // pred: ^bb4
    llvm.unreachable
  ^bb6:  // pred: ^bb3
    %41 = llvm.load %15 {alignment = 8 : i64} : !llvm.ptr -> !llvm.struct<(i1, struct<(ptr)>)>
    %42 = llvm.extractvalue %41[1] : !llvm.struct<(i1, struct<(ptr)>)> 
    %43 = llvm.extractvalue %42[0] : !llvm.struct<(ptr)> 
    %44 = llvm.load %43 {alignment = 8 : i64} : !llvm.ptr -> i252
    llvm.call @free(%43) : (!llvm.ptr) -> ()
    %45 = llvm.call @"core::integer::Felt252TryIntoU8::try_into(f5)"(%38, %44) : (i64, i252) -> !llvm.struct<(i64, struct<(i8, array<1 x i8>)>)>
    %46 = llvm.extractvalue %45[0] : !llvm.struct<(i64, struct<(i8, array<1 x i8>)>)> 
    %47 = llvm.extractvalue %45[1] : !llvm.struct<(i64, struct<(i8, array<1 x i8>)>)> 
    llvm.store %47, %17 {alignment = 1 : i64} : !llvm.struct<(i8, array<1 x i8>)>, !llvm.ptr
    %48 = llvm.load %17 {alignment = 1 : i64} : !llvm.ptr -> i1
    llvm.switch %48 : i1, ^bb4 [
      0: ^bb8,
      1: ^bb9(%18, %18, %46 : !llvm.ptr, !llvm.ptr, i64)
    ]
  ^bb7(%49: !llvm.ptr, %50: !llvm.struct<(ptr, i32, i32, i32)>, %51: !llvm.ptr, %52: !llvm.ptr, %53: i64):  // 2 preds: ^bb3, ^bb13
    %54 = llvm.load %49 {alignment = 8 : i64} : !llvm.ptr -> !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)>
    %55 = llvm.extractvalue %54[1] : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)> 
    %56 = llvm.extractvalue %50[0] : !llvm.struct<(ptr, i32, i32, i32)> 
    llvm.call @free(%56) : (!llvm.ptr) -> ()
    %57 = llvm.mlir.undef : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)>
    %58 = llvm.insertvalue %2, %57[0] : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)> 
    %59 = llvm.insertvalue %55, %58[1] : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)> 
    llvm.store %59, %51 {alignment = 8 : i64} : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)>, !llvm.ptr
    %60 = llvm.load %52 {alignment = 8 : i64} : !llvm.ptr -> !llvm.struct<(i64, array<24 x i8>)>
    %61 = llvm.mlir.undef : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)>
    %62 = llvm.insertvalue %53, %61[0] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    %63 = llvm.insertvalue %28, %62[1] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    %64 = llvm.insertvalue %60, %63[2] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    llvm.return %64 : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)>
  ^bb8:  // pred: ^bb6
    %65 = llvm.load %17 {alignment = 1 : i64} : !llvm.ptr -> !llvm.struct<(i1, i8)>
    %66 = llvm.extractvalue %65[1] : !llvm.struct<(i1, i8)> 
    %67 = llvm.extractvalue %25[2] : !llvm.struct<(ptr, i32, i32, i32)> 
    %68 = llvm.extractvalue %25[3] : !llvm.struct<(ptr, i32, i32, i32)> 
    %69 = llvm.icmp "ult" %67, %68 : i32
    llvm.cond_br %69, ^bb13(%25 : !llvm.struct<(ptr, i32, i32, i32)>), ^bb10
  ^bb9(%70: !llvm.ptr, %71: !llvm.ptr, %72: i64):  // 2 preds: ^bb2, ^bb6
    %73 = llvm.mlir.undef : !llvm.struct<()>
    %74 = llvm.mlir.undef : !llvm.struct<(struct<(ptr, i32, i32, i32)>, i32, struct<()>)>
    %75 = llvm.insertvalue %25, %74[0] : !llvm.struct<(struct<(ptr, i32, i32, i32)>, i32, struct<()>)> 
    %76 = llvm.insertvalue %23, %75[1] : !llvm.struct<(struct<(ptr, i32, i32, i32)>, i32, struct<()>)> 
    %77 = llvm.insertvalue %73, %76[2] : !llvm.struct<(struct<(ptr, i32, i32, i32)>, i32, struct<()>)> 
    %78 = llvm.mlir.undef : !llvm.struct<(i1, struct<(struct<(ptr, i32, i32, i32)>, i32, struct<()>)>)>
    %79 = llvm.insertvalue %8, %78[0] : !llvm.struct<(i1, struct<(struct<(ptr, i32, i32, i32)>, i32, struct<()>)>)> 
    %80 = llvm.insertvalue %77, %79[1] : !llvm.struct<(i1, struct<(struct<(ptr, i32, i32, i32)>, i32, struct<()>)>)> 
    llvm.store %80, %70 {alignment = 8 : i64} : !llvm.struct<(i1, struct<(struct<(ptr, i32, i32, i32)>, i32, struct<()>)>)>, !llvm.ptr
    %81 = llvm.load %71 {alignment = 8 : i64} : !llvm.ptr -> !llvm.struct<(i64, array<24 x i8>)>
    %82 = llvm.mlir.undef : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)>
    %83 = llvm.insertvalue %72, %82[0] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    %84 = llvm.insertvalue %28, %83[1] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    %85 = llvm.insertvalue %81, %84[2] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    llvm.return %85 : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)>
  ^bb10:  // pred: ^bb8
    %86 = llvm.extractvalue %25[1] : !llvm.struct<(ptr, i32, i32, i32)> 
    %87 = llvm.icmp "ne" %86, %6 : i32
    llvm.cond_br %87, ^bb11, ^bb12
  ^bb11:  // pred: ^bb10
    %88 = llvm.extractvalue %25[1] : !llvm.struct<(ptr, i32, i32, i32)> 
    %89 = llvm.zext %88 : i32 to i64
    %90 = llvm.extractvalue %25[0] : !llvm.struct<(ptr, i32, i32, i32)> 
    %91 = llvm.getelementptr %90[%89] : (!llvm.ptr, i64) -> !llvm.ptr, i8
    %92 = llvm.sub %67, %88  : i32
    %93 = llvm.zext %92 : i32 to i64
    llvm.call_intrinsic "llvm.memmove"(%90, %91, %93, %8) : (!llvm.ptr, !llvm.ptr, i64, i1) -> ()  {intrin = "llvm.memmove"}
    %94 = llvm.insertvalue %6, %25[1] : !llvm.struct<(ptr, i32, i32, i32)> 
    %95 = llvm.insertvalue %92, %94[2] : !llvm.struct<(ptr, i32, i32, i32)> 
    llvm.br ^bb13(%95 : !llvm.struct<(ptr, i32, i32, i32)>)
  ^bb12:  // pred: ^bb10
    %96 = llvm.shl %67, %7  : i32
    %97 = llvm.intr.umin(%96, %4)  : (i32, i32) -> i32
    %98 = llvm.add %97, %67  : i32
    %99 = llvm.intr.umax(%98, %5)  : (i32, i32) -> i32
    %100 = llvm.zext %99 : i32 to i64
    %101 = llvm.extractvalue %25[0] : !llvm.struct<(ptr, i32, i32, i32)> 
    %102 = llvm.call @realloc(%101, %100) : (!llvm.ptr, i64) -> !llvm.ptr
    %103 = llvm.insertvalue %102, %25[0] : !llvm.struct<(ptr, i32, i32, i32)> 
    %104 = llvm.insertvalue %99, %103[3] : !llvm.struct<(ptr, i32, i32, i32)> 
    llvm.br ^bb13(%104 : !llvm.struct<(ptr, i32, i32, i32)>)
  ^bb13(%105: !llvm.struct<(ptr, i32, i32, i32)>):  // 3 preds: ^bb8, ^bb11, ^bb12
    %106 = llvm.extractvalue %105[0] : !llvm.struct<(ptr, i32, i32, i32)> 
    %107 = llvm.extractvalue %105[2] : !llvm.struct<(ptr, i32, i32, i32)> 
    %108 = llvm.zext %107 : i32 to i64
    %109 = llvm.getelementptr %106[%108] : (!llvm.ptr, i64) -> !llvm.ptr, i8
    llvm.store %66, %109 {alignment = 1 : i64} : i8, !llvm.ptr
    %110 = llvm.add %107, %7  : i32
    %111 = llvm.insertvalue %110, %105[2] : !llvm.struct<(ptr, i32, i32, i32)> 
    %112 = llvm.call @"core::integer::U32Add::add(f3)"(%46, %23, %7) : (i64, i32, i32) -> !llvm.struct<(i64, struct<(i64, array<20 x i8>)>)>
    %113 = llvm.extractvalue %112[0] : !llvm.struct<(i64, struct<(i64, array<20 x i8>)>)> 
    %114 = llvm.extractvalue %112[1] : !llvm.struct<(i64, struct<(i64, array<20 x i8>)>)> 
    llvm.store %114, %19 {alignment = 8 : i64} : !llvm.struct<(i64, array<20 x i8>)>, !llvm.ptr
    %115 = llvm.load %19 {alignment = 8 : i64} : !llvm.ptr -> i1
    llvm.switch %115 : i1, ^bb4 [
      0: ^bb14,
      1: ^bb7(%19, %105, %20, %20, %113 : !llvm.ptr, !llvm.struct<(ptr, i32, i32, i32)>, !llvm.ptr, !llvm.ptr, i64)
    ]
  ^bb14:  // pred: ^bb13
    %116 = llvm.load %19 {alignment = 8 : i64} : !llvm.ptr -> !llvm.struct<(i1, struct<(i32)>)>
    %117 = llvm.extractvalue %116[1] : !llvm.struct<(i1, struct<(i32)>)> 
    %118 = llvm.extractvalue %117[0] : !llvm.struct<(i32)> 
    %119 = llvm.load %12 : !llvm.ptr -> i64
    %120 = llvm.add %119, %3  : i64
    llvm.store %120, %12 : i64, !llvm.ptr
    llvm.br ^bb1(%113, %28, %118, %35, %111 : i64, i128, i32, !llvm.struct<(struct<(ptr, i32, i32, i32)>)>, !llvm.struct<(ptr, i32, i32, i32)>)
  ^bb15(%121: !llvm.struct<(ptr, i32, i32, i32)>):  // pred: ^bb1
    %122 = llvm.extractvalue %121[0] : !llvm.struct<(ptr, i32, i32, i32)> 
    llvm.call @free(%122) : (!llvm.ptr) -> ()
    %123 = llvm.mlir.null : !llvm.ptr
    %124 = llvm.mlir.undef : !llvm.struct<(ptr, i32, i32, i32)>
    %125 = llvm.insertvalue %123, %124[0] : !llvm.struct<(ptr, i32, i32, i32)> 
    %126 = llvm.insertvalue %6, %125[1] : !llvm.struct<(ptr, i32, i32, i32)> 
    %127 = llvm.insertvalue %6, %126[2] : !llvm.struct<(ptr, i32, i32, i32)> 
    %128 = llvm.insertvalue %6, %127[3] : !llvm.struct<(ptr, i32, i32, i32)> 
    %129 = llvm.call @realloc(%123, %0) : (!llvm.ptr, i64) -> !llvm.ptr
    %130 = llvm.insertvalue %129, %128[0] : !llvm.struct<(ptr, i32, i32, i32)> 
    %131 = llvm.insertvalue %5, %130[3] : !llvm.struct<(ptr, i32, i32, i32)> 
    llvm.store %1, %129 {alignment = 8 : i64} : i252, !llvm.ptr
    %132 = llvm.insertvalue %7, %131[2] : !llvm.struct<(ptr, i32, i32, i32)> 
    %133 = llvm.mlir.undef : !llvm.struct<()>
    %134 = llvm.mlir.undef : !llvm.struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>
    %135 = llvm.insertvalue %133, %134[0] : !llvm.struct<(struct<()>, struct<(ptr, i32, i32, i32)>)> 
    %136 = llvm.insertvalue %132, %135[1] : !llvm.struct<(struct<()>, struct<(ptr, i32, i32, i32)>)> 
    %137 = llvm.mlir.undef : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)>
    %138 = llvm.insertvalue %2, %137[0] : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)> 
    %139 = llvm.insertvalue %136, %138[1] : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)> 
    llvm.store %139, %13 {alignment = 8 : i64} : !llvm.struct<(i1, struct<(struct<()>, struct<(ptr, i32, i32, i32)>)>)>, !llvm.ptr
    %140 = llvm.load %13 {alignment = 8 : i64} : !llvm.ptr -> !llvm.struct<(i64, array<24 x i8>)>
    %141 = llvm.mlir.undef : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)>
    %142 = llvm.insertvalue %26, %141[0] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    %143 = llvm.insertvalue %28, %142[1] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    %144 = llvm.insertvalue %140, %143[2] : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)> 
    llvm.return %144 : !llvm.struct<(i64, i128, struct<(i64, array<24 x i8>)>)>
  ^bb16:  // pred: ^bb4
    %145 = llvm.mlir.addressof @assert_msg_1 : !llvm.ptr
    llvm.call @puts(%145) : (!llvm.ptr) -> ()
    llvm.call @abort() : () -> ()
    llvm.unreachable
  }
Loop function's LLVM IR
define { i64, i128, { i64, [24 x i8] } } @"simple::simple::try_into_bytes[expr29](f0)"(i64 %0, i128 %1, i32 %2, { { ptr, i32, i32, i32 } } %3, { ptr, i32, i32, i32 } %4) {
  %6 = alloca i64, i64 1, align 8
  store i64 0, ptr %6, align 8
  %7 = alloca { i64, [24 x i8] }, i64 1, align 8
  %8 = alloca { i64, [24 x i8] }, i64 1, align 8
  %9 = alloca { i64, [20 x i8] }, i64 1, align 8
  %10 = alloca { i64, [24 x i8] }, i64 1, align 8
  %11 = alloca { i8, [1 x i8] }, i64 1, align 1
  %12 = alloca { i64, [24 x i8] }, i64 1, align 8
  %13 = alloca { i64, [20 x i8] }, i64 1, align 8
  %14 = alloca { i64, [24 x i8] }, i64 1, align 8
  br label %15

15:                                               ; preds = %115, %5
  %16 = phi i64 [ %112, %115 ], [ %0, %5 ]
  %17 = phi i128 [ %23, %115 ], [ %1, %5 ]
  %18 = phi i32 [ %118, %115 ], [ %2, %5 ]
  %19 = phi { { ptr, i32, i32, i32 } } [ %32, %115 ], [ %3, %5 ]
  %20 = phi { ptr, i32, i32, i32 } [ %110, %115 ], [ %4, %5 ]
  %21 = add i64 %16, 1
  %22 = icmp uge i128 %17, 9120
  %23 = call i128 @llvm.usub.sat.i128(i128 %17, i128 9120)
  br i1 %22, label %24, label %121

24:                                               ; preds = %15
  %25 = phi { { ptr, i32, i32, i32 } } [ %19, %15 ]
  %26 = extractvalue { { ptr, i32, i32, i32 } } %25, 0
  %27 = extractvalue { ptr, i32, i32, i32 } %26, 1
  %28 = extractvalue { ptr, i32, i32, i32 } %26, 2
  %29 = sub i32 %28, %27
  %30 = icmp eq i32 %18, %29
  br i1 %30, label %69, label %31

31:                                               ; preds = %24
  %32 = phi { { ptr, i32, i32, i32 } } [ %25, %24 ]
  %33 = extractvalue { { ptr, i32, i32, i32 } } %32, 0
  %34 = call { i64, { i64, [20 x i8] } } @"core::array::array_at::<core::felt252>(f6)"(i64 %21, { ptr, i32, i32, i32 } %33, i32 %18)
  %35 = extractvalue { i64, { i64, [20 x i8] } } %34, 0
  %36 = extractvalue { i64, { i64, [20 x i8] } } %34, 1
  store { i64, [20 x i8] } %36, ptr %9, align 8
  %37 = load i1, ptr %9, align 8
  switch i1 %37, label %38 [
    i1 false, label %40
    i1 true, label %49
  ]

38:                                               ; preds = %103, %40, %31
  br i1 false, label %39, label %134

39:                                               ; preds = %38
  unreachable

40:                                               ; preds = %31
  %41 = load { i1, { ptr } }, ptr %9, align 8
  %42 = extractvalue { i1, { ptr } } %41, 1
  %43 = extractvalue { ptr } %42, 0
  %44 = load i252, ptr %43, align 8
  call void @free(ptr %43)
  %45 = call { i64, { i8, [1 x i8] } } @"core::integer::Felt252TryIntoU8::try_into(f5)"(i64 %35, i252 %44)
  %46 = extractvalue { i64, { i8, [1 x i8] } } %45, 0
  %47 = extractvalue { i64, { i8, [1 x i8] } } %45, 1
  store { i8, [1 x i8] } %47, ptr %11, align 1
  %48 = load i1, ptr %11, align 1
  switch i1 %48, label %38 [
    i1 false, label %63
    i1 true, label %69
  ]

49:                                               ; preds = %103, %31
  %50 = phi ptr [ %13, %103 ], [ %9, %31 ]
  %51 = phi { ptr, i32, i32, i32 } [ %104, %103 ], [ %20, %31 ]
  %52 = phi ptr [ %14, %103 ], [ %10, %31 ]
  %53 = phi ptr [ %14, %103 ], [ %10, %31 ]
  %54 = phi i64 [ %112, %103 ], [ %35, %31 ]
  %55 = load { i1, { {}, { ptr, i32, i32, i32 } } }, ptr %50, align 8
  %56 = extractvalue { i1, { {}, { ptr, i32, i32, i32 } } } %55, 1
  %57 = extractvalue { ptr, i32, i32, i32 } %51, 0
  call void @free(ptr %57)
  %58 = insertvalue { i1, { {}, { ptr, i32, i32, i32 } } } { i1 true, { {}, { ptr, i32, i32, i32 } } undef }, { {}, { ptr, i32, i32, i32 } } %56, 1
  store { i1, { {}, { ptr, i32, i32, i32 } } } %58, ptr %52, align 8
  %59 = load { i64, [24 x i8] }, ptr %53, align 8
  %60 = insertvalue { i64, i128, { i64, [24 x i8] } } undef, i64 %54, 0
  %61 = insertvalue { i64, i128, { i64, [24 x i8] } } %60, i128 %23, 1
  %62 = insertvalue { i64, i128, { i64, [24 x i8] } } %61, { i64, [24 x i8] } %59, 2
  ret { i64, i128, { i64, [24 x i8] } } %62

63:                                               ; preds = %40
  %64 = load { i1, i8 }, ptr %11, align 1
  %65 = extractvalue { i1, i8 } %64, 1
  %66 = extractvalue { ptr, i32, i32, i32 } %20, 2
  %67 = extractvalue { ptr, i32, i32, i32 } %20, 3
  %68 = icmp ult i32 %66, %67
  br i1 %68, label %103, label %81

69:                                               ; preds = %40, %24
  %70 = phi ptr [ %12, %40 ], [ %8, %24 ]
  %71 = phi ptr [ %12, %40 ], [ %8, %24 ]
  %72 = phi i64 [ %46, %40 ], [ %21, %24 ]
  %73 = insertvalue { { ptr, i32, i32, i32 }, i32, {} } undef, { ptr, i32, i32, i32 } %20, 0
  %74 = insertvalue { { ptr, i32, i32, i32 }, i32, {} } %73, i32 %18, 1
  %75 = insertvalue { { ptr, i32, i32, i32 }, i32, {} } %74, {} undef, 2
  %76 = insertvalue { i1, { { ptr, i32, i32, i32 }, i32, {} } } { i1 false, { { ptr, i32, i32, i32 }, i32, {} } undef }, { { ptr, i32, i32, i32 }, i32, {} } %75, 1
  store { i1, { { ptr, i32, i32, i32 }, i32, {} } } %76, ptr %70, align 8
  %77 = load { i64, [24 x i8] }, ptr %71, align 8
  %78 = insertvalue { i64, i128, { i64, [24 x i8] } } undef, i64 %72, 0
  %79 = insertvalue { i64, i128, { i64, [24 x i8] } } %78, i128 %23, 1
  %80 = insertvalue { i64, i128, { i64, [24 x i8] } } %79, { i64, [24 x i8] } %77, 2
  ret { i64, i128, { i64, [24 x i8] } } %80

81:                                               ; preds = %63
  %82 = extractvalue { ptr, i32, i32, i32 } %20, 1
  %83 = icmp ne i32 %82, 0
  br i1 %83, label %84, label %93

84:                                               ; preds = %81
  %85 = extractvalue { ptr, i32, i32, i32 } %20, 1
  %86 = zext i32 %85 to i64
  %87 = extractvalue { ptr, i32, i32, i32 } %20, 0
  %88 = getelementptr i8, ptr %87, i64 %86
  %89 = sub i32 %66, %85
  %90 = zext i32 %89 to i64
  call void @llvm.memmove.p0.p0.i64(ptr %87, ptr %88, i64 %90, i1 false)
  %91 = insertvalue { ptr, i32, i32, i32 } %20, i32 0, 1
  %92 = insertvalue { ptr, i32, i32, i32 } %91, i32 %89, 2
  br label %103

93:                                               ; preds = %81
  %94 = shl i32 %66, 1
  %95 = call i32 @llvm.umin.i32(i32 %94, i32 1024)
  %96 = add i32 %95, %66
  %97 = call i32 @llvm.umax.i32(i32 %96, i32 8)
  %98 = zext i32 %97 to i64
  %99 = extractvalue { ptr, i32, i32, i32 } %20, 0
  %100 = call ptr @realloc(ptr %99, i64 %98)
  %101 = insertvalue { ptr, i32, i32, i32 } %20, ptr %100, 0
  %102 = insertvalue { ptr, i32, i32, i32 } %101, i32 %97, 3
  br label %103

103:                                              ; preds = %84, %93, %63
  %104 = phi { ptr, i32, i32, i32 } [ %102, %93 ], [ %92, %84 ], [ %20, %63 ]
  %105 = extractvalue { ptr, i32, i32, i32 } %104, 0
  %106 = extractvalue { ptr, i32, i32, i32 } %104, 2
  %107 = zext i32 %106 to i64
  %108 = getelementptr i8, ptr %105, i64 %107
  store i8 %65, ptr %108, align 1
  %109 = add i32 %106, 1
  %110 = insertvalue { ptr, i32, i32, i32 } %104, i32 %109, 2
  %111 = call { i64, { i64, [20 x i8] } } @"core::integer::U32Add::add(f3)"(i64 %46, i32 %18, i32 1)
  %112 = extractvalue { i64, { i64, [20 x i8] } } %111, 0
  %113 = extractvalue { i64, { i64, [20 x i8] } } %111, 1
  store { i64, [20 x i8] } %113, ptr %13, align 8
  %114 = load i1, ptr %13, align 8
  switch i1 %114, label %38 [
    i1 false, label %115
    i1 true, label %49
  ]

115:                                              ; preds = %103
  %116 = load { i1, { i32 } }, ptr %13, align 8
  %117 = extractvalue { i1, { i32 } } %116, 1
  %118 = extractvalue { i32 } %117, 0
  %119 = load i64, ptr %6, align 8
  %120 = add i64 %119, 1
  store i64 %120, ptr %6, align 8
  br label %15

121:                                              ; preds = %15
  %122 = phi { ptr, i32, i32, i32 } [ %20, %15 ]
  %123 = extractvalue { ptr, i32, i32, i32 } %122, 0
  call void @free(ptr %123)
  %124 = call ptr @realloc(ptr null, i64 256)
  %125 = insertvalue { ptr, i32, i32, i32 } zeroinitializer, ptr %124, 0
  %126 = insertvalue { ptr, i32, i32, i32 } %125, i32 8, 3
  store i252 375233589013918064796019, ptr %124, align 8
  %127 = insertvalue { ptr, i32, i32, i32 } %126, i32 1, 2
  %128 = insertvalue { {}, { ptr, i32, i32, i32 } } undef, { ptr, i32, i32, i32 } %127, 1
  %129 = insertvalue { i1, { {}, { ptr, i32, i32, i32 } } } { i1 true, { {}, { ptr, i32, i32, i32 } } undef }, { {}, { ptr, i32, i32, i32 } } %128, 1
  store { i1, { {}, { ptr, i32, i32, i32 } } } %129, ptr %7, align 8
  %130 = load { i64, [24 x i8] }, ptr %7, align 8
  %131 = insertvalue { i64, i128, { i64, [24 x i8] } } undef, i64 %21, 0
  %132 = insertvalue { i64, i128, { i64, [24 x i8] } } %131, i128 %23, 1
  %133 = insertvalue { i64, i128, { i64, [24 x i8] } } %132, { i64, [24 x i8] } %130, 2
  ret { i64, i128, { i64, [24 x i8] } } %133

134:                                              ; preds = %38
  call void @puts(ptr @assert_msg_1)
  call void @abort()
  unreachable
}

@greged93
Copy link
Collaborator Author

@azteca1998 just checking but you are getting this on the unwinding right:

thread 'integration_tests::complex_contracts::kakarot::test_kakarot_contract' panicked at /Users/greg/.cargo/git/checkouts/cairo_native-0bad8506281b7281/4cd14b1/src/values.rs:531:21:
assertion failed: length_value >= offset_value
stack backtrace:
   0: rust_begin_unwind
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/panicking.rs:72:14
   2: core::panicking::panic
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/panicking.rs:127:5
   3: cairo_native::values::JitValue::from_jit
             at /Users/greg/.cargo/git/checkouts/cairo_native-0bad8506281b7281/4cd14b1/src/values.rs:531:21
   4: cairo_native::values::JitValue::from_jit
             at /Users/greg/.cargo/git/checkouts/cairo_native-0bad8506281b7281/4cd14b1/src/values.rs:657:38
   5: cairo_native::executor::parse_result
             at /Users/greg/.cargo/git/checkouts/cairo_native-0bad8506281b7281/4cd14b1/src/executor.rs:731:37
   6: cairo_native::executor::invoke_dynamic
             at /Users/greg/.cargo/git/checkouts/cairo_native-0bad8506281b7281/4cd14b1/src/executor.rs:292:24
   7: cairo_native::executor::jit::JitNativeExecutor::invoke_contract_dynamic
             at /Users/greg/.cargo/git/checkouts/cairo_native-0bad8506281b7281/4cd14b1/src/executor/jit.rs:125:56
   8: starknet_in_rust::execution::execution_entry_point::ExecutionEntryPoint::native_execute
             at ./src/execution/execution_entry_point.rs:773:46
   9: starknet_in_rust::execution::execution_entry_point::ExecutionEntryPoint::execute
             at ./src/execution/execution_entry_point.rs:168:23
  10: starknet_in_rust::transaction::invoke_function::InvokeFunction::run_validate_entrypoint
             at ./src/transaction/invoke_function.rs:273:49
  11: starknet_in_rust::transaction::invoke_function::InvokeFunction::apply
             at ./src/transaction/invoke_function.rs:366:13
  12: starknet_in_rust::transaction::invoke_function::InvokeFunction::execute::{{closure}}
             at ./src/transaction/invoke_function.rs:461:28
  13: starknet_in_rust::transaction::invoke_function::InvokeFunction::execute
             at ./src/transaction/invoke_function.rs:425:5
  14: starknet_in_rust::transaction::Transaction::execute
             at ./src/transaction/mod.rs:224:48
  15: tests::integration_tests::cairo_native::TestState::execute_transaction
             at ./tests/integration_tests/cairo_native.rs:1074:39
  16: tests::integration_tests::complex_contracts::kakarot::test_kakarot_contract
             at ./tests/integration_tests/complex_contracts/kakarot/mod.rs:299:28
  17: tests::integration_tests::complex_contracts::kakarot::test_kakarot_contract::{{closure}}
             at ./tests/integration_tests/complex_contracts/kakarot/mod.rs:22:27
  18: core::ops::function::FnOnce::call_once
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5
  19: core::ops::function::FnOnce::call_once
             at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:250:5
libunwind: malformed __unwind_info at 0x18370BCA8 bad second level page
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

If I can help in anyway for debugging let me know. I also bumped native to the latest commit and the validate issue (WrongValidateRetdata ) seems to have been fixed with your latest PR.

@edg-l
Copy link
Member

edg-l commented Apr 11, 2024

Just in case, can you try to update to the latest native commit?

@greged93
Copy link
Collaborator Author

Bumped to 60ed062 but still getting the same error

@edg-l
Copy link
Member

edg-l commented Apr 12, 2024

This is more a comment to keep track of the information I've been finding.

On x86_64, the program from @azteca1998 segfaults, but on a Mac m1 it doesn't. My guess is that there are some assumptions here where in x86 it's more strict and on arm it isn't, but at the end the result is wrong nevertheless.

My guess is this is related to enum handling, probably panic related ones.

fn try_into_bytes(self: Span<felt252>) {
    let mut i = 0;
    let mut bytes: Array<u8> = Default::default();

    loop {
        // adding a print here makes it not segfault on x86
        if (i == self.len()) {
            break ();
        };

        let v: Option<u8> = (*self[i]).try_into();

        match v {
            Option::Some(v) => { bytes.append(v); },
            Option::None => { break (); }
        }

        i += 1;
    };
}

fn main() {
    let mut data = array![0, 1, 2, 3, 4, 5, 6, 7].span();
    try_into_bytes(data);
}

I could find a more minimal program:

fn try_into_bytes(self: Span<felt252>) {
    let mut i = 0;
    let mut bytes: Array<u8> = Default::default();

    loop {
        // removing self.len() to a 1 makes it not segfault in x86
        if (i == self.len()) {
            break ();
        };

        let v: Option<u8> = Option::None;

        match v {
            Option::Some(v) => {
                bytes.append(v);
            },
            Option::None => {
                break ();
            }
        }

        i += 1;
    };
}

fn main() {
    let mut data = array![].span();
    try_into_bytes(data);
}

With this we can rule out anything to do with the try_into.

Here is the x86 assembly of the first program with some comments:

Dump of assembler code for function program::program::try_into_bytes(f1):
   0x00007ffff7fbf000 <+0>:     push   r14
   0x00007ffff7fbf002 <+2>:     push   rbx ; here rbx has an addressable address
   0x00007ffff7fbf003 <+3>:     sub    rsp,0x98
   0x00007ffff7fbf00a <+10>:    mov    r10d,DWORD PTR [rsp+0xb0]
   0x00007ffff7fbf012 <+18>:    mov    r11d,DWORD PTR [rsp+0xb8]
   0x00007ffff7fbf01a <+26>:    mov    eax,r9d
   0x00007ffff7fbf01d <+29>:    mov    rbx,rdi
   0x00007ffff7fbf020 <+32>:    mov    r9,r8
   0x00007ffff7fbf023 <+35>:    sub    rsp,0x8
   0x00007ffff7fbf027 <+39>:    movabs r14,0x7ffff7fbf290
   0x00007ffff7fbf031 <+49>:    lea    rdi,[rsp+0x48]
   0x00007ffff7fbf036 <+54>:    xor    r8d,r8d
   0x00007ffff7fbf039 <+57>:    push   0x0
   0x00007ffff7fbf03b <+59>:    push   0x0
   0x00007ffff7fbf03d <+61>:    push   0x0
   0x00007ffff7fbf03f <+63>:    push   0x0
   0x00007ffff7fbf041 <+65>:    push   r11
   0x00007ffff7fbf043 <+67>:    push   r10
   0x00007ffff7fbf045 <+69>:    push   rax
   0x00007ffff7fbf046 <+70>:    call   r14 ; after this call, rbx has a invalid addr - program::program::try_into_bytes[expr29](f0)
   0x00007ffff7fbf049 <+73>:    add    rsp,0x40
   0x00007ffff7fbf04d <+77>:    vmovups xmm0,XMMWORD PTR [rsp+0x60]
   0x00007ffff7fbf053 <+83>:    mov    rsi,QWORD PTR [rsp+0x58]
   0x00007ffff7fbf058 <+88>:    mov    rax,QWORD PTR [rsp+0x40]
   0x00007ffff7fbf05d <+93>:    mov    rcx,QWORD PTR [rsp+0x48]
   0x00007ffff7fbf062 <+98>:    mov    rdx,QWORD PTR [rsp+0x50]
   0x00007ffff7fbf067 <+103>:   mov    rdi,QWORD PTR [rsp+0x70]
   0x00007ffff7fbf06c <+108>:   mov    QWORD PTR [rsp+0x20],rsi
   0x00007ffff7fbf071 <+113>:   mov    QWORD PTR [rsp+0x38],rdi
   0x00007ffff7fbf076 <+118>:   vmovups XMMWORD PTR [rsp+0x28],xmm0
   0x00007ffff7fbf07c <+124>:   test   sil,0x1
   0x00007ffff7fbf080 <+128>:   jne    0x7ffff7fbf0b5 <program::program::try_into_bytes(f1)+181>
   0x00007ffff7fbf082 <+130>:   vmovups xmm0,XMMWORD PTR [rsp+0x80]
   0x00007ffff7fbf08b <+139>:   mov    BYTE PTR [rsp+0x78],0x0
   0x00007ffff7fbf090 <+144>:   mov    edi,DWORD PTR [rsp+0x90]
   0x00007ffff7fbf097 <+151>:   mov    rsi,QWORD PTR [rsp+0x78]
=> 0x00007ffff7fbf09c <+156>:   mov    QWORD PTR [rbx+0x10],rdx
   0x00007ffff7fbf0a0 <+160>:   mov    QWORD PTR [rbx+0x8],rcx
   0x00007ffff7fbf0a4 <+164>:   mov    DWORD PTR [rbx+0x30],edi

This is the ax86 asm of the call r14 with some comments

Dump of assembler code for function program::program::try_into_bytes[expr25](f0):
   0x00007ffff7fc22a0 <+0>:     push   r15
   0x00007ffff7fc22a2 <+2>:     push   r14
   0x00007ffff7fc22a4 <+4>:     push   r12
   0x00007ffff7fc22a6 <+6>:     push   rbx
   0x00007ffff7fc22a7 <+7>:     sub    rsp,0x68
   0x00007ffff7fc22ab <+11>:    int3
=> 0x00007ffff7fc22ac <+12>:    mov    r14,rdi
   0x00007ffff7fc22af <+15>:    mov    rdi,QWORD PTR [rsp+0xa8]
   0x00007ffff7fc22b7 <+23>:    mov    r12,rsi
   0x00007ffff7fc22ba <+26>:    inc    r12
   0x00007ffff7fc22bd <+29>:    mov    r15,rcx
   0x00007ffff7fc22c0 <+32>:    mov    rbx,rdx
   0x00007ffff7fc22c3 <+35>:    xor    eax,eax
   0x00007ffff7fc22c5 <+37>:    test   al,al
   0x00007ffff7fc22c7 <+39>:    jne    0x7ffff7fc233d <program::program::try_into_bytes[expr25](f0)+157>
   0x00007ffff7fc22c9 <+41>:    mov    esi,DWORD PTR [rsp+0x98]
   0x00007ffff7fc22d0 <+48>:    mov    eax,DWORD PTR [rsp+0xc0]
   0x00007ffff7fc22d7 <+55>:    mov    ecx,DWORD PTR [rsp+0xb8]
   0x00007ffff7fc22de <+62>:    mov    edx,DWORD PTR [rsp+0xb0]
   0x00007ffff7fc22e5 <+69>:    lea    r10,[rsp+0x48] ; this stack addr is loaded into r9 
   0x00007ffff7fc22ea <+74>:    lea    r9,[rsp+0x28] ; or this stack addr is loaded into r9
   0x00007ffff7fc22ef <+79>:    sub    esi,DWORD PTR [rsp+0x90]
   0x00007ffff7fc22f6 <+86>:    cmp    r8d,esi ; which depends on this
   0x00007ffff7fc22f9 <+89>:    cmove  r9,r10 ; at this cmov
   0x00007ffff7fc22fd <+93>:    mov    DWORD PTR [r9+0x14],ecx
   0x00007ffff7fc2301 <+97>:    mov    DWORD PTR [r9+0x10],edx
   0x00007ffff7fc2305 <+101>:   mov    QWORD PTR [r9+0x8],rdi
   0x00007ffff7fc2309 <+105>:   mov    DWORD PTR [r9+0x18],eax
   0x00007ffff7fc230d <+109>:   mov    DWORD PTR [r9+0x20],r8d
   0x00007ffff7fc2311 <+113>:   mov    BYTE PTR [r9],0x0 ; this breaks the value stored in rbx
   0x00007ffff7fc2315 <+117>:   vmovups xmm0,XMMWORD PTR [r9+0x8]
   0x00007ffff7fc231b <+123>:   mov    rax,QWORD PTR [r9]
   0x00007ffff7fc231e <+126>:   mov    rcx,QWORD PTR [r9+0x18]
   0x00007ffff7fc2322 <+130>:   mov    QWORD PTR [r14+0x10],r15
   0x00007ffff7fc2326 <+134>:   mov    QWORD PTR [r14+0x8],rbx
   0x00007ffff7fc232a <+138>:   mov    QWORD PTR [r14+0x30],rcx
   0x00007ffff7fc232e <+142>:   vmovups XMMWORD PTR [r14+0x20],xmm0
   0x00007ffff7fc2334 <+148>:   mov    QWORD PTR [r14+0x18],rax
   0x00007ffff7fc2338 <+152>:   mov    QWORD PTR [r14],r12
   0x00007ffff7fc233b <+155>:   jmp    0x7ffff7fc23b8 <program::program::try_into_bytes[expr25](f0)+280>
   0x00007ffff7fc233d <+157>:   movabs rax,0x7ffff7a0b0c0
   0x00007ffff7fc2347 <+167>:   call   rax
   0x00007ffff7fc2349 <+169>:   movabs rax,0x7ffff7a0b320
   0x00007ffff7fc2353 <+179>:   mov    esi,0x100
   0x00007ffff7fc2358 <+184>:   xor    edi,edi
   0x00007ffff7fc235a <+186>:   call   rax
   0x00007ffff7fc235c <+188>:   movabs rcx,0x7ffff7fc1020
   0x00007ffff7fc2366 <+198>:   vmovaps ymm0,YMMWORD PTR [rcx]
   0x00007ffff7fc236a <+202>:   movabs rcx,0x100000000
   0x00007ffff7fc2374 <+212>:   vmovups YMMWORD PTR [rax],ymm0
   0x00007ffff7fc2378 <+216>:   mov    QWORD PTR [rsp+0x18],rcx
   0x00007ffff7fc237d <+221>:   mov    QWORD PTR [rsp+0x10],rax
   0x00007ffff7fc2382 <+226>:   mov    BYTE PTR [rsp+0x8],0x1
   0x00007ffff7fc2387 <+231>:   mov    DWORD PTR [rsp+0x20],0x8
   0x00007ffff7fc238f <+239>:   vmovups xmm0,XMMWORD PTR [rsp+0x10]
   0x00007ffff7fc2395 <+245>:   mov    rax,QWORD PTR [rsp+0x8]
   0x00007ffff7fc239a <+250>:   mov    rcx,QWORD PTR [rsp+0x20]
   0x00007ffff7fc239f <+255>:   mov    QWORD PTR [r14],r12
   0x00007ffff7fc23a2 <+258>:   mov    QWORD PTR [r14+0x10],r15
   0x00007ffff7fc23a6 <+262>:   mov    QWORD PTR [r14+0x30],rcx
   0x00007ffff7fc23aa <+266>:   vmovups XMMWORD PTR [r14+0x20],xmm0
   0x00007ffff7fc23b0 <+272>:   mov    QWORD PTR [r14+0x18],rax
   0x00007ffff7fc23b4 <+276>:   mov    QWORD PTR [r14+0x8],rbx
   0x00007ffff7fc23b8 <+280>:   mov    rax,r14
   0x00007ffff7fc23bb <+283>:   add    rsp,0x68
   0x00007ffff7fc23bf <+287>:   pop    rbx
   0x00007ffff7fc23c0 <+288>:   pop    r12
   0x00007ffff7fc23c2 <+290>:   pop    r14
   0x00007ffff7fc23c4 <+292>:   pop    r15
   0x00007ffff7fc23c6 <+294>:   vzeroupper
   0x00007ffff7fc23c9 <+297>:   ret
End of assembler dump.

The register rbx seems to have a good address up till the call r14 which calls this method program::program::try_into_bytes[expr29](f0) which is like an internal method made by the compiler to handle the for loop.

I need to investigate more what goes on in there. But it leaves the rbx register with a bad address.

Edit: it seems a stack addr is loaded into r9 at some point, and the byte 0 assigned to where it points, which messes the stack when popping rbx.

@azteca1998 azteca1998 linked a pull request Apr 24, 2024 that will close this issue
5 tasks
@azteca1998
Copy link
Collaborator

azteca1998 commented Apr 24, 2024

Hello, I have some good news. I did some reverse engineering and found out some interesting stuff:

The program generates the following instructions (relevant only, the others have been omitted):

push   rbx                                         ; Store parent base register.
sub    rsp,0x68                                    ; Allocate 104 bytes of locals (3* 32  bytes + 8 bytes for stack alignment).
lea    r10,[rsp+0x48]                              ; r10 = rsp + 72 ???
cmove  r9,r10                                      ; r9 = r10 if `i == self.len()`
mov    DWORD PTR [r9+0x20],r8d                     ; *(r9 + 32) = r8d (i)

I could explain this in detail but I'll just get to the point: the last instruction is overwriting 4 bytes of rbx (stored in the first instruction) because it's taking 32 as the offset, therefore storing it behind where it's supposed to end.

To understand why this happened let's take a look at the array's internal representation:

Name Alignment Size
ptr 8 8
beg 4 4
end 4 4
cap 4 4

This gives us a struct of 28 bytes with an alignment of 8 bytes. Apparently this is wrong.

The correct answer is 32 bytes, because it includes 4 bytes of padding to align the structure's end to 8 (the structure's alignment). I'm not sure why this happens here because it's not an array. This is the same behavior that C exhibits, so it must be correct.

Up until now, the allocations didn't contain the extra padding. I've made a fix for that: now all generated layouts contain the padding to align them properly.

@greged93
Copy link
Collaborator Author

greged93 commented Apr 24, 2024

@azteca1998 that's amazing!
Just some questions:

This gives us a struct of 28 bytes with an alignment of 8 bytes.

But you don't have an alignment of 8 bytes without the padding right?

I'm not sure why this happens here because it's not an array.

Alignment requirements aren't only for arrays right? All structures are usually padded in order to be aligned with the native alignment of the host machine as far as I know?

@azteca1998
Copy link
Collaborator

But you don't have an alignment of 8 bytes without the padding right?

I thought only the struct's beginning were aligned, not also the end. In this case there's no need to have padding between the fields because the most restrictive value is upfront.

Alignment requirements aren't only for arrays right? All structures are usually padded in order to be aligned with the native alignment of the host machine as far as I know?

Alignment requirements are for all types afaik, but I didn't know we were supposed to align the struct size too. It doesn't really make sense in my head. The thing with arrays is that having the struct size being a multiple of the alignment makes it trivial to calculate offsets, but the compilers can compute that trivially; having it outside arrays seems like a waste of space to me, but I'm probably missing some crucial information about this decision.

@greged93
Copy link
Collaborator Author

having it outside arrays seems like a waste of space to me, but I'm probably missing some crucial information about this decision.

As far as I know, the reason why you would want to have the total struct size correctly aligned to the native alignment of the machine is for efficient memory reads/writes. Let's say we kept the 28 bytes alignment on the struct, and store a u64 right after. On 64-bits machines, you would need to perform two reads (read at address 0x3 and 0x4 assuming addressing starts at 0x0) instead of one if aligned to 8 bytes.

@azteca1998
Copy link
Collaborator

Yes, but you only need the address where something starts to be aligned, not the address where something ends.

The problem was that we had an array ((ptr, u32, u32, u32)) followed by an u32 giving the following structure ((ptr, u32, u32, u32), u32). We had it implemented so that there was no padding after the structures. The final u32 would fit where the padding of the first struct is and still be properly aligned:

Type Alignment  Size
ptr 8 8
u32  4 4
u32  4 4
u32  4  4
u32  4  4

The compiler, however, inserts the padding to the inner and outer structs as follows, wasting eight bytes of memory per instance:

Type Alignment  Size
ptr 8 8
u32  4 4
u32  4 4
u32  4  4
padding N/A  4
u32  4  4
padding N/A  4

As you can see both of them are properly aligned, that's why I don't understand the decision about padding the struct's end, but as I said before I'm probably missing some crucial information or edge case that would explain the padding.

@greged93
Copy link
Collaborator Author

Oh yeah that is indeed stange...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working integration
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

4 participants