diff options
| author | 2025-12-07 12:29:27 +0100 | |
|---|---|---|
| committer | 2025-12-07 12:29:27 +0100 | |
| commit | 4d4e61e82cd693d2800bf409c8e3dde1ac2b75a5 (patch) | |
| tree | a2a531dd53648dd3037fb02f0f2d1c5e6fa21113 /amd64/isel.c | |
| parent | f7d68175fc2b52230b8f78bf78145c7f1d0ad9c5 (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.c | 9 |
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: |