-
Notifications
You must be signed in to change notification settings - Fork 18
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
Add dichotomic symbolic clz and ctz #195
base: main
Are you sure you want to change the base?
Conversation
src/interpret.ml
Outdated
end | ||
in | ||
let> cond = I32.(eqz n) in | ||
if cond then return @@ const 32l else aux 0 32 |
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.
Could you explain to me quickly why this can't be in symbolic_value
directly ?
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.
Because currently, code in symbolic_value has no reference to the Choice monad
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.
What about putting it in symbolic.ml
then ?
Replacing:
module Value = Symbolic_value
By:
module Value = struct
include Symbolic_value
let clz ...
end
?
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 can try but it is awkward as well because we would have to separate cases for I64 and I32 in Symbolic.value
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.
Oh yes I forgot about this. I still believe you can do it "properly":
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.
module I32 = struct
include Symbolic_value.I32
let clz = ...
end
module I64 = struct
... same ...
end
module Value = struct
include Symbolic_value
module I32 = I32
module I64 = I64
end
Do you have approximate numbers on how much faster is it ? |
This is super cool! I'd love to see the differences between this branching approach and using ite expressions inside EDIT: maybe I'll write one such implementation in encoding and see 😅 |
Well the previous implementation when run on tests gave
So it may have been faster but certainly less useful 😉
Could you clarify what you mean by "ite expressions" ? |
This is supposedly SMT friendly, and keeps the bit-hacks for the concrete case.
I also added ctz in a similar vein. |
I mean the ternary operator if-then-else. It would allow us to write the same code in a single expression. Here is the pseudo code for the let clz n =
let rec aux (lb : int) ub =
if ub = lb + 1 then I32.const_i32 (Int32.of_int (32 - ub))
else
let mid = (lb + ub) / 2 in
let two_pow_mid = Int32.shl 1l (Int32.of_int mid) in
let cond = I32.lt_u n (I32.const_i32 two_pow_mid) in
ite cond (aux lb mid) (aux mid ub)
ite (I32.eqz n) (I32.const_i32 32l) (aux 0 32) I did a quick and dirty implementation of this directly in Z3, which you can find here: formalsec/encoding@2f83d34 (hope you don't mind!) After that, I measured execution times and paths explored on
I referred to your approach as branching and the other as ite-exprs. It appears that the use of branching in the I also experimented with removing the second assert in the test, and the performance was much faster, essentially identical between the two approaches, with the same number of paths as well. Perhaps it's worth providing an implementation for these operators in encoding and see how they compare against this approach. What do you think? |
Hi @filipeom ! Very interesting thank you. A few points:
|
Yes, you are correct. However, I want to note that having easier-to-reason path conditions might not be as helpful when there are exponentially as many branches to check for satisfiability. Even though you can quickly reason about them in the SMT solver, it requires exponential memory usage to do so. Regardless, even if I prefer approaches that do not branch to encode such operations, I must admit that this approach might be better in cases where there is a good path exploration strategy.
I think it's because But in summary:
|
@filipeom, do you think this could be added more easily (i.e. without the optional reference to a function hack) with your current work on the memory? |
I think the issue remains. Even though we can now use |
This is supposedly SMT friendly, and keeps the
bit-hacks for the concrete case.