Skip to content

Commit de9ad4b

Browse files
committed
[X86][3DNOW] Teach decoder about AMD 3DNow! instrs
Summary: This patch makes the decoder understand old AMD 3DNow! instructions that have never been properly supported in the X86 disassembler, despite being supported in other subsystems. Hopefully this should make the X86 decoder more complete with respect to binaries containing legacy code. Reviewers: craig.topper Reviewed By: craig.topper Subscribers: llvm-commits, maksfb, bruno Differential Revision: https://reviews.llvm.org/D43311 llvm-svn: 325295
1 parent 775c7af commit de9ad4b

File tree

8 files changed

+156
-32
lines changed

8 files changed

+156
-32
lines changed

llvm/lib/Target/X86/Disassembler/X86DisassemblerDecoder.cpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,11 +588,44 @@ static int readPrefixes(struct InternalInstruction* insn) {
588588
insn->vectorExtensionPrefix[0], insn->vectorExtensionPrefix[1],
589589
insn->vectorExtensionPrefix[2]);
590590
}
591+
} else if (byte == 0x0f) {
592+
uint8_t byte1;
593+
594+
// Check for AMD 3DNow without a REX prefix
595+
if (consumeByte(insn, &byte1)) {
596+
unconsumeByte(insn);
597+
} else {
598+
if (byte1 != 0x0f) {
599+
unconsumeByte(insn);
600+
unconsumeByte(insn);
601+
} else {
602+
dbgprintf(insn, "Found AMD 3DNow prefix 0f0f");
603+
insn->vectorExtensionType = TYPE_3DNOW;
604+
}
605+
}
591606
} else if (isREX(insn, byte)) {
592607
if (lookAtByte(insn, &nextByte))
593608
return -1;
594609
insn->rexPrefix = byte;
595610
dbgprintf(insn, "Found REX prefix 0x%hhx", byte);
611+
612+
// Check for AMD 3DNow with a REX prefix
613+
if (nextByte == 0x0f) {
614+
consumeByte(insn, &nextByte);
615+
uint8_t byte1;
616+
617+
if (consumeByte(insn, &byte1)) {
618+
unconsumeByte(insn);
619+
} else {
620+
if (byte1 != 0x0f) {
621+
unconsumeByte(insn);
622+
unconsumeByte(insn);
623+
} else {
624+
dbgprintf(insn, "Found AMD 3DNow prefix 0f0f");
625+
insn->vectorExtensionType = TYPE_3DNOW;
626+
}
627+
}
628+
}
596629
} else
597630
unconsumeByte(insn);
598631

@@ -623,6 +656,8 @@ static int readPrefixes(struct InternalInstruction* insn) {
623656
return 0;
624657
}
625658

659+
static int readModRM(struct InternalInstruction* insn);
660+
626661
/*
627662
* readOpcode - Reads the opcode (excepting the ModR/M byte in the case of
628663
* extended or escape opcodes).
@@ -690,6 +725,12 @@ static int readOpcode(struct InternalInstruction* insn) {
690725
insn->opcodeType = XOPA_MAP;
691726
return consumeByte(insn, &insn->opcode);
692727
}
728+
} else if (insn->vectorExtensionType == TYPE_3DNOW) {
729+
// Consume operands before the opcode to comply with the 3DNow encoding
730+
if (readModRM(insn))
731+
return -1;
732+
insn->opcodeType = TWOBYTE;
733+
return consumeByte(insn, &insn->opcode);
693734
}
694735

695736
if (consumeByte(insn, &current))
@@ -735,8 +776,6 @@ static int readOpcode(struct InternalInstruction* insn) {
735776
return 0;
736777
}
737778

738-
static int readModRM(struct InternalInstruction* insn);
739-
740779
/*
741780
* getIDWithAttrMask - Determines the ID of an instruction, consuming
742781
* the ModR/M byte as appropriate for extended and escape opcodes,
@@ -912,6 +951,8 @@ static int getID(struct InternalInstruction* insn, const void *miiArg) {
912951

913952
if (lFromXOP3of3(insn->vectorExtensionPrefix[2]))
914953
attrMask |= ATTR_VEXL;
954+
} else if (insn->vectorExtensionType == TYPE_3DNOW) {
955+
attrMask |= ATTR_3DNOW;
915956
} else {
916957
return -1;
917958
}

llvm/lib/Target/X86/Disassembler/X86DisassemblerDecoder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,8 @@ enum VectorExtensionType {
493493
TYPE_VEX_2B = 0x1,
494494
TYPE_VEX_3B = 0x2,
495495
TYPE_EVEX = 0x3,
496-
TYPE_XOP = 0x4
496+
TYPE_XOP = 0x4,
497+
TYPE_3DNOW = 0x5
497498
};
498499

499500
/// \brief Type for the byte reader that the consumer must provide to

llvm/lib/Target/X86/Disassembler/X86DisassemblerDecoderCommon.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ namespace X86Disassembler {
6060
ENUM_ENTRY(ATTR_EVEXL2, (0x1 << 10)) \
6161
ENUM_ENTRY(ATTR_EVEXK, (0x1 << 11)) \
6262
ENUM_ENTRY(ATTR_EVEXKZ, (0x1 << 12)) \
63-
ENUM_ENTRY(ATTR_EVEXB, (0x1 << 13))
63+
ENUM_ENTRY(ATTR_EVEXB, (0x1 << 13)) \
64+
ENUM_ENTRY(ATTR_3DNOW, (0x1 << 14))
6465

6566
#define ENUM_ENTRY(n, v) n = v,
6667
enum attributeBits {
@@ -270,7 +271,8 @@ enum attributeBits {
270271
ENUM_ENTRY(IC_EVEX_L2_W_KZ, 3, "requires EVEX_KZ, L2 and W") \
271272
ENUM_ENTRY(IC_EVEX_L2_W_XS_KZ, 4, "requires EVEX_KZ, L2, W and XS prefix") \
272273
ENUM_ENTRY(IC_EVEX_L2_W_XD_KZ, 4, "requires EVEX_KZ, L2, W and XD prefix") \
273-
ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_KZ, 4, "requires EVEX_KZ, L2, W and OpSize")
274+
ENUM_ENTRY(IC_EVEX_L2_W_OPSIZE_KZ, 4, "requires EVEX_KZ, L2, W and OpSize") \
275+
ENUM_ENTRY(IC_3DNOW, 8, "requires AMD 3DNow prefix 0f0f")
274276

275277
#define ENUM_ENTRY(n, r, d) n,
276278
enum InstructionContext {

llvm/lib/Target/X86/X86Instr3DNow.td

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,14 @@ class I3DNow_binop<bits<8> o, Format F, dag ins, string Mnemonic, list<dag> pat,
5252
: I3DNow<o, F, (outs VR64:$dst), ins,
5353
!strconcat(Mnemonic, "\t{$src2, $dst|$dst, $src2}"), pat, itin>,
5454
Has3DNow0F0FOpcode {
55-
// FIXME: The disassembler doesn't support Has3DNow0F0FOpcode yet.
56-
let isAsmParserOnly = 1;
5755
let Constraints = "$src1 = $dst";
5856
}
5957

6058
class I3DNow_conv<bits<8> o, Format F, dag ins, string Mnemonic, list<dag> pat,
6159
InstrItinClass itin>
6260
: I3DNow<o, F, (outs VR64:$dst), ins,
6361
!strconcat(Mnemonic, "\t{$src, $dst|$dst, $src}"), pat, itin>,
64-
Has3DNow0F0FOpcode {
65-
// FIXME: The disassembler doesn't support Has3DNow0F0FOpcode yet.
66-
let isAsmParserOnly = 1;
67-
}
62+
Has3DNow0F0FOpcode;
6863

6964
multiclass I3DNow_binop_rm_int<bits<8> opc, string Mn, OpndItins itins,
7065
bit Commutable = 0, string Ver = ""> {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# RUN: llvm-mc --disassemble %s -triple=x86_64-unknown-linux-gnu | FileCheck %s
2+
3+
# Reference: AMD64 Architecture Programmer's Manual Vol.3
4+
# Pub no. 24594 - Rev. 3.25 - Dec 2017 - pgs.468-469
5+
6+
# CHECK: pfcmpge %mm0, %mm1
7+
0x0f 0x0f 0xc8 0x90
8+
9+
# CHECK: pfcmpgt %mm2, %mm0
10+
0x0f 0x0f 0xc2 0xa0
11+
12+
# CHECK: pfcmpeq %mm5, %mm2
13+
0x0f 0x0f 0xd5 0xb0
14+
15+
# CHECK: pfmin %mm1, %mm0
16+
0x0f 0x0f 0xc1 0x94
17+
18+
# CHECK: pfmax (%rax), %mm0
19+
0x0f 0x0f 0x00 0xa4
20+
21+
# CHECK: pfmul %mm6, %mm0
22+
0x0f 0x0f 0xc6 0xb4
23+
24+
# CHECK: pfrcp (%rbx), %mm1
25+
0x0f 0x0f 0x0b 0x96
26+
27+
# CHECK: pfrcpit1 %mm0, %mm2
28+
0x0f 0x0f 0xd0 0xa6
29+
30+
# CHECK: pfrcpit2 %mm0, %mm1
31+
0x0f 0x0f 0xc8 0xb6
32+
33+
# CHECK: pfrsqrt (%eax), %mm1
34+
0x67 0x0f 0x0f 0x08 0x97
35+
36+
# CHECK: pfrsqit1 (%ebx), %mm4
37+
0x67 0x0f 0x0f 0x23 0xa7
38+
39+
# CHECK: pmulhrw %mm3, %mm0
40+
0x0f 0x0f 0xc3 0xb7
41+
42+
# CHECK: pi2fw %mm1, %mm3
43+
0x0f 0x0f 0xd9 0x0c
44+
45+
# CHECK: pf2iw %mm2, %mm4
46+
0x0f 0x0f 0xe2 0x1c
47+
48+
# CHECK: pi2fd %mm3, %mm1
49+
0x0f 0x0f 0xcb 0x0d
50+
51+
# CHECK: pf2id (%rdi,%r8), %mm1
52+
0x42 0x0f 0x0f 0x0c 0x07 0x1d
53+
54+
# CHECK: pfnacc 16(%eax,%ebx,4), %mm0
55+
0x67 0x0f 0x0f 0x44 0x98 0x10 0x8a
56+
57+
# CHECK: pfsub %mm1, %mm0
58+
0x0f 0x0f 0xc1 0x9a
59+
60+
# CHECK: pfsubr %mm2, %mm1
61+
0x0f 0x0f 0xca 0xaa
62+
63+
# CHECK: pswapd %mm1, %mm3
64+
0x0f 0x0f 0xd9 0xbb
65+
66+
# CHECK: pfpnacc %mm0, %mm2
67+
0x0f 0x0f 0xd0 0x8e
68+
69+
# CHECK: pfadd %mm4, %mm3
70+
0x0f 0x0f 0xdc 0x9e
71+
72+
# CHECK: pfacc %mm1, %mm2
73+
0x0f 0x0f 0xd1 0xae
74+
75+
# CHECK: pavgusb %mm1, %mm3
76+
0x0f 0x0f 0xd9 0xbf

llvm/utils/TableGen/X86DisassemblerTables.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,8 @@ static inline bool inheritsFrom(InstructionContext child,
546546
case IC_EVEX_L2_W_XD_KZ_B:
547547
case IC_EVEX_L2_W_OPSIZE_KZ_B:
548548
return false;
549+
case IC_3DNOW:
550+
return false;
549551
default:
550552
errs() << "Unknown instruction class: " <<
551553
stringForContext((InstructionContext)parent) << "\n";
@@ -888,15 +890,17 @@ void DisassemblerTables::emitInstructionInfo(raw_ostream &o,
888890
}
889891

890892
void DisassemblerTables::emitContextTable(raw_ostream &o, unsigned &i) const {
891-
const unsigned int tableSize = 16384;
893+
const unsigned int tableSize = 32768;
892894
o.indent(i * 2) << "static const uint8_t " CONTEXTS_STR
893895
"[" << tableSize << "] = {\n";
894896
i++;
895897

896898
for (unsigned index = 0; index < tableSize; ++index) {
897899
o.indent(i * 2);
898900

899-
if (index & ATTR_EVEX) {
901+
if (index & ATTR_3DNOW)
902+
o << "IC_3DNOW";
903+
else if (index & ATTR_EVEX) {
900904
o << "IC_EVEX";
901905
if (index & ATTR_EVEXL2)
902906
o << "_L2";

llvm/utils/TableGen/X86RecognizableInstr.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,20 @@ RecognizableInstr::RecognizableInstr(DisassemblerTables &tables,
8080
Form = byteFromRec(Rec, "FormBits");
8181
Encoding = byteFromRec(Rec, "OpEncBits");
8282

83-
OpSize = byteFromRec(Rec, "OpSizeBits");
84-
AdSize = byteFromRec(Rec, "AdSizeBits");
85-
HasREX_WPrefix = Rec->getValueAsBit("hasREX_WPrefix");
86-
HasVEX_4V = Rec->getValueAsBit("hasVEX_4V");
87-
VEX_WPrefix = byteFromRec(Rec,"VEX_WPrefix");
88-
IgnoresVEX_L = Rec->getValueAsBit("ignoresVEX_L");
89-
HasEVEX_L2Prefix = Rec->getValueAsBit("hasEVEX_L2");
90-
HasEVEX_K = Rec->getValueAsBit("hasEVEX_K");
91-
HasEVEX_KZ = Rec->getValueAsBit("hasEVEX_Z");
92-
HasEVEX_B = Rec->getValueAsBit("hasEVEX_B");
93-
IsCodeGenOnly = Rec->getValueAsBit("isCodeGenOnly");
94-
ForceDisassemble = Rec->getValueAsBit("ForceDisassemble");
95-
CD8_Scale = byteFromRec(Rec, "CD8_Scale");
83+
OpSize = byteFromRec(Rec, "OpSizeBits");
84+
AdSize = byteFromRec(Rec, "AdSizeBits");
85+
HasREX_WPrefix = Rec->getValueAsBit("hasREX_WPrefix");
86+
HasVEX_4V = Rec->getValueAsBit("hasVEX_4V");
87+
VEX_WPrefix = byteFromRec(Rec,"VEX_WPrefix");
88+
IgnoresVEX_L = Rec->getValueAsBit("ignoresVEX_L");
89+
HasEVEX_L2Prefix = Rec->getValueAsBit("hasEVEX_L2");
90+
HasEVEX_K = Rec->getValueAsBit("hasEVEX_K");
91+
HasEVEX_KZ = Rec->getValueAsBit("hasEVEX_Z");
92+
HasEVEX_B = Rec->getValueAsBit("hasEVEX_B");
93+
Has3DNow0F0FOpcode = Rec->getValueAsBit("has3DNow0F0FOpcode");
94+
IsCodeGenOnly = Rec->getValueAsBit("isCodeGenOnly");
95+
ForceDisassemble = Rec->getValueAsBit("ForceDisassemble");
96+
CD8_Scale = byteFromRec(Rec, "CD8_Scale");
9697

9798
Name = Rec->getName();
9899

@@ -288,6 +289,8 @@ InstructionContext RecognizableInstr::insnContext() const {
288289
errs() << "Instruction does not use a prefix: " << Name << "\n";
289290
llvm_unreachable("Invalid prefix");
290291
}
292+
} else if (Has3DNow0F0FOpcode) {
293+
insnContext = IC_3DNOW;
291294
} else if (Is64Bit || HasREX_WPrefix || AdSize == X86Local::AdSize64) {
292295
if (HasREX_WPrefix && (OpSize == X86Local::OpSize16 || OpPrefix == X86Local::PD))
293296
insnContext = IC_64BIT_REXW_OPSIZE;

llvm/utils/TableGen/X86RecognizableInstr.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ class RecognizableInstr {
191191
bool HasEVEX_KZ;
192192
/// The hasEVEX_B field from the record
193193
bool HasEVEX_B;
194+
/// The has3DNow0F0FOpcode field from the record
195+
bool Has3DNow0F0FOpcode;
194196
/// Indicates that the instruction uses the L and L' fields for RC.
195197
bool EncodeRC;
196198
/// The isCodeGenOnly field from the record
@@ -210,12 +212,12 @@ class RecognizableInstr {
210212
/// Indicates whether the instruction should be emitted into the decode
211213
/// tables; regardless, it will be emitted into the instruction info table
212214
bool ShouldBeEmitted;
213-
215+
214216
/// The operands of the instruction, as listed in the CodeGenInstruction.
215217
/// They are not one-to-one with operands listed in the MCInst; for example,
216218
/// memory operands expand to 5 operands in the MCInst
217219
const std::vector<CGIOperandList::OperandInfo>* Operands;
218-
220+
219221
/// The description of the instruction that is emitted into the instruction
220222
/// info table
221223
InstructionSpecifier* Spec;
@@ -283,7 +285,7 @@ class RecognizableInstr {
283285
/// operand exists.
284286
/// @param operandIndex - The index into the generated operand table.
285287
/// Incremented by this function one or more
286-
/// times to reflect possible duplicate
288+
/// times to reflect possible duplicate
287289
/// operands).
288290
/// @param physicalOperandIndex - The index of the current operand into the
289291
/// set of non-duplicate ('physical') operands.
@@ -314,12 +316,12 @@ class RecognizableInstr {
314316
bool shouldBeEmitted() const {
315317
return ShouldBeEmitted;
316318
}
317-
319+
318320
/// emitInstructionSpecifier - Loads the instruction specifier for the current
319321
/// instruction into a DisassemblerTables.
320322
///
321323
void emitInstructionSpecifier();
322-
324+
323325
/// emitDecodePath - Populates the proper fields in the decode tables
324326
/// corresponding to the decode paths for this instruction.
325327
///
@@ -349,7 +351,7 @@ class RecognizableInstr {
349351
const CodeGenInstruction &insn,
350352
InstrUID uid);
351353
};
352-
354+
353355
} // namespace X86Disassembler
354356

355357
} // namespace llvm

0 commit comments

Comments
 (0)