Optimization disables address forwarding prefix insertion

When compiling:

#include <inttypes.h>

void foo(void)
{
        *(uint16_t *) (0xb8000) = 0xf61;
}

      

from

gcc test.c -c -m16 -O1

      

I am getting the following warning:

/tmp/ccyziKm4.s: Assembler messages:
/tmp/ccyziKm4.s:9: Warning: 753664 shortened to 32768

      

And when I drop the switch -O1

, I don't get it, and gcc uses the prefix 0x67

to switch the address size as expected (it -m16

basically emits prefixed 32-bit code):

00000000 <foo>:
   0:   66 55                   push   %ebp
   2:   66 89 e5                mov    %esp,%ebp
   5:   66 b8 00 80 0b 00       mov    $0xb8000,%eax
   b:   67 c7 00 61 0f          movw   $0xf61,(%eax)
  10:   90                      nop
  11:   66 5d                   pop    %ebp
  13:   66 c3                   retl

      

So obviously it has to do with the optimization switch -O1

. The gcc man page describes all the options it sets, and I wrote a script to highlight each one and pass them to gcc, but it doesn't really work. Now gcc does not display any warning at all, even with all linkage.

I appreciate any suggestions on how to resolve this.

+3


source to share


1 answer


I would say this is a bug in gcc, but I can see some logic behind this behavior:

GCC without optimization produces pretty simple code with two instructions (I prefer the syntax of intelligence):

mov eax, 0xb8000 # move value 0xb8000 to eax
movw [eax], 0xf61 # move value 0xf61 to address stored in eax

      

Binary view:

66 b8 00 80 0b 00
   ^ operation: move 16 bit value to 16 register ax
^ size override prefix to indicate that 32 bit data is used instead of 16 bit, so eax should be used instead of ax

66 c7 00 61 0f
   ^ operation: move 16 bit value to 16 address in ax
^ size override prefix

      

Optimized GCC tries to optimize, so it generates the following code:

movw [0xb8000], 0xf61 # mov value 0xf61 directly to 32 bit address 0xb8000 without any intermediate registers

      

Binary view:

66 c7 05 00 80 0b 00 61 0f
   ^ operation: move 16 bit value to 16 bit address
^ size override prefix

      



So, 32-bit opcodes are actually the same opcodes as 16 bits, but prefixed with 66/67 .

And here's the problem:

  • The operation movw [REGISTER], 0xf61

    is official and officially supported in 16/32 modes.
  • Operation movw [0xb8000], 0xf61

    is legal, but values> 16 bits (0xffff) are not officially supported in 16 real mode, in 32 protected ones - they are officially supported

This is why the compiler issues a warning and truncates the value from 0xb8000 to 0x8000 to create a legal and officially supported statement.

Note. I believe gcc should also issue a warning in the first case, as it doesn't work the way you would expect in 16 bits:

  • In real mode, such an instruction is allowed, but eax

    cannot exceed 0xffff

    (effectively it does not use eax

    , but only a part ax

    ).
  • in protected / unreal mode such instruction is allowed and full will be used eax

    .

I don't know why gcc allows the flag to be used m16

without properly supporting the generation of 16-bit code and real mode memory models. I suggest you switch to something else. 20 years ago watcom was really cool.

If you are in unreal mode, this automatically means that you can and should use m32 instructions.

+1


source







All Articles