https://sourceware.org/bugzilla/show_bug.cgi?id=34981

            Bug ID: 34981
           Summary: fmovex bug in m68k gas
           Product: binutils
           Version: 2.47
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: gas
          Assignee: unassigned at sourceware dot org
          Reporter: edercarneiru at gmail dot com
  Target Milestone: ---

[ AI found this bug, not me ]

Title
-----
m68k gas: floating immediates for .x instructions are encoded wrongly
(no 16-bit gap, hidden integer bit); inf/NaN also emit uninitialised memory


Version and host
----------------
GNU assembler (GNU Binutils; openSUSE Tumbleweed) 2.47.0.20260921-2
Package cross-m68k-binutils-2.47-2.1.x86_64
Host x86_64-linux, target m68k-suse-linux

The same results were obtained earlier with 2.45.0.20251103-4. Git master has
not been tested.


Summary
-------
The 68881 extended format is 12 bytes: sign and exponent (16 bits), 16 zero
bits, then a 64-bit mantissa with an explicit integer bit.

For a decimal immediate on an extended-precision instruction, gas emits a
different layout: no 16-bit gap, and no explicit integer bit. Every non-zero
finite value is therefore wrong.

For an infinity or NaN immediate, gas writes a double-precision pattern into
the first 8 bytes and leaves the last 4 bytes unwritten. Those bytes come from
uninitialised stack memory, so the output can change from run to run.

The .extend directive produces correct values in all these cases.


How to reproduce
----------------
finite.s:

        .text
        fmovex  #0e1.5,%fp0
        .extend 1.5
        fmovex  #0e1.0,%fp0
        .extend 1.0
        fmovex  #0e-2.0,%fp0
        .extend -2.0
        fmovex  #0e0.1,%fp0
        .extend 0.1

inf.s:

        .text
        fmovex  #0einf,%fp0

Commands (run inf.s several times):

        m68k-suse-linux-as -m68040 -o finite.o finite.s
        m68k-suse-linux-objdump -s -j .text finite.o

        m68k-suse-linux-as -m68040 -o inf.o inf.s
        m68k-suse-linux-objdump -s -j .text inf.o


Actual and expected results
---------------------------
The first word of each instruction, f23c4800, is correct and omitted here.

 value   emitted for the immediate       .extend (correct)
 1.5     3fff8000 00000000 00000000      3fff0000 c0000000 00000000
 1.0     3fff0000 00000000 00000000      3fff0000 80000000 00000000
 -2.0    c0000000 00000000 00000000      c0000000 80000000 00000000
 0.1     3ffb9999 99999999 9999999a      3ffb0000 cccccccc cccccccd
 inf     7ff00000 00000000 ????????      7fff0000 00000000 00000000
 -inf    fff00000 00000000 ????????      ffff0000 00000000 00000000
 nan     7fffffff ffffffff ????????      7fff0000 ffffffff ffffffff

When the inf or NaN is the only immediate in the file, the last word changes
on every run. Three runs each gave:

 inf     f7e832c3  f7e879a3  f7e80963
 -inf    f7e88063  f7e814c3  f7e85dc3
 nan     f7e8cb83  f7e88b03  f7e82683

When a finite immediate comes earlier in the file, the last word is left over
from it. After fmovex #0e0.1, the inf, -inf and nan immediates all end in
9999999a, the last word of the 0.1 immediate.


Cause
-----
In m68k_ip (gas/config/tc-m68k.c), an 'x' operand sets baseo = 6, outro = 15,
and then calls:

        gen_to_words (words, baseo, (long) outro);

So gen_to_words is called with precision 6, while atof-ieee.c defines
X_PRECISION as 5 and adds the m68k gap itself. Because 6 != X_PRECISION:

  - the code for the 16-bit gap and the explicit integer bit is skipped, which
    gives the wrong finite values;
  - the inf and NaN cases take their final "else" branch, for double
    precision, which writes only words[0..3]. words is a local array in
    m68k_ip that is not initialised, and all 6 words are then emitted.

A possible fix is to pass X_PRECISION to gen_to_words for 'x' while still
emitting 6 words. This has not been built or tested. The 'p' case also uses
baseo = 6 and was not examined.


Valgrind
--------
A file holding only "fmovex #0einf,%fp0", assembled under
valgrind --track-origins=yes, reports:

 Syscall param write(buf) points to uninitialised byte(s)
    ...
 Uninitialised value was created by a stack allocation


Other observations
------------------
The inf and finite results are the same with -m68020, -m68030 -m68881, and
-m68060. The inf result is the same with fcmpx, faddx, and Motorola-style
fmove.x.

The shorter formats are correct:

 fmoves #0e-inf  f23c4400 ff800000
 fmoved #0e-inf  f23c5400 fff00000 00000000


Workaround
----------
Give the 12 bytes in hexadecimal:

        fmovex  #0x3fff0000c000000000000000,%fp0

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to