3

I am porting Quake 2's inline Win32 assembly to GAS. I started out by taking the inline assembly and then placing it into it's own ASM file. Fixed any issues, then started porting to GAS. I do know that the src/dst is reversed in AT&T vs Intel syntax (including floating point registers for some math operations) and a few other small gotchas, but when I got this compiling fine I noticed that the code was not working as intended. I looked over it carefully for hours with a compare utility and reading assembly manuals and finally tried "objdump -dwrC > out.txt" on the GAS and MASM versions. What I noticed was strange.

All of the code looked to be generated the same except in the MASM version there is an "fsubp st(1), st(0)" line and in GAS it was "fsubrp st(1), st(0)". This is strange because the actually assembly code in MASM and GAS both used fsubrp. Changing it to fsubp allowed the particle drawing code to work as intended.

Why is this happening?

Steps taken: Verifying src/dst line by line with compare utilities, reading x87 assembly manuals including tutorials and GCC ASM manual for any quirks.

Code snippet: Original MASM code:

fsubrp st(1), st(0)

GAS code:

fsubrp %st(0), %st(1)

MASM code according to objdump:

fsubp %st(0), st(1)

GAS code according to objdump:

fsubrp %st(0), st(1)

Expected results: I expected subrp to be generated in the objdump of the MASM file not fsubp.

Maraakate
  • 81
  • 4

2 Answers2

3

This is a known bug with gas which unfortunately cannot be fixed; reversed vs. non-reversed mnemonics for x87 non-commutative floating point instructions with register operands (like fdiv vs. fdivr) is the wrong way round. As compilers expect the order to be this way, this bug is here to stay.

See the AT&T Syntax bugs section of the GAS manual; this syntax design bug originated with AT&T's UnixWare assembler, and GAS chose to copy that syntax for compatibility.

It also affects objdump in AT&T mode.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
fuz
  • 88,405
  • 25
  • 200
  • 352
  • I know about that, but the operands are already swapped (should have clarified this, sorry). It's this instruction is becoming fsubp in MASM when it was declared fsubrp in the ASM file. I had to change it to fsubp in GAS for the code to work. – Maraakate May 19 '19 at 17:28
  • @Maraakate I don't mean the usual swapping in AT&T syntax. What I mean is that some instructions do not have their operands swapped that should have them swapped. That's the bug. – fuz May 19 '19 at 17:33
  • I am still new to assembly, that's why I'm starting off with this small project of just porting a function from one to the other... But, yes I have seen that GCC specifically states for compatibility reasons some FP math need to have their operands swapped. I believe FSUB/FSUBP/FSUBRP is one of them. If I compiled it the other way around the compiler warns that it's going to swap it so %st(0) comes first. The specific problem is that FSUBRP is what is in the assembly code, but objdump reports it as FSUBP and I had to change it to FSUBP in my GAS version of the code for it to work. – Maraakate May 19 '19 at 17:35
  • I have edited the question because I think I am not communicating properly what the issue is. Hope this clarifies things for you. – Maraakate May 19 '19 at 17:42
  • @Maraakate I think just `fsubp %st(0), %st(1)` is what you want. Note that `fsubrp` is an entirely different instruction from `fsubp`; it's not merely the operand-swapped variant. – fuz May 19 '19 at 17:46
  • The fsubp %st(0), %st(1) is what was required for the code to work however, the MASM code used fsubrp. When I run objdump on the MASM obj file it changes to fsubp. Why? – Maraakate May 19 '19 at 17:49
  • @Maraakate I have no idea. Perhaps that's one of the design bugs in gas. – fuz May 19 '19 at 18:01
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/193597/discussion-between-maraakate-and-fuz). – Maraakate May 19 '19 at 19:15
  • 1
    @Maraakate and Fuz: This isn't a GAS "bug", it's an AT&T syntax design bug. GAS and objdump are both compatible with each other, and agree that `fsubp %st(1)` is `de e1`, unlike the Intel documentation. You might want to use GAS's `.intel_syntax noprefix` / `objdump -Mintel` because that syntax is already MASM-like, and it does *not* suffer from the AT&T syntax bug. (I thought I remembered at some point in the past seeing objdump or gas apply the AT&T interpretation of mnemonics when in Intel-syntax mode, though, so check your version of tools.) – Peter Cordes May 19 '19 at 21:38
  • 1
    @Maraakate: NASM is a useful sanity check here; you can assemble `fsubp st1` with NASM and see that it's `de e9`, which we know is correct. The fact that `objdump -drwC -Mintel` disassembles that as `de e9 fsubp st(1),st` tells us that 2.31.1 at least agrees with Intel, not AT&T, when in Intel-syntax mode. – Peter Cordes May 19 '19 at 21:42
  • Hi Peter, I have ran objdump -drwC -Mintel on the MASM and GAS. So it looks like they put the fsubrp before the fsubp even though in the source files fsubp is first. Opcodes are proper when I swap it manually in the GAS file, though the video output is still incorrect but maybe another issue. – Maraakate May 20 '19 at 02:14
1

I figured this out. Thanks to Peter Cordes, I did run with -Mintel on MASM and GAS versions. The opcodes were wrong like he stated. For some reason with GAS if you have and fsubp followed by and fsubrp you have to reverse that. Once this was done, it was generating the right opcodes. Not sure if this an ultra-obscure issue.

Maraakate
  • 81
  • 4