Skip to content

Fix unsimplified bytes2int o int2bytes patterns. #137

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

Closed
wants to merge 3 commits into from

Conversation

Stevengre
Copy link
Contributor

No description provided.

@Stevengre Stevengre marked this pull request as draft June 13, 2025 15:28
@Stevengre Stevengre requested a review from tothtamas28 June 13, 2025 15:28
@Stevengre Stevengre removed the request for review from tothtamas28 June 13, 2025 15:28
@Stevengre Stevengre self-assigned this Jun 13, 2025
<pc> 0 </pc>
<regs>
// 25 |-> (Bytes2Int(Int2Bytes(1, (Bytes2Int(substrBytes(reverseBytes(W3), 8, 12), LE, Unsigned) +Int Bytes2Int(substrBytes(reverseBytes(W0), 8, 12), LE, Unsigned) +Int Bool2Word(4294967295 <Int Bytes2Int(substrBytes(reverseBytes(W3), 4, 8), LE, Unsigned) +Int Bytes2Int(substrBytes(reverseBytes(W0), 4, 8), LE, Unsigned) +Int Bool2Word(4294967295 <Int Bytes2Int(substrBytes(reverseBytes(W3), 0, 4), LE, Unsigned) +Int Bytes2Int(substrBytes(reverseBytes(W0), 0, 4), LE, Unsigned))) &Int 4294967295) >>Int 8, LE), LE, Unsigned) => ( Bytes2Int ( substrBytes ( reverseBytes ( W3:Bytes ) , 8 , 12 ) , LE , Unsigned ) +Int Bytes2Int ( substrBytes ( reverseBytes ( W0:Bytes ) , 8 , 12 ) , LE , Unsigned ) +Int Bool2Word ( 4294967295 <Int Bytes2Int ( substrBytes ( reverseBytes ( W3:Bytes ) , 4 , 8 ) , LE , Unsigned ) +Int Bytes2Int ( substrBytes ( reverseBytes ( W0:Bytes ) , 4 , 8 ) , LE , Unsigned ) +Int Bool2Word ( 4294967295 <Int Bytes2Int ( substrBytes ( reverseBytes ( W3:Bytes ) , 0 , 4 ) , LE , Unsigned ) +Int Bytes2Int ( substrBytes ( reverseBytes ( W0:Bytes ) , 0 , 4 ) , LE , Unsigned ) ) ) &Int 4294967295 ) >>Int 8 &Int 255)
26 |-> (Bytes2Int(Int2Bytes(1, (X +Int Y +Int Z &Int 4294967295) >>Int 8, LE), LE, Unsigned) => (X +Int Y +Int Z &Int 4294967295 ) >>Int 8 &Int 255)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC the issue is that this rewrite does not work, and the simplification sequence that should take place is something like:

1. Bytes2Int(Int2Bytes(1, (X +Int Y +Int Z &Int 4294967295) >>Int 8, LE), LE, Unsigned)

0 <=Int 1 ✅
0 <=Int (X +Int Y +Int Z &Int 4294967295) >>Int 8 ✅
by bytes2int-int2bytes:

2. ((X +Int Y +Int Z &Int 4294967295) >>Int 8) &Int (2 ^Int (1 *Int 8) -Int 1)

by constant propagation:

3. (X +Int Y +Int Z &Int 4294967295) >>Int 8) &Int 255

If that's the case, then the problematic part indeed seems to be showing 0 <=Int (X +Int Y +Int Z &Int 4294967295) >>Int 8. Consider adding a dedicated functional test for this.

CC @jberthold

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If that's the case, then the problematic part indeed seems to be showing 0 <=Int (X +Int Y +Int Z &Int 4294967295) >>Int 8. Consider adding a dedicated functional test for this.

Sorry, but I don't quite understand what does you mean about adding a dedicated functional test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the simplified pattern for this test (Node 3):

Node 3:

<riscv>
    <instrs>
      #HALT ~> .K
    </instrs>
    <regs>
      26 |-> ( X:Int >>Int 8 ) +Int ( Y:Int >>Int 8 ) +Int ( Z:Int >>Int 8 ) &Int 255
      27 |-> Bytes2Int ( Int2Bytes ( 1 , ( Bytes2Int ( substrBytes ( W3:Bytes , 8 , 12 ) , LE , Unsigned ) +Int Y:Int +Int Z:Int &Int 4294967295 ) >>Int 8 , LE ) , LE , Unsigned )
      28 |-> 1
    </regs>

As you can see, 26 is simplified successfully but 27 keeps unchanged.

Copy link
Contributor Author

@Stevengre Stevengre Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The failed rewriting is still unchanged after providing the following condition:

andBool 0 <=Int Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned)

and I cannot find this condition in node 3.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the Y and Z, are they constrained in any way?
The rule https://github.com/runtimeverification/riscv-semantics/pull/137/files#diff-686f3abe669ed3a3f63669576b1d85e3f5932c17acaec672cf1a98d637e69bbdR164 requires 0 <= V but that's not immediately clear if for example Y < 0
Well... on the other hand &Int 2 ^Int 32 will indeed make anything positive AFAICT but the related simplification requires andBool: 0 <=Int A &Int B => true requires 0 <=Int A _andBool_ 0 <=Int B

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 andBool 0 <=Int X andBool 0 <=Int Y andBool 0 <=Int Z
 andBool 0 <=Int Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed to this rule [int-and-ineq]: 0 <=Int _ &Int B => true requires 0 <=Int B [simplification], but still doesn't work. Should I make it specific to 4294967295?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I don't quite understand what does you mean about adding a dedicated functional test for this?

If you suspect that a simplification rule does not apply due to a precondition, you can extract a test for the precodition itself. For example:

claim <k> runLemma( 0 <=Int (X +Int Y +Int Z &Int 4294967295) >>Int 8 ) =>
          doneLemma( true ) </k>
  requires 0 <=Int X
   andBool 0 <=Int Y
   andBool 0 <=Int Z

This is how it's done in evm-semantics: https://github.com/runtimeverification/evm-semantics/blob/62c2eaaa73827895362b2f249a139b6d7a65302b/tests/specs/functional/lemmas-spec.k

Copy link
Contributor Author

@Stevengre Stevengre Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rewrite works

31 |-> (Bytes2Int(Int2Bytes(1, (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295), LE), LE, Unsigned) => (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 255 ))

does it mean that the problem is caused by >>Int 8?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! This is what I've tested for the precondition:

28 |-> (Bool2Word(0 <=Int Bytes2Int(substrBytes(reverseBytes(W3), 8, 12), LE, Unsigned)) => 1)

Therefore, it looks the same for these two patterns:

Bytes2Int(Int2Bytes(1, (X +Int Y +Int Z &Int 4294967295) >>Int 8, LE), LE, Unsigned)
Bytes2Int(Int2Bytes(1, (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295) >>Int 8, LE), LE, Unsigned)

@@ -41,8 +49,9 @@ module INT-SIMPLIFICATIONS [symbolic]

```k
rule [int-and-ineq]: 0 <=Int A &Int B => true requires 0 <=Int A andBool 0 <=Int B [simplification]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably we could say 0 <=Int A &Int B => true requires 0 <=Int B here?
K uses 2-s complement in infinite precision so if A is negative it starts with an "infinite" sequence of ...11111... that would get removed if B is not also negative.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! Let me try this. Additionally, do we need to add preserve-definedness attribute to this simp rule?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated this simp rule and k version. still doesnt' work.

<regs>
// 25 |-> (Bytes2Int(Int2Bytes(1, (Bytes2Int(substrBytes(reverseBytes(W3), 8, 12), LE, Unsigned) +Int Bytes2Int(substrBytes(reverseBytes(W0), 8, 12), LE, Unsigned) +Int Bool2Word(4294967295 <Int Bytes2Int(substrBytes(reverseBytes(W3), 4, 8), LE, Unsigned) +Int Bytes2Int(substrBytes(reverseBytes(W0), 4, 8), LE, Unsigned) +Int Bool2Word(4294967295 <Int Bytes2Int(substrBytes(reverseBytes(W3), 0, 4), LE, Unsigned) +Int Bytes2Int(substrBytes(reverseBytes(W0), 0, 4), LE, Unsigned))) &Int 4294967295) >>Int 8, LE), LE, Unsigned) => ( Bytes2Int ( substrBytes ( reverseBytes ( W3:Bytes ) , 8 , 12 ) , LE , Unsigned ) +Int Bytes2Int ( substrBytes ( reverseBytes ( W0:Bytes ) , 8 , 12 ) , LE , Unsigned ) +Int Bool2Word ( 4294967295 <Int Bytes2Int ( substrBytes ( reverseBytes ( W3:Bytes ) , 4 , 8 ) , LE , Unsigned ) +Int Bytes2Int ( substrBytes ( reverseBytes ( W0:Bytes ) , 4 , 8 ) , LE , Unsigned ) +Int Bool2Word ( 4294967295 <Int Bytes2Int ( substrBytes ( reverseBytes ( W3:Bytes ) , 0 , 4 ) , LE , Unsigned ) +Int Bytes2Int ( substrBytes ( reverseBytes ( W0:Bytes ) , 0 , 4 ) , LE , Unsigned ) ) ) &Int 4294967295 ) >>Int 8 &Int 255)
26 |-> (Bytes2Int(Int2Bytes(1, (X +Int Y +Int Z &Int 4294967295) >>Int 8, LE), LE, Unsigned) => (X +Int Y +Int Z &Int 4294967295 ) >>Int 8 &Int 255)
27 |-> (Bytes2Int(Int2Bytes(1, (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295) >>Int 8, LE), LE, Unsigned) => (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295 ) >>Int 8 &Int 255)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if the simplification rule is:

rule [bytes2int-int2bytes]:
  Bytes2Int(Int2Bytes(LEN, V, LE), LE, Unsigned) =>
  V &Int (2 ^Int (LEN *Int 8) -Int 1)
requires 0 <=Int LEN
 andBool 0 <=Int V [simplification(45), preserves-definedness]

and the LHS is:

Bytes2Int(Int2Bytes(1, (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295) >>Int 8, LE), LE, Unsigned)

then:

LEN |-> 1
V   |-> (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295) >>Int 8

and the preconditions are the following:

0 <=Int 1
0 <=Int (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295) >>Int 8

Out of the two preconditions, the first one is trivial. Do you have a test for the second preconditon?

Once you have that, and you've confirmed that it indeed fails, you can start decomposing the problem by abstracting subterms to figure out which one is problematic. For example, does

0<=Int X >>Int 8 => true
  requires 0 <=Int X

hold? If not, you have a new lemma, If so, let's continue with X. Does

0 <=Int Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295 => true
  requires 0 <=Int Y andBool 0 <=Int Z

hold? Etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Let me try!

Copy link
Contributor Author

@Stevengre Stevengre Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 |-> (Bool2Word(0 <=Int (Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295) >>Int 8) => 1) // fail
4 |-> (Bool2Word(0 <=Int X >>Int 8) => 1) // pass
5 |-> (Bool2Word(0 <=Int Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned) +Int Y +Int Z &Int 4294967295) => 1) // pass

Copy link
Contributor

@tothtamas28 tothtamas28 Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, this looks like the root cause. To simplify exposition, we basically have

(3) 0 <=Int complex-term >>Int 8 => true                     // fail
(4) 0 <=Int X            >>Int 8 => true requires 0 <=Int X  // pass
(5) 0 <=Int complex-term         => true                     // pass

The backend is obviously not clever enough to infer (3) from (4) and (5), it's not how it works. But @jberthold, what is the workaround in such a case? Is there a way to utilize theory reasoning by marking some of the simplifications smt-lemma-s?

Copy link
Contributor Author

@Stevengre Stevengre Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's confused for me that why (5) is passed and (30) is failed, because complex-term is already complex.

Another fact confuses me is that the (Bytes2Int(Int2Bytes(1, (X +Int Y +Int Z &Int 4294967295) >>Int 8, LE), LE, Unsigned) => (X +Int Y +Int Z &Int 4294967295 ) >>Int 8 &Int 255) is passed, but the more complex one doesn't. However, the main difference between them is just the fact that 0 <=Int Bytes2Int(substrBytes(W3, 8, 12), LE, Unsigned).

BTW, terms passed with condition 0 <=Int ( Bytes2Int ( substrBytes ( W3:Bytes , 8 , 12 ) , LE , Unsigned ) +Int Y:Int +Int Z:Int &Int 4294967295 ) >>Int 8

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add smt-lemma doesn't work.

Copy link
Contributor Author

@Stevengre Stevengre Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking that why must we have 0 <=Int V for Bytes2Int(Int2Bytes(LEN, V, LE), LE, Unsigned) => V &Int (2 ^Int (LEN *Int 8) -Int 1)? In fact the following is passed:

6 |-> (-1 &Int 255 => 255) // passed

Even if not correct, we can add more process for the RHS's V to make it align with the concrete execution result.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think it's safe to drop 0 <=Int V from the requires clause of bytes2int-int2bytes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add smt-lemma doesn't work.

Adding it to which simplification rule(s)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To all the <=Int rules.

…ith SMT lemma annotations. Update test cases in `xx.k` to reflect changes and add new assertions for `Bytes2Int` patterns.
Stevengre added a commit that referenced this pull request Jun 17, 2025
Solve the problem in #137.

---------

Co-authored-by: devops <devops@runtimeverification.com>
@Stevengre Stevengre closed this Jun 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants