Skip to content

Commit 1ca8092

Browse files
authored
[X86][MC] Support encoding/decoding for APX CCMP/CTEST (llvm#83863)
APX assembly syntax recommendations: https://cdrdv2.intel.com/v1/dl/getContent/817241 NOTE: The change in llvm/tools/llvm-exegesis/lib/X86/Target.cpp is for test LLVM :: tools/llvm-exegesis/X86/latency/latency-SETCCr-cond-codes-sweep.s For `SETcc`, llvm-exegesis would randomly choose 1 other instruction to test with `SETcc`, after selecting the instruction, llvm-exegesis would check if the operand is initialized and valid, if not `randomizeTargetMCOperand` would choose a value for invalid operand, it misses support for condition code operand, which cause the flaky failure after `CCMP` supported. llvm-exegesis can choose `CCMP` without specifying ccmp feature b/c it use `MCSubtarget` and only16/32/64 bit is considered. llvm-exegesis doesn't choose other instructions b/c requirement in `hasAliasingRegistersThrough`: the instruction should use GPR (defined by `SETcc`) and define `EFLAGS` (used by `SETcc`).
1 parent d742872 commit 1ca8092

23 files changed

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

3526+
// Parse condtional flags after mnemonic.
3527+
if ((Name.starts_with("ccmp") || Name.starts_with("ctest")) &&
3528+
parseCFlagsOp(Operands))
3529+
return true;
3530+
34643531
// This does the actual operand parsing. Don't parse any more if we have a
34653532
// prefix juxtaposed with an operation like "lock incl 4(%rax)", because we
34663533
// just want to parse the "lock" as the first instruction and the "incl" as

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

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

1144+
static bool isCCMPOrCTEST(InternalInstruction *insn) {
1145+
if (insn->opcodeType != MAP4)
1146+
return false;
1147+
if (insn->opcode == 0x83 && regFromModRM(insn->modRM) == 7)
1148+
return true;
1149+
switch (insn->opcode & 0xfe) {
1150+
default:
1151+
return false;
1152+
case 0x38:
1153+
case 0x3a:
1154+
case 0x84:
1155+
return true;
1156+
case 0x80:
1157+
return regFromModRM(insn->modRM) == 7;
1158+
case 0xf6:
1159+
return regFromModRM(insn->modRM) == 0;
1160+
}
1161+
}
1162+
11441163
static bool isNF(InternalInstruction *insn) {
11451164
if (!nfFromEVEX4of4(insn->vectorExtensionPrefix[3]))
11461165
return false;
@@ -1197,9 +1216,12 @@ static int getInstructionID(struct InternalInstruction *insn,
11971216
attrMask |= ATTR_EVEXKZ;
11981217
if (bFromEVEX4of4(insn->vectorExtensionPrefix[3]))
11991218
attrMask |= ATTR_EVEXB;
1200-
if (isNF(insn)) // NF bit is the MSB of aaa.
1219+
if (isNF(insn) && !readModRM(insn) &&
1220+
!isCCMPOrCTEST(insn)) // NF bit is the MSB of aaa.
12011221
attrMask |= ATTR_EVEXNF;
1202-
else if (aaaFromEVEX4of4(insn->vectorExtensionPrefix[3]))
1222+
// aaa is not used a opmask in MAP4
1223+
else if (aaaFromEVEX4of4(insn->vectorExtensionPrefix[3]) &&
1224+
(insn->opcodeType != MAP4))
12031225
attrMask |= ATTR_EVEXK;
12041226
if (lFromEVEX4of4(insn->vectorExtensionPrefix[3]))
12051227
attrMask |= ATTR_VEXL;
@@ -1732,8 +1754,15 @@ static int readOperands(struct InternalInstruction *insn) {
17321754
if (readOpcodeRegister(insn, 0))
17331755
return -1;
17341756
break;
1757+
case ENCODING_CF:
1758+
insn->immediates[1] = oszcFromEVEX3of4(insn->vectorExtensionPrefix[2]);
1759+
needVVVV = false; // oszc shares the same bits with VVVV
1760+
break;
17351761
case ENCODING_CC:
1736-
insn->immediates[1] = insn->opcode & 0xf;
1762+
if (isCCMPOrCTEST(insn))
1763+
insn->immediates[2] = scFromEVEX4of4(insn->vectorExtensionPrefix[3]);
1764+
else
1765+
insn->immediates[1] = insn->opcode & 0xf;
17371766
break;
17381767
case ENCODING_FP:
17391768
break;
@@ -2371,9 +2400,15 @@ static bool translateOperand(MCInst &mcInst, const OperandSpecifier &operand,
23712400
case ENCODING_Rv:
23722401
translateRegister(mcInst, insn.opcodeRegister);
23732402
return false;
2374-
case ENCODING_CC:
2403+
case ENCODING_CF:
23752404
mcInst.addOperand(MCOperand::createImm(insn.immediates[1]));
23762405
return false;
2406+
case ENCODING_CC:
2407+
if (isCCMPOrCTEST(&insn))
2408+
mcInst.addOperand(MCOperand::createImm(insn.immediates[2]));
2409+
else
2410+
mcInst.addOperand(MCOperand::createImm(insn.immediates[1]));
2411+
return false;
23772412
case ENCODING_FP:
23782413
translateFPRegister(mcInst, insn.modRM & 7);
23792414
return false;

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

Lines changed: 6 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,15 @@ 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 scFromEVEX4of4(evex) fourBitsFromOffset0(evex)
107111

108112
// These enums represent Intel registers for use by the decoder.
109113
#define REGS_8BIT \
@@ -755,10 +759,10 @@ struct InternalInstruction {
755759
// The displacement, used for memory operands
756760
int32_t displacement;
757761

758-
// Immediates. There can be two in some cases
762+
// Immediates. There can be three in some cases
759763
uint8_t numImmediatesConsumed;
760764
uint8_t numImmediatesTranslated;
761-
uint64_t immediates[2];
765+
uint64_t immediates[3];
762766

763767
// A register or immediate operand encoded into the opcode
764768
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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ using namespace llvm;
2929
void X86InstPrinterCommon::printCondCode(const MCInst *MI, unsigned Op,
3030
raw_ostream &O) {
3131
int64_t Imm = MI->getOperand(Op).getImm();
32-
bool IsCMPCCXADD = X86::isCMPCCXADD(MI->getOpcode());
32+
unsigned Opc = MI->getOpcode();
33+
bool IsCMPCCXADD = X86::isCMPCCXADD(Opc);
34+
bool IsCCMPOrCTEST = X86::isCCMPCC(Opc) || X86::isCTESTCC(Opc);
3335

3436
// clang-format off
3537
switch (Imm) {
@@ -44,8 +46,8 @@ void X86InstPrinterCommon::printCondCode(const MCInst *MI, unsigned Op,
4446
case 7: O << (IsCMPCCXADD ? "nbe" : "a"); break;
4547
case 8: O << "s"; break;
4648
case 9: O << "ns"; break;
47-
case 0xa: O << "p"; break;
48-
case 0xb: O << "np"; break;
49+
case 0xa: O << (IsCCMPOrCTEST ? "t" : "p"); break;
50+
case 0xb: O << (IsCCMPOrCTEST ? "f" : "np"); break;
4951
case 0xc: O << "l"; break;
5052
case 0xd: O << (IsCMPCCXADD ? "nl" : "ge"); break;
5153
case 0xe: O << "le"; break;
@@ -54,6 +56,27 @@ void X86InstPrinterCommon::printCondCode(const MCInst *MI, unsigned Op,
5456
// clang-format on
5557
}
5658

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

0 commit comments

Comments
 (0)