[X86] Apply the data32 mode switch in the Intel matcher (#212417)

In .code16, `data32 push 8` in Intel syntax assembled as `pushw $8` with
the 66 prefix dropped, and `data32 push 0x1234` truncated the immediate
to 16 bits. AT&T syntax gets both right.

`ForcedDataPrefix` is set while parsing either syntax, but only
`matchAndEmitATTInstruction` switched mode on it, so the Intel path took
the operand size from the mode and never saw the prefix.

Do the same switch in `matchAndEmitIntelInstruction`. The mode has to go
back to 16-bit before the instruction is emitted, otherwise the 32-bit
form is emitted without its 66 prefix. That function has several error
returns partway through matching, so a scope guard covers those.

Encodings after the change match both AT&T syntax and GNU as:

```
data32 push 8       [0x6a,0x08]      -> [0x66,0x6a,0x08]
data32 push 0x1234  [0x68,0x34,0x12] -> [0x66,0x68,0x34,0x12,0x00,0x00]
```

Fixes #156286
This commit is contained in:
Zane Hambly 2026-07-30 14:37:14 +12:00 committed by GitHub
parent dc57785129
commit bdddd3e720
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 56 additions and 0 deletions

View file

@ -4600,6 +4600,18 @@ bool X86AsmParser::matchAndEmitIntelInstruction(
MCStreamer &Out, uint64_t &ErrorInfo, bool MatchingInlineAsm) {
X86Operand &Op = static_cast<X86Operand &>(*Operands[0]);
SMRange EmptyRange;
// In 16-bit mode, if data32 is specified, temporarily switch to 32-bit mode
// when matching the instruction. The mode must be restored before the
// instruction is emitted, or the 32-bit form loses its 0x66 prefix.
const bool ForcedData32 = ForcedDataPrefix == X86::Is32Bit;
auto RestoreMode = [&] {
if (ForcedData32) {
SwitchMode(X86::Is16Bit);
ForcedDataPrefix = 0;
}
};
if (ForcedData32)
SwitchMode(X86::Is32Bit);
// Find one unsized memory operand, if present.
X86Operand *UnsizedMemOp = nullptr;
for (const auto &Op : Operands) {
@ -4697,6 +4709,7 @@ bool X86AsmParser::matchAndEmitIntelInstruction(
// If it's a bad mnemonic, all results will be the same.
if (Match.back() == Match_MnemonicFail) {
RestoreMode();
return Error(IDLoc, "invalid instruction mnemonic '" + Mnemonic + "'",
Op.getLocRange(), MatchingInlineAsm);
}
@ -4721,6 +4734,9 @@ bool X86AsmParser::matchAndEmitIntelInstruction(
/*Len=*/0, UnsizedMemOp->getMemFrontendSize());
}
// Matching is done, so drop back to 16-bit before anything is emitted.
RestoreMode();
// If exactly one matched, then we treat that as a successful match (and the
// instruction will already have been filled in correctly, since the failing
// matches won't have modified it).

View file

@ -0,0 +1,40 @@
// RUN: not llvm-mc -triple i386-unknown-unknown-code16 -x86-asm-syntax=intel --show-encoding %s 2> %t.err | FileCheck %s
// RUN: FileCheck --check-prefix=ERR < %t.err %s
// A data32 prefix makes an unsized push of an immediate 32-bit, matching the
// AT&T behaviour of the same instruction.
// CHECK: push 8
// CHECK-SAME: encoding: [0x66,0x6a,0x08]
data32 push 8
// CHECK: push 4660
// CHECK-SAME: encoding: [0x66,0x68,0x34,0x12,0x00,0x00]
data32 push 0x1234
// CHECK: push eax
// CHECK-SAME: encoding: [0x66,0x50]
data32 push eax
// Without the prefix the operand size still comes from the mode.
// CHECK: push 8
// CHECK-SAME: encoding: [0x6a,0x08]
push 8
// The prefix applies to one instruction only, so 16-bit mode has to be back in
// effect for whatever follows.
// CHECK: push 4660
// CHECK-SAME: encoding: [0x68,0x34,0x12]
push 0x1234
// The same holds when the prefixed instruction fails to match, otherwise the
// 32-bit mode used for matching leaks into the rest of the file.
// ERR: error: invalid instruction mnemonic 'nosuchinsn'
data32 nosuchinsn
// CHECK: push 8
// CHECK-SAME: encoding: [0x6a,0x08]
push 8