-
Notifications
You must be signed in to change notification settings - Fork 155
lemmas: booster-only gap fixes — widthOpCode, int/bytes simplifications, preserves-definedness #2849
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
base: master
Are you sure you want to change the base?
lemmas: booster-only gap fixes — widthOpCode, int/bytes simplifications, preserves-definedness #2849
Changes from all commits
983360b
1e96bf5
fa9903d
e849dee
3d39038
2bc3628
8e72989
140cdc8
90dd3b2
36bf920
c59dfaa
ecafb04
677cf26
8c3de92
d9cc35a
a087d2a
dac2ce6
db711c4
17cd083
7bc52dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1750,10 +1750,10 @@ The various `CALL*` (and other inter-contract control flow) operations will be d | |
| syntax Bytes ::= #computeValidJumpDests(Bytes) [symbol(computeValidJumpDests), function, memo, total] | ||
| | #computeValidJumpDests(Bytes, Int, Bytes, Int) [symbol(computeValidJumpDestsAux), function ] | ||
| // ------------------------------------------------------------------------------------------------------------------------- | ||
| rule #computeValidJumpDests(PGM) => #computeValidJumpDests(PGM, 0, padRightBytes(.Bytes, lengthBytes(PGM), 0), lengthBytes(PGM)) | ||
| rule #computeValidJumpDests( _, I, RESULT, LEN) => RESULT requires I >=Int LEN | ||
| rule #computeValidJumpDests(PGM, I, RESULT, LEN) => #computeValidJumpDests(PGM, I +Int 1, RESULT[I <- 1], LEN) requires I <Int LEN andBool PGM [ I ] ==Int 91 | ||
| rule #computeValidJumpDests(PGM, I, RESULT, LEN) => #computeValidJumpDests(PGM, I +Int #widthOpCode(PGM [ I ]), RESULT, LEN) requires I <Int LEN andBool notBool PGM [ I ] ==Int 91 | ||
| rule #computeValidJumpDests(PGM) => #computeValidJumpDests(PGM, 0, padRightBytes(.Bytes, lengthBytes(PGM), 0), lengthBytes(PGM)) [preserves-definedness] | ||
| rule #computeValidJumpDests( _, I, RESULT, LEN) => RESULT requires I >=Int LEN [preserves-definedness] | ||
| rule #computeValidJumpDests(PGM, I, RESULT, LEN) => #computeValidJumpDests(PGM, I +Int 1, RESULT[I <- 1], LEN) requires I <Int LEN andBool PGM [ I ] ==Int 91 [preserves-definedness] | ||
| rule #computeValidJumpDests(PGM, I, RESULT, LEN) => #computeValidJumpDests(PGM, I +Int #widthOpCode(PGM [ I ]), RESULT, LEN) requires I <Int LEN andBool notBool PGM [ I ] ==Int 91 [preserves-definedness] | ||
|
Comment on lines
+1754
to
+1756
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it true that these are preserving definedness?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess not, but it is pragmatically the only choice to avoid having |
||
| ``` | ||
|
|
||
| System Calls | ||
|
|
@@ -1812,7 +1812,7 @@ System Transaction Configuration | |
| ``` | ||
|
|
||
| ```k | ||
| syntax Int ::= #widthOpCode(Int) [symbol(#widthOpCode), function] | ||
| syntax Int ::= #widthOpCode(Int) [symbol(#widthOpCode), function, total, smtlib(widthOpCode)] | ||
| // ----------------------------------------------------------------- | ||
|
Comment on lines
+1815
to
1816
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. formatting |
||
| rule #widthOpCode(W) => W -Int 94 requires W >=Int 96 andBool W <=Int 127 | ||
| rule #widthOpCode(_) => 1 [owise] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,6 +29,10 @@ module BYTES-SIMPLIFICATION [symbolic] | |
|
|
||
| rule [bytes-concat-empty-right]: B:Bytes +Bytes .Bytes => B [simplification] | ||
| rule [bytes-concat-empty-left]: .Bytes +Bytes B:Bytes => B [simplification] | ||
| // b"" is a Kore domain value (\dv{Bytes}("")) distinct from the .Bytes constructor; | ||
| // LLVM hooks (e.g. #encodeArgs base case) return b"", not .Bytes, so we need both forms. | ||
| rule [bytes-concat-empty-right-concrete]: B:Bytes +Bytes b"" => B [simplification, preserves-definedness] | ||
| rule [bytes-concat-empty-left-concrete]: b"" +Bytes B:Bytes => B [simplification, preserves-definedness] | ||
|
|
||
| rule [bytes-concat-right-assoc-symb-l]: (B1:Bytes +Bytes B2:Bytes) +Bytes B3:Bytes => B1 +Bytes (B2 +Bytes B3) [symbolic(B1), simplification(40)] | ||
| rule [bytes-concat-right-assoc-symb-r]: (B1:Bytes +Bytes B2:Bytes) +Bytes B3:Bytes => B1 +Bytes (B2 +Bytes B3) [symbolic(B2), simplification(40)] | ||
|
|
@@ -267,8 +271,8 @@ module BYTES-SIMPLIFICATION [symbolic] | |
| rule [lengthBytes-geq-zero]: 0 <=Int lengthBytes ( _ ) => true [simplification, smt-lemma] | ||
| rule [lengthBytes-geq-nonPos]: X <=Int lengthBytes ( _ ) => true requires X <=Int 0 [simplification, concrete(X)] | ||
| rule [lengthBytes-concat]: lengthBytes(BUF1 +Bytes BUF2) => lengthBytes(BUF1) +Int lengthBytes(BUF2) [simplification] | ||
| rule [lengthBytes-buf]: lengthBytes(#buf(S, _)) => S requires 0 <=Int S [simplification] | ||
| rule [lengthBytes-range]: lengthBytes(#range(_, S:Int, W:Int)) => maxInt(0, W) requires 0 <=Int S [simplification] | ||
| rule [lengthBytes-buf]: lengthBytes(#buf(S, _)) => S requires 0 <=Int S [simplification, preserves-definedness] | ||
| rule [lengthBytes-range]: lengthBytes(#range(_, S:Int, W:Int)) => maxInt(0, W) requires 0 <=Int S [simplification, preserves-definedness] | ||
|
Comment on lines
+274
to
+275
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if these rules actually do preserve definedness.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| rule [lengthBytes-prtw]: lengthBytes(#padRightToWidth(W:Int, B:Bytes)) => maxInt(lengthBytes(B), W) [simplification] | ||
|
|
||
| rule [lengthBytes-leq-zero]: lengthBytes(B:Bytes) <=Int 0 => B ==K .Bytes [simplification] | ||
|
|
@@ -306,5 +310,5 @@ module BYTES-SIMPLIFICATION [symbolic] | |
| rule [lookup-as-asWord]: | ||
| B:Bytes [ I:Int ] => #asWord ( #range ( B, I, 1 ) ) | ||
| requires 0 <=Int I andBool I <Int lengthBytes(B) | ||
| [simplification(60)] | ||
| [simplification(60), preserves-definedness] | ||
| endmodule | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -101,6 +101,25 @@ module EVM-INT-SIMPLIFICATION | |
| requires 0 <=Int WB andBool 0 <=Int X andBool X <Int minInt ( 2 ^Int (8 *Int WB), pow256 ) | ||
| [simplification, concrete(WB), preserves-definedness] | ||
|
|
||
| // Specialisation for 32-byte buf with rangeUInt(256) side-condition: avoids | ||
| // the minInt(2^256, pow256) evaluation that Booster cannot match against the | ||
| // path condition's 2^Int 256 tree form. | ||
| rule [asWord-buf-inversion-rangeUInt256]: | ||
| #asWord ( #buf ( 32, X:Int ) ) => X | ||
| requires #rangeUInt ( 256, X ) | ||
| [simplification, preserves-definedness] | ||
|
|
||
| // General reduction: #asWord of a range extracted from a buf. | ||
| // #buf(N, X) is the big-endian N-byte encoding of X. Taking W bytes | ||
| // starting at byte S gives (X >> (8*(N-S-W))) & (2^(8*W)-1). | ||
| // Activated with concrete N, S, W so Booster can evaluate the arithmetic. | ||
| rule [asWord-range-buf]: | ||
| #asWord ( #range ( #buf ( N:Int, X:Int ), S:Int, W:Int ) ) => | ||
| X /Int ( 2 ^Int ( 8 *Int ( N -Int S -Int W ) ) ) modInt ( 2 ^Int ( 8 *Int W ) ) | ||
| requires 0 <=Int S andBool 0 <Int W andBool S +Int W <=Int N | ||
| andBool 0 <=Int X andBool X <Int 2 ^Int ( 8 *Int N ) | ||
| [simplification, concrete(N, S, W), preserves-definedness] | ||
|
Comment on lines
+112
to
+121
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This lemma seems unnecessarily complicated, we should track down which test triggered adding it, and see if we can find a smaller/simpler version that works. |
||
|
|
||
| // Reduction: bitwise right shift in terms of `#range` | ||
| rule [asWord-shr]: | ||
| #asWord( BA ) >>Int N => #asWord ( #range ( BA, 0, lengthBytes( BA ) -Int ( N /Int 8 ) ) ) | ||
|
|
@@ -137,6 +156,30 @@ module EVM-INT-SIMPLIFICATION | |
| andBool lengthBytes ( BA1 +Bytes BA2 ) <=Int 32 | ||
| [simplification, concrete(BA2, X), preserves-definedness] | ||
|
|
||
| // Masking `#asWord(BA)` with a byte-aligned maxUIntN mask (= 2^N-1) gives | ||
| // the lower N/8 bytes as a new `#asWord`. Fires before `multimask-b-and` | ||
| // (priority 50) to avoid an intermediate form that Booster cannot close. | ||
| rule [bit-mask-asWord-m]: | ||
| MASK:Int &Int #asWord(BA:Bytes) => | ||
| #asWord(#range(BA, 32 -Int (log2Int(MASK +Int 1) /Int 8), log2Int(MASK +Int 1) /Int 8)) | ||
| requires 0 <Int MASK | ||
| andBool MASK +Int 1 ==Int 2 ^Int log2Int(MASK +Int 1) | ||
| andBool log2Int(MASK +Int 1) modInt 8 ==Int 0 | ||
| andBool lengthBytes(BA) ==Int 32 | ||
| [simplification(40), concrete(MASK), preserves-definedness] | ||
|
|
||
| // Masking `#asWord(BA)` with a byte-aligned notMaxUIntN mask (complement of | ||
| // 2^N-1 within 256 bits) zeroes the lower N/8 bytes and keeps the rest. | ||
| rule [bit-notmask-asWord-nm]: | ||
| MASK:Int &Int #asWord(BA:Bytes) => | ||
| #asWord(#range(BA, 0, 32 -Int (log2Int(pow256 -Int MASK) /Int 8)) +Bytes #buf(log2Int(pow256 -Int MASK) /Int 8, 0)) | ||
| requires 0 <=Int MASK | ||
| andBool MASK <Int pow256 | ||
| andBool pow256 -Int MASK ==Int 2 ^Int log2Int(pow256 -Int MASK) | ||
| andBool log2Int(pow256 -Int MASK) modInt 8 ==Int 0 | ||
| andBool lengthBytes(BA) ==Int 32 | ||
| [simplification(40), concrete(MASK), preserves-definedness] | ||
|
|
||
| // ########################################################################### | ||
| // chop | ||
| // ########################################################################### | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -86,6 +86,10 @@ module INT-SIMPLIFICATION-HASKELL [symbolic] | |
| rule A +Int B >Int C +Int D => A -Int C >Int D -Int B [concrete(B, D), symbolic(A, C), simplification(45)] | ||
| rule A +Int B >=Int C +Int D => A -Int C >=Int D -Int B [concrete(B, D), symbolic(A, C), simplification(45)] | ||
|
|
||
| rule [int-eq-comm-concrete]: | ||
| N:Int ==Int X:Int => X ==Int N | ||
| [simplification(30), concrete(N), symbolic(X)] | ||
|
|
||
| rule A +Int B ==Int A => B ==Int 0 [simplification(40)] | ||
| rule A +Int B ==Int B => A ==Int 0 [simplification(40)] | ||
| rule A +Int B ==Int C +Int B => A ==Int C [simplification(40)] | ||
|
|
@@ -101,6 +105,11 @@ module INT-SIMPLIFICATION-HASKELL [symbolic] | |
| rule A -Int B <Int 0 => A <Int B [simplification, symbolic(A, B)] | ||
| rule A -Int B <=Int 0 => A <=Int B [simplification, symbolic(A, B)] | ||
|
|
||
| // Mixed range contradictions: A < B /\ C <= A where B <= C (all concrete B, C) | ||
| // Requires is purely concrete (both B and C are concrete), so no circular self-application. | ||
| rule A:Int <Int B:Int andBool C:Int <=Int A:Int => false requires B <=Int C [simplification, concrete(B, C)] | ||
| rule C:Int <=Int A:Int andBool A:Int <Int B:Int => false requires B <=Int C [simplification, concrete(B, C)] | ||
|
|
||
| endmodule | ||
|
|
||
| module INT-SIMPLIFICATION-COMMON | ||
|
|
@@ -133,6 +142,7 @@ module INT-SIMPLIFICATION-COMMON | |
| rule (A +Int B) +Int (C -Int B) => A +Int C [simplification] | ||
| rule (A -Int B) -Int (C -Int B) => A -Int C [simplification] | ||
| rule ((A -Int B) -Int C) +Int B => A -Int C [simplification] | ||
| rule A +Int ((B -Int A) +Int C) => B +Int C [simplification] | ||
|
|
||
| // 5 terms | ||
| // NOTE: required for `tests/specs/functional/infinite-gas-spec.k.prove` (haskell) | ||
|
|
@@ -232,6 +242,35 @@ module INT-SIMPLIFICATION-COMMON | |
| rule [maxInt-factor-left]: maxInt ( A:Int +Int B:Int, A:Int +Int C:Int ) => A +Int maxInt ( B, C ) [simplification] | ||
| rule [maxInt-factor-right]: maxInt ( A:Int +Int B:Int, C:Int +Int B:Int ) => maxInt ( A, C ) +Int B [simplification] | ||
|
|
||
| // minInt <Int maxInt / minInt <=Int maxInt: priority 40 fires before the default-priority (50) expansion rules, | ||
| // so Booster sees `true` directly rather than a disjunction requiring path-condition reasoning. | ||
| rule [minint-lt-maxint-a]: | ||
| minInt(A:Int, _B:Int) <Int maxInt(C:Int, _D:Int) => true | ||
| requires A <Int C | ||
| [simplification(40), preserves-definedness] | ||
| rule [minint-lt-maxint-shift-a]: | ||
| minInt(E:Int +Int A:Int, E:Int +Int _B:Int) <Int maxInt(F:Int +Int C:Int, F:Int +Int _D:Int) => true | ||
| requires A <Int C andBool E <=Int F | ||
| [simplification(40), preserves-definedness] | ||
| rule [minint-leq-maxint-b]: | ||
| minInt(_A:Int, B:Int) <=Int maxInt(_C:Int, D:Int) => true | ||
| requires B <=Int D | ||
| [simplification(40), preserves-definedness] | ||
| rule [minint-leq-maxint-shift-b]: | ||
| minInt(_A:Int +Int E:Int, B:Int +Int E:Int) <=Int maxInt(_C:Int +Int F:Int, D:Int +Int F:Int) => true | ||
| requires B <=Int D andBool E <=Int F | ||
| [simplification(40), preserves-definedness] | ||
| // Factored forms: after minInt-factor-{left,right} + maxInt-factor-{left,right} (priority 50) have fired, | ||
| // the non-linear shift rules above no longer match; these linear rules catch the result. | ||
| rule [minint-lt-maxint-shift-a-factored]: | ||
| E:Int +Int minInt(A:Int, _B:Int) <Int F:Int +Int maxInt(C:Int, _D:Int) => true | ||
| requires A <Int C andBool E <=Int F | ||
| [simplification(40), preserves-definedness] | ||
| rule [minint-leq-maxint-shift-b-factored]: | ||
| minInt(_A:Int, B:Int) +Int E:Int <=Int maxInt(_C:Int, D:Int) +Int F:Int => true | ||
| requires B <=Int D andBool E <=Int F | ||
| [simplification(40), preserves-definedness] | ||
|
|
||
|
Comment on lines
+245
to
+273
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of these seem like they can be simplified with some other reasoning steps in the way. For eaxmple some of the rules could be combined with a disjunction in the requires clause, and others can first do arithmetic simplification on the expression then rely on the other rules here to fire afterwards. |
||
| // ########################################################################### | ||
| // inequality | ||
| // ########################################################################### | ||
|
|
@@ -241,6 +280,14 @@ module INT-SIMPLIFICATION-COMMON | |
|
|
||
| rule A <Int A -Int B => false requires 0 <=Int B [simplification] | ||
|
|
||
| rule A:Int <=Int A:Int => true [simplification] | ||
|
|
||
| // Higher-priority: short-circuit to true directly when path condition already has 0 <=Int B. | ||
| rule A:Int <=Int A:Int +Int B:Int => true requires 0 <=Int B [simplification(40)] | ||
| rule A:Int <=Int A:Int +Int B:Int => 0 <=Int B [simplification] | ||
|
|
||
| rule A:Int <Int B:Int andBool B:Int <Int A:Int => false [simplification] | ||
|
|
||
| rule 0 <Int 1 <<Int A => true requires 0 <=Int A [simplification, preserves-definedness] | ||
|
|
||
| // inequality sign normalization | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is already marked as total.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this rule should still be marked
preserves-definedness... the RHS#computeValidJumpDests(_, _, _, _)is not total but should be defined for these arguments.