Skip to content

Commit 1a15906

Browse files
committed
[X86][MC] Support encoding/decoding for APX CCMP/CTEST
APX assembly syntax recommendations: https://cdrdv2.intel.com/v1/dl/getContent/817241
1 parent 96b2c3b commit 1a15906

20 files changed

+4505
-14
lines changed

llvm/include/llvm/Support/X86DisassemblerDecoderCommon.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ enum ModRMDecisionType { MODRMTYPES MODRM_max };
434434
ENUM_ENTRY(ENCODING_Rv, \
435435
"Register code of operand size added to the opcode byte") \
436436
ENUM_ENTRY(ENCODING_CC, "Condition code encoded in opcode") \
437+
ENUM_ENTRY(ENCODING_CF, "Condition flags encoded in EVEX.VVVV") \
437438
ENUM_ENTRY(ENCODING_DUP, \
438439
"Duplicate of another operand; ID is encoded in type") \
439440
ENUM_ENTRY(ENCODING_SI, "Source index; encoded in OpSize/Adsize prefix") \

llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,7 @@ class X86AsmParser : public MCTargetAsmParser {
11221122
unsigned IdentifyMasmOperator(StringRef Name);
11231123
bool ParseMasmOperator(unsigned OpKind, int64_t &Val);
11241124
bool ParseRoundingModeOp(SMLoc Start, OperandVector &Operands);
1125+
bool parseCFlagsOp(OperandVector &Operands);
11251126
bool ParseIntelNamedOperator(StringRef Name, IntelExprStateMachine &SM,
11261127
bool &ParseError, SMLoc &End);
11271128
bool ParseMasmNamedOperator(StringRef Name, IntelExprStateMachine &SM,
@@ -2306,6 +2307,58 @@ bool X86AsmParser::ParseRoundingModeOp(SMLoc Start, OperandVector &Operands) {
23062307
return Error(Tok.getLoc(), "unknown token in expression");
23072308
}
23082309

2310+
/// Parse condtional flags for CCMP/CTEST, e.g {of,sf,zf,cf} right after
2311+
/// mnemonic.
2312+
bool X86AsmParser::parseCFlagsOp(OperandVector &Operands) {
2313+
MCAsmParser &Parser = getParser();
2314+
const AsmToken StartTok = Parser.getTok();
2315+
const SMLoc Start = StartTok.getLoc();
2316+
if (!StartTok.is(AsmToken::LCurly))
2317+
return Error(StartTok.getLoc(), "Expected { at this point");
2318+
Parser.Lex(); // Eat "{"
2319+
AsmToken Tok = Parser.getTok();
2320+
SMLoc End;
2321+
if (Tok.is(AsmToken::RCurly)) {
2322+
End = Tok.getEndLoc();
2323+
Operands.push_back(X86Operand::CreateImm(
2324+
MCConstantExpr::create(0, Parser.getContext()), Start, End));
2325+
Parser.Lex(); // Eat "}"
2326+
return false;
2327+
}
2328+
unsigned CFlags = 0;
2329+
for (unsigned I = 0; I < 4; ++I) {
2330+
Tok = Parser.getTok();
2331+
unsigned CFlag = StringSwitch<unsigned>(Tok.getIdentifier())
2332+
.Case("of", 0x8)
2333+
.Case("sf", 0x4)
2334+
.Case("zf", 0x2)
2335+
.Case("cf", 0x1)
2336+
.Default(~0U);
2337+
if (CFlag == ~0U)
2338+
return Error(Tok.getLoc(), "Invalid conditional flags");
2339+
2340+
if (CFlags & CFlag)
2341+
return Error(Tok.getLoc(), "Duplicated conditional flag");
2342+
CFlags |= CFlag;
2343+
2344+
Parser.Lex(); // Eat one conditional flag
2345+
Tok = Parser.getTok();
2346+
if (Tok.is(AsmToken::RCurly)) {
2347+
End = Tok.getEndLoc();
2348+
Operands.push_back(X86Operand::CreateImm(
2349+
MCConstantExpr::create(CFlags, Parser.getContext()), Start, End));
2350+
Parser.Lex(); // Eat "}"
2351+
return false;
2352+
} else if (I == 3) {
2353+
return Error(Tok.getLoc(), "Expected } at this point");
2354+
} else if (Tok.isNot(AsmToken::Comma)) {
2355+
return Error(Tok.getLoc(), "Expected } or , at this point");
2356+
}
2357+
Parser.Lex(); // Eat ","
2358+
}
2359+
llvm_unreachable("Unexpected control flow");
2360+
}
2361+
23092362
/// Parse the '.' operator.
23102363
bool X86AsmParser::ParseIntelDotOperator(IntelExprStateMachine &SM,
23112364
SMLoc &End) {
@@ -3461,6 +3514,11 @@ bool X86AsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
34613514
Operands.push_back(X86Operand::CreateImm(ImmOp, NameLoc, NameLoc));
34623515
}
34633516

3517+
// Parse condtional flags after mnemonic.
3518+
if ((Name.starts_with("ccmp") || Name.starts_with("ctest")) &&
3519+
parseCFlagsOp(Operands))
3520+
return true;
3521+
34643522
// This does the actual operand parsing. Don't parse any more if we have a
34653523
// prefix juxtaposed with an operation like "lock incl 4(%rax)", because we
34663524
// just want to parse the "lock" as the first instruction and the "incl" as

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,6 +1141,15 @@ static int getInstructionIDWithAttrMask(uint16_t *instructionID,
11411141
return 0;
11421142
}
11431143

1144+
static bool isCCMPOrCTEST(InternalInstruction *insn) {
1145+
return (insn->opcodeType == MAP4) &&
1146+
(((insn->opcode & 0xfe) == 0x38) || ((insn->opcode & 0xfe) == 0x3a) ||
1147+
(((insn->opcode & 0xfe) == 0x80 || insn->opcode == 0x83) &&
1148+
regFromModRM(insn->modRM) == 7) ||
1149+
(insn->opcode & 0xfe) == 0x84 ||
1150+
((insn->opcode & 0xfe) == 0xf6 && regFromModRM(insn->modRM) == 0));
1151+
}
1152+
11441153
static bool isNF(InternalInstruction *insn) {
11451154
if (!nfFromEVEX4of4(insn->vectorExtensionPrefix[3]))
11461155
return false;
@@ -1197,9 +1206,12 @@ static int getInstructionID(struct InternalInstruction *insn,
11971206
attrMask |= ATTR_EVEXKZ;
11981207
if (bFromEVEX4of4(insn->vectorExtensionPrefix[3]))
11991208
attrMask |= ATTR_EVEXB;
1200-
if (isNF(insn)) // NF bit is the MSB of aaa.
1209+
if (isNF(insn) && !readModRM(insn) &&
1210+
!isCCMPOrCTEST(insn)) // NF bit is the MSB of aaa.
12011211
attrMask |= ATTR_EVEXNF;
1202-
else if (aaaFromEVEX4of4(insn->vectorExtensionPrefix[3]))
1212+
// aaa is not used a opmask in MAP4
1213+
else if (aaaFromEVEX4of4(insn->vectorExtensionPrefix[3]) &&
1214+
(insn->opcodeType != MAP4))
12031215
attrMask |= ATTR_EVEXK;
12041216
if (lFromEVEX4of4(insn->vectorExtensionPrefix[3]))
12051217
attrMask |= ATTR_VEXL;
@@ -1732,8 +1744,15 @@ static int readOperands(struct InternalInstruction *insn) {
17321744
if (readOpcodeRegister(insn, 0))
17331745
return -1;
17341746
break;
1747+
case ENCODING_CF:
1748+
insn->immediates[1] = oszcFromEVEX3of4(insn->vectorExtensionPrefix[2]);
1749+
needVVVV = false; // oszc shares the same bits with VVVV
1750+
break;
17351751
case ENCODING_CC:
1736-
insn->immediates[1] = insn->opcode & 0xf;
1752+
if (isCCMPOrCTEST(insn))
1753+
insn->immediates[2] = scFromEVEX4of4(insn->vectorExtensionPrefix[3]);
1754+
else
1755+
insn->immediates[1] = insn->opcode & 0xf;
17371756
break;
17381757
case ENCODING_FP:
17391758
break;
@@ -2371,9 +2390,15 @@ static bool translateOperand(MCInst &mcInst, const OperandSpecifier &operand,
23712390
case ENCODING_Rv:
23722391
translateRegister(mcInst, insn.opcodeRegister);
23732392
return false;
2374-
case ENCODING_CC:
2393+
case ENCODING_CF:
23752394
mcInst.addOperand(MCOperand::createImm(insn.immediates[1]));
23762395
return false;
2396+
case ENCODING_CC:
2397+
if (isCCMPOrCTEST(&insn))
2398+
mcInst.addOperand(MCOperand::createImm(insn.immediates[2]));
2399+
else
2400+
mcInst.addOperand(MCOperand::createImm(insn.immediates[1]));
2401+
return false;
23772402
case ENCODING_FP:
23782403
translateFPRegister(mcInst, insn.modRM & 7);
23792404
return false;

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ namespace X86Disassembler {
3333
#define twoBitsFromOffset6(val) (((val) >> 6) & 0x3)
3434
#define threeBitsFromOffset0(val) ((val) & 0x7)
3535
#define threeBitsFromOffset3(val) (((val) >> 3) & 0x7)
36+
#define fourBitsFromOffset0(val) ((val) & 0xf)
37+
#define fourBitsFromOffset3(val) (((val) >> 3) & 0xf)
3638
#define fiveBitsFromOffset0(val) ((val) & 0x1f)
3739
#define invertedBitFromOffset2(val) (((~(val)) >> 2) & 0x1)
3840
#define invertedBitFromOffset3(val) (((~(val)) >> 3) & 0x1)
@@ -97,13 +99,16 @@ namespace X86Disassembler {
9799
#define vvvvFromEVEX3of4(evex) invertedFourBitsFromOffset3(evex)
98100
#define x2FromEVEX3of4(evex) invertedBitFromOffset2(evex)
99101
#define ppFromEVEX3of4(evex) twoBitsFromOffset0(evex)
102+
#define oszcFromEVEX3of4(evex) fourBitsFromOffset3(evex)
100103
#define zFromEVEX4of4(evex) bitFromOffset7(evex)
101104
#define l2FromEVEX4of4(evex) bitFromOffset6(evex)
102105
#define lFromEVEX4of4(evex) bitFromOffset5(evex)
103106
#define bFromEVEX4of4(evex) bitFromOffset4(evex)
104107
#define v2FromEVEX4of4(evex) invertedBitFromOffset3(evex)
105108
#define aaaFromEVEX4of4(evex) threeBitsFromOffset0(evex)
106109
#define nfFromEVEX4of4(evex) bitFromOffset2(evex)
110+
#define oszcFromEVEX3of4(evex) fourBitsFromOffset3(evex)
111+
#define scFromEVEX4of4(evex) fourBitsFromOffset0(evex)
107112

108113
// These enums represent Intel registers for use by the decoder.
109114
#define REGS_8BIT \
@@ -752,10 +757,10 @@ struct InternalInstruction {
752757
// The displacement, used for memory operands
753758
int32_t displacement;
754759

755-
// Immediates. There can be two in some cases
760+
// Immediates. There can be three in some cases
756761
uint8_t numImmediatesConsumed;
757762
uint8_t numImmediatesTranslated;
758-
uint64_t immediates[2];
763+
uint64_t immediates[3];
759764

760765
// A register or immediate operand encoded into the opcode
761766
Reg opcodeRegister;

llvm/lib/Target/X86/MCTargetDesc/X86BaseInfo.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,10 @@ enum : uint64_t {
875875
ExplicitOpPrefixMask = 3ULL << ExplicitOpPrefixShift,
876876
/// EVEX_NF - Set if this instruction has EVEX.NF field set.
877877
EVEX_NFShift = ExplicitOpPrefixShift + 2,
878-
EVEX_NF = 1ULL << EVEX_NFShift
878+
EVEX_NF = 1ULL << EVEX_NFShift,
879+
// TwoConditionalOps - Set if this instruction has two conditional operands
880+
TwoConditionalOps_Shift = EVEX_NFShift + 1,
881+
TwoConditionalOps = 1ULL << TwoConditionalOps_Shift
879882
};
880883

881884
/// \returns true if the instruction with given opcode is a prefix.

llvm/lib/Target/X86/MCTargetDesc/X86InstPrinterCommon.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ void X86InstPrinterCommon::printCondCode(const MCInst *MI, unsigned Op,
3333
MI->getOpcode() == X86::CMPCCXADDmr64 ||
3434
MI->getOpcode() == X86::CMPCCXADDmr32_EVEX ||
3535
MI->getOpcode() == X86::CMPCCXADDmr64_EVEX;
36+
bool IsCCMPOrCTEST =
37+
X86::isCCMPCC(MI->getOpcode()) || X86::isCTESTCC(MI->getOpcode());
38+
3639
switch (Imm) {
3740
default: llvm_unreachable("Invalid condcode argument!");
3841
case 0: O << "o"; break;
@@ -45,15 +48,36 @@ void X86InstPrinterCommon::printCondCode(const MCInst *MI, unsigned Op,
4548
case 7: O << (Flavor ? "nbe" : "a"); break;
4649
case 8: O << "s"; break;
4750
case 9: O << "ns"; break;
48-
case 0xa: O << "p"; break;
49-
case 0xb: O << "np"; break;
51+
case 0xa: O << (IsCCMPOrCTEST ? "t" : "p"); break;
52+
case 0xb: O << (IsCCMPOrCTEST ? "f" : "np"); break;
5053
case 0xc: O << "l"; break;
5154
case 0xd: O << (Flavor ? "nl" : "ge"); break;
5255
case 0xe: O << "le"; break;
5356
case 0xf: O << (Flavor ? "nle" : "g"); break;
5457
}
5558
}
5659

60+
void X86InstPrinterCommon::printCondFlags(const MCInst *MI, unsigned Op,
61+
raw_ostream &O) {
62+
// +----+----+----+----+
63+
// | OF | SF | ZF | CF |
64+
// +----+----+----+----+
65+
int64_t Imm = MI->getOperand(Op).getImm();
66+
assert(Imm >= 0 && Imm < 16 && "Invalid condition flags");
67+
O << "{";
68+
std::string Flags;
69+
if (Imm & 0x8)
70+
Flags += "of,";
71+
if (Imm & 0x4)
72+
Flags += "sf,";
73+
if (Imm & 0x2)
74+
Flags += "zf,";
75+
if (Imm & 0x1)
76+
Flags += "cf,";
77+
StringRef SimplifiedFlags = StringRef(Flags).rtrim(",");
78+
O << SimplifiedFlags << "}";
79+
}
80+
5781
void X86InstPrinterCommon::printSSEAVXCC(const MCInst *MI, unsigned Op,
5882
raw_ostream &O) {
5983
int64_t Imm = MI->getOperand(Op).getImm();

llvm/lib/Target/X86/MCTargetDesc/X86InstPrinterCommon.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class X86InstPrinterCommon : public MCInstPrinter {
2424

2525
virtual void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O) = 0;
2626
void printCondCode(const MCInst *MI, unsigned Op, raw_ostream &OS);
27+
void printCondFlags(const MCInst *MI, unsigned Op, raw_ostream &OS);
2728
void printSSEAVXCC(const MCInst *MI, unsigned Op, raw_ostream &OS);
2829
void printVPCOMMnemonic(const MCInst *MI, raw_ostream &OS);
2930
void printVPCMPMnemonic(const MCInst *MI, raw_ostream &OS);

llvm/lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,12 @@ class X86OpcodePrefixHelper {
200200
void setB(const MCInst &MI, unsigned OpNum) {
201201
B = getRegEncoding(MI, OpNum) >> 3 & 1;
202202
}
203-
void set4V(const MCInst &MI, unsigned OpNum) {
204-
set4V(getRegEncoding(MI, OpNum));
203+
void set4V(const MCInst &MI, unsigned OpNum, bool IsImm = false) {
204+
// OF, SF, ZF and CF reuse VEX_4V bits but are not reversed
205+
if (IsImm)
206+
set4V(~(MI.getOperand(OpNum).getImm()));
207+
else
208+
set4V(getRegEncoding(MI, OpNum));
205209
}
206210
void setL(bool V) { VEX_L = V; }
207211
void setPP(unsigned V) { VEX_PP = V; }
@@ -252,6 +256,11 @@ class X86OpcodePrefixHelper {
252256
EVEX_aaa = getRegEncoding(MI, OpNum);
253257
}
254258
void setNF(bool V) { EVEX_aaa |= V << 2; }
259+
void setSC(const MCInst &MI, unsigned OpNum) {
260+
unsigned Encoding = MI.getOperand(OpNum).getImm();
261+
EVEX_V2 = ~(Encoding >> 3) & 0x1;
262+
EVEX_aaa = Encoding & 0x7;
263+
}
255264

256265
X86OpcodePrefixHelper(const MCRegisterInfo &MRI)
257266
: W(0), R(0), X(0), B(0), M(0), R2(0), X2(0), B2(0), VEX_4V(0), VEX_L(0),
@@ -1045,6 +1054,7 @@ X86MCCodeEmitter::emitVEXOpcodePrefix(int MemOperand, const MCInst &MI,
10451054
uint8_t EVEX_rc = 0;
10461055

10471056
unsigned CurOp = X86II::getOperandBias(Desc);
1057+
bool HasTwoConditionalOps = TSFlags & X86II::TwoConditionalOps;
10481058

10491059
switch (TSFlags & X86II::FormMask) {
10501060
default:
@@ -1086,6 +1096,10 @@ X86MCCodeEmitter::emitVEXOpcodePrefix(int MemOperand, const MCInst &MI,
10861096
Prefix.set4VV2(MI, CurOp++);
10871097

10881098
Prefix.setRR2(MI, CurOp++);
1099+
if (HasTwoConditionalOps) {
1100+
Prefix.set4V(MI, CurOp++, /*IsImm=*/true);
1101+
Prefix.setSC(MI, CurOp++);
1102+
}
10891103
break;
10901104
}
10911105
case X86II::MRMSrcMemFSIB:
@@ -1115,7 +1129,11 @@ X86MCCodeEmitter::emitVEXOpcodePrefix(int MemOperand, const MCInst &MI,
11151129
Prefix.setBB2(MI, MemOperand + X86::AddrBaseReg);
11161130
Prefix.setXX2(MI, MemOperand + X86::AddrIndexReg);
11171131
Prefix.setV2(MI, MemOperand + X86::AddrIndexReg, HasVEX_4V);
1118-
1132+
CurOp += X86::AddrNumOperands;
1133+
if (HasTwoConditionalOps) {
1134+
Prefix.set4V(MI, CurOp++, /*IsImm=*/true);
1135+
Prefix.setSC(MI, CurOp++);
1136+
}
11191137
break;
11201138
}
11211139
case X86II::MRMSrcMem4VOp3: {
@@ -1155,7 +1173,11 @@ X86MCCodeEmitter::emitVEXOpcodePrefix(int MemOperand, const MCInst &MI,
11551173
Prefix.setBB2(MI, MemOperand + X86::AddrBaseReg);
11561174
Prefix.setXX2(MI, MemOperand + X86::AddrIndexReg);
11571175
Prefix.setV2(MI, MemOperand + X86::AddrIndexReg, HasVEX_4V);
1158-
1176+
CurOp += X86::AddrNumOperands + 1; // Skip first imm.
1177+
if (HasTwoConditionalOps) {
1178+
Prefix.set4V(MI, CurOp++, /*IsImm=*/true);
1179+
Prefix.setSC(MI, CurOp++);
1180+
}
11591181
break;
11601182
}
11611183
case X86II::MRMSrcReg: {
@@ -1183,6 +1205,11 @@ X86MCCodeEmitter::emitVEXOpcodePrefix(int MemOperand, const MCInst &MI,
11831205
Prefix.setX(MI, CurOp, 4);
11841206
++CurOp;
11851207

1208+
if (HasTwoConditionalOps) {
1209+
Prefix.set4V(MI, CurOp++, /*IsImm=*/true);
1210+
Prefix.setSC(MI, CurOp++);
1211+
}
1212+
11861213
if (TSFlags & X86II::EVEX_B) {
11871214
if (HasEVEX_RC) {
11881215
unsigned NumOps = Desc.getNumOperands();
@@ -1236,6 +1263,10 @@ X86MCCodeEmitter::emitVEXOpcodePrefix(int MemOperand, const MCInst &MI,
12361263
Prefix.set4VV2(MI, CurOp++);
12371264

12381265
Prefix.setRR2(MI, CurOp++);
1266+
if (HasTwoConditionalOps) {
1267+
Prefix.set4V(MI, CurOp++, /*IsImm=*/true);
1268+
Prefix.setSC(MI, CurOp++);
1269+
}
12391270
if (TSFlags & X86II::EVEX_B)
12401271
EncodeRC = true;
12411272
break;
@@ -1266,6 +1297,10 @@ X86MCCodeEmitter::emitVEXOpcodePrefix(int MemOperand, const MCInst &MI,
12661297
Prefix.setBB2(MI, CurOp);
12671298
Prefix.setX(MI, CurOp, 4);
12681299
++CurOp;
1300+
if (HasTwoConditionalOps) {
1301+
Prefix.set4V(MI, ++CurOp, /*IsImm=*/true);
1302+
Prefix.setSC(MI, ++CurOp);
1303+
}
12691304
break;
12701305
}
12711306
}
@@ -1525,6 +1560,7 @@ void X86MCCodeEmitter::encodeInstruction(const MCInst &MI,
15251560
unsigned OpcodeOffset = 0;
15261561

15271562
bool IsND = X86II::hasNewDataDest(TSFlags);
1563+
bool HasTwoConditionalOps = TSFlags & X86II::TwoConditionalOps;
15281564

15291565
uint64_t Form = TSFlags & X86II::FormMask;
15301566
switch (Form) {
@@ -1914,11 +1950,16 @@ void X86MCCodeEmitter::encodeInstruction(const MCInst &MI,
19141950
// If there is a remaining operand, it must be a trailing immediate. Emit it
19151951
// according to the right size for the instruction. Some instructions
19161952
// (SSE4a extrq and insertq) have two trailing immediates.
1917-
while (CurOp != NumOps && NumOps - CurOp <= 2) {
1953+
1954+
// Skip two trainling conditional operands encoded in EVEX prefix
1955+
unsigned RemaningOps = NumOps - CurOp - 2 * HasTwoConditionalOps;
1956+
while (RemaningOps) {
19181957
emitImmediate(MI.getOperand(CurOp++), MI.getLoc(),
19191958
X86II::getSizeOfImm(TSFlags), getImmFixupKind(TSFlags),
19201959
StartByte, CB, Fixups);
1960+
--RemaningOps;
19211961
}
1962+
CurOp += 2 * HasTwoConditionalOps;
19221963
}
19231964

19241965
if ((TSFlags & X86II::OpMapMask) == X86II::ThreeDNow)

0 commit comments

Comments
 (0)