Skip to content

Separating symbolic and concrete floats#1024

Open
dkcumming wants to merge 4 commits intomasterfrom
dc/concrete-floats
Open

Separating symbolic and concrete floats#1024
dkcumming wants to merge 4 commits intomasterfrom
dc/concrete-floats

Conversation

@dkcumming
Copy link
Copy Markdown
Collaborator

This PR:

  • Separates concrete and symbolic floats by adding .concrete or .symbolic to the md specifiers of the k source;
  • Allows for full decoding and evaluation of concrete floats when called by either LLVM backend directly, or invoked by booster backend calling evaluate on the concrete float term;
  • Prohibits decoding of symbolic floats with #UnableToDecode produced which will thunk any operation containing it, therefore not evaluating it and avoiding crashing the haskell backend which does not have hooks for float functions;
  • Moved float tests to exec-smir and separated LLVM and Haskell backend kmir run expected test files so both behaviours can be tested;
  • Maintains a symlink to the concrete kmir run test in prove-rs/ which calls kmir prove invoking the booster backend successfully evaluating the concrete floats;

One thing to note is that the expected files for the haskell backend KMIR run are rather large, this is because it branches on thunk. This could be avoided if kmir run could take --terminate-on-thunk as kmir prove does. Alternatively, and probably most sensible would be to remove thunk all together. Any value we could of gained from it is likely already achieved and for myself has been more a source of frustration than benefit for a while.

With this inclusion #985 can continue as it will have proper error case for symbolic floats

Concrete floats are supported and so the rules that decode them are
added in k blocks with `.concrete`, which will mean symbolic floats
will decode to `#UnableToDecode`. The casting rules were branching
non-deterministically when evaluating the args so I added
`#UnsupportedFloatTransmute` as a `MIRError`. I think I have caught
all the cases where floats could crash the haskell backend now or
create nd branching but I could be wrong.
With decoding for symbolic floats yielding `#UnableToDecode`, (nearly)
all float tests can be replaced for the haskell backend as they will not
be able to crash on the unsupported hooks. This requires two `.state`
files since we have to compare both successful LLVM and unsuccessful
Haskell handling of floats.

The casts are in a different category as there is `IntToFloat` which does
not start with an `#UnableToDecode` and so will reach an unsupported hook
and crash. In practice, I believe it is not possible to construct a
symbolic float, so these casts are the only danger for crashing the
backend.

Note: The .state.haskell files are quite large because `kmir run` does
not have the ability to `--terminate-on-thunk`. Probably well and truly
time to remove `thunk`.
@dkcumming dkcumming self-assigned this Apr 8, 2026
requires 0 <=Int I andBool I <Int size(LOCALS)
andBool isTypedLocal(LOCALS[I])
andBool #isFloatType(lookupTy({getTyOf(tyOfLocal({LOCALS[I]}:>TypedLocal), PROJS)}:>Ty))
[priority(40), preserves-definedness]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The float transmute rules cover operandCopy and operandMove source types but not operandConstant. It seems that OperandConstant can reach this rule though.

# UnableToDecode default rule for float constants.
if symbolic:
haskell_output = output_kast.with_suffix('.state.haskell')
if haskell_output.is_file() or update_expected_output:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

It seems that the --update-expected-output path creates .state.haskell files for all tests unconditionally. For tests where haskell output is identical to LLVM, this creates files that

  1. are redundant
  2. can become stale if .state is later updated independently.
    I don't mind the redundancy that much, but having stale files can easily lead to mistakes, so I would like a solution that creates the .state.haskell files only if needed.

Suggestion: generate fresh .state (LLVM) first, then generate .state.haskell and only keep it when it differs.

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.

2 participants