aboutsummaryrefslogtreecommitdiffhomepage
path: root/amd64/isel.c
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2025-12-07 12:29:27 +0100
committerlemon <lsof@mailbox.org>2025-12-07 12:29:27 +0100
commit4d4e61e82cd693d2800bf409c8e3dde1ac2b75a5 (patch)
treea2a531dd53648dd3037fb02f0f2d1c5e6fa21113 /amd64/isel.c
parentf7d68175fc2b52230b8f78bf78145c7f1d0ad9c5 (diff)
amd64: use XORPS for floating point negation
Previously `neg x` was being turned into `sub 0, x`. But this gives the wrong result for zero/negative zero (-0.0 == -0.0 but 0.0 - 0.0 == 0.0), so it wasn't IEEE compliant or correct. Do what every other compiler does instead and flip the sign bit with an exclusive or. Should implement someway of deduplicating small data constants like the ones used here though.
Diffstat (limited to 'amd64/isel.c')
-rw-r--r--amd64/isel.c9
1 files changed, 6 insertions, 3 deletions
diff --git a/amd64/isel.c b/amd64/isel.c
index 64cda27..be2f769 100644
--- a/amd64/isel.c
+++ b/amd64/isel.c
@@ -494,9 +494,12 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
goto ALU;
case Oneg:
if (kisflt(ins->cls)) {
- ins->op = Osub;
- ins->r = ins->l;
- ins->l = ZEROREF;
+ /* flip sign bit with XORPS/D */
+ static const uvlong sd[2] = {0x8000000000000000,0x8000000000000000};
+ static const uint sf[4] = {0x80000000,80000000,0x80000000,80000000};
+ ins->op = Oxor;
+ ins->r = mkdatref(NULL, mktype(ins->cls == KF32 ? TYFLOAT : TYDOUBLE), /*siz*/16,
+ /*align*/16, ins->cls == KF32 ? (void *)sf : sd, /*siz*/16, /*deref*/1);
}
/* fallthru */
case Onot: