Skip to content

Commit

Permalink
x64 backend: fix fpcmp to avoid load-op merging. (#3934) (#4012)
Browse files Browse the repository at this point in the history
The `fpcmp` helper in the x64 backend uses `put_in_xmm_mem` for one of
its operands, which allows the compiler to merge a load with the compare
instruction (`ucomiss` or `ucomisd`).

Unfortunately, as we saw in #2576 for the integer-compare case, this
does not work with our lowering algorithm because compares can be
lowered more than once (unlike all other instructions) to reproduce the
flags where needed. Merging a load into an op that executes more than
once is invalid in general (the two loads may observe different values,
which violates the original program semantics because there was only one
load originally).

This does not result in a miscompilation, but instead will cause a panic
at regalloc time because the register that should have been defined by
the separate load is never written (the load is never emitted
separately).

I think this (very subtle, easy to miss) condition was unfortunately not
ported over when we moved the logic in #3682.

The existing fcmp-of-load test in `cmp-mem-bug` (from #2576) does not
seem to trigger it, for a reason I haven't fully deduced. I just added
the verbatim function body (happens to come from `clang.wasm`) that
triggers the bug as a test.

Discovered while bringing up regalloc2 support. It's pretty unlikely to
hit by chance, which is why I think none of our fuzzing has hit it yet.
  • Loading branch information
cfallin authored Apr 11, 2022
1 parent 59bfe50 commit c740e5d
Show file tree
Hide file tree
Showing 4 changed files with 950 additions and 172 deletions.
6 changes: 4 additions & 2 deletions cranelift/codegen/src/isa/x64/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -1528,9 +1528,11 @@
;; `clif.isle`).
(decl fpcmp (Value Value) ProducesFlags)
(rule (fpcmp src1 @ (value_type $F32) src2)
(xmm_cmp_rm_r (SseOpcode.Ucomiss) (put_in_xmm_mem src1) (put_in_xmm src2)))
;; N.B.: cmp can be generated more than once, so cannot do a
;; load-op merge. So `put_in_xmm` for src1, not `put_in_xmm_mem`.
(xmm_cmp_rm_r (SseOpcode.Ucomiss) (put_in_xmm src1) (put_in_xmm src2)))
(rule (fpcmp src1 @ (value_type $F64) src2)
(xmm_cmp_rm_r (SseOpcode.Ucomisd) (put_in_xmm_mem src1) (put_in_xmm src2)))
(xmm_cmp_rm_r (SseOpcode.Ucomisd) (put_in_xmm src1) (put_in_xmm src2)))

;; Helper for creating `test` instructions.
(decl test (OperandSize GprMemImm Gpr) ProducesFlags)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
src/clif.isle 9ea75a6f790b5c03
src/prelude.isle b2bc986bcbbbb77
src/isa/x64/inst.isle 40f495d3ca5ae547
src/isa/x64/inst.isle cdd292107fb36cf
src/isa/x64/lower.isle c049f7d36db0e0fb
Loading

0 comments on commit c740e5d

Please sign in to comment.