diff --git a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp index 54edcba34a7e9..0a79538cd7e75 100644 --- a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp +++ b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp @@ -4600,6 +4600,18 @@ bool X86AsmParser::matchAndEmitIntelInstruction( MCStreamer &Out, uint64_t &ErrorInfo, bool MatchingInlineAsm) { X86Operand &Op = static_cast(*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). diff --git a/llvm/test/MC/X86/intel-syntax-data32-16.s b/llvm/test/MC/X86/intel-syntax-data32-16.s new file mode 100644 index 0000000000000..274a505c3cdc9 --- /dev/null +++ b/llvm/test/MC/X86/intel-syntax-data32-16.s @@ -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