https://gcc.gnu.org/bugzilla/show_bug.cgi?id=124505
Bug ID: 124505
Summary: -m16 is inconsistent about 0x66 operand size prefix
for JMP/CALL instructions
Product: gcc
Version: 15.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: paul.emmerich at icloud dot com
Target Milestone: ---
Okay, this is a weird one and you might say it's not a bug and/or I'm holding
it wrong.
Consider this C code:
extern void foo(int);
void example(int n) {
for (int i = 0; i < n; ++i) {
foo(i);
}
}
> gcc-15 -m16 -c -O2 example.c
Yields these JMP instructions for the loop:
eb 0d jmp 30 <example+0x30>
75 e7 jne 30 <example+0x3
These don't get the 0x66 prefix whereas the CALL for the function call inside
the loop does get this prefix:
66 e8 fc ff ff ff calld 3c <example+0x3c>
But why would you even want the 0x66 prefix on a jmp immediate? It does control
whether the instruction pointer is 16 or 32 bit.
And that's relevant when using unreal mode
(https://en.wikipedia.org/wiki/Unreal_mode) to run code from segments larger
than 64 KiB, the jump wouldn't be able to cross these boundaries (or even
worse: be truncated on each jmp, not sure about real hardware, but all
implementations in DOSBox truncate on each jmp).
Arguably that's not something you support in the first place, so, well, I'm
holding it wrong.
But why does the CALL get the 0x66 prefix? Presumably because the target could
be relocated and it might need the large immediate vs. the small static known
offset for jmp.
But if that is the explanation, then it should consistently apply (or not
apply) the 0x66 prefix:
void example(void (*func)()) {
func();
}
But no, with -O2 this is compiled to
67 FF 64 24 04 jmp word ptr [esp + 4]
And with -O1 it compiles to:
67 66 FF 54 24 10 call dword ptr [esp + 0x10]
To summarize: I think it would be more correct to always add 0x66 to all JMPs
with -m16.