Fix encoding and add parsing support for the arm/thumb CPS instruction:

- Add custom operand matching for imod and iflags.
- Rename SplitMnemonicAndCC to SplitMnemonic since it splits more than CC
  from mnemonic.
- While adding ".w" as an operand, don't change "Head" to avoid passing the
  wrong mnemonic to ParseOperand.
- Add asm parser tests.
- Add disassembler tests just to make sure it can catch all cps versions.



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@125489 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Bruno Cardoso Lopes 2011-02-14 13:09:44 +00:00
parent 283c8caccd
commit a2b6e4151b
16 changed files with 316 additions and 107 deletions

View File

@ -97,6 +97,36 @@ inline static const char *ARMCondCodeToString(ARMCC::CondCodes CC) {
}
}
namespace ARM_PROC {
enum IMod {
IE = 2,
ID = 3
};
enum IFlags {
F = 1,
I = 2,
A = 4
};
inline static const char *IFlagsToString(unsigned val) {
switch (val) {
default: llvm_unreachable("Unknown iflags operand");
case F: return "f";
case I: return "i";
case A: return "a";
}
}
inline static const char *IModToString(unsigned val) {
switch (val) {
default: llvm_unreachable("Unknown imod operand");
case IE: return "ie";
case ID: return "id";
}
}
}
namespace ARM_MB {
// The Memory Barrier Option constants map directly to the 4-bit encoding of
// the option field for memory barrier operations.

View File

@ -155,6 +155,22 @@ def MemBarrierOptOperand : AsmOperandClass {
let ParserMethod = "tryParseMemBarrierOptOperand";
}
def ProcIFlagsOperand : AsmOperandClass {
let Name = "ProcIFlags";
let SuperClasses = [];
let ParserMethod = "tryParseProcIFlagsOperand";
}
// ARM imod and iflag operands, used only by the CPS instruction.
def imod_op : Operand<i32> {
let PrintMethod = "printCPSIMod";
}
def iflags_op : Operand<i32> {
let PrintMethod = "printCPSIFlag";
let ParserMatchClass = ProcIFlagsOperand;
}
// ARM Predicate operand. Default to 14 = always (AL). Second part is CC
// register whose default is 0 (no register).
def pred : PredicateOperand<OtherVT, (ops i32imm, CCR),

View File

@ -1102,22 +1102,38 @@ def BKPT : AI<(outs), (ins i32imm:$val), MiscFrm, NoItinerary, "bkpt", "\t$val",
let Inst{7-4} = 0b0111;
}
// Change Processor State is a system instruction -- for disassembly only.
// The singleton $opt operand contains the following information:
// opt{4-0} = mode from Inst{4-0}
// opt{5} = changemode from Inst{17}
// opt{8-6} = AIF from Inst{8-6}
// opt{10-9} = imod from Inst{19-18} with 0b10 as enable and 0b11 as disable
// FIXME: Integrated assembler will need these split out.
def CPS : AXI<(outs), (ins cps_opt:$opt), MiscFrm, NoItinerary, "cps$opt",
[/* For disassembly only; pattern left blank */]>,
Requires<[IsARM]> {
// Change Processor State is a system instruction -- for disassembly and
// parsing only.
// FIXME: Since the asm parser has currently no clean way to handle optional
// operands, create 3 versions of the same instruction. Once there's a clean
// framework to represent optional operands, change this behavior.
class CPS<dag iops, string asm_ops>
: AXI<(outs), iops, MiscFrm, NoItinerary, !strconcat("cps", asm_ops),
[/* For disassembly only; pattern left blank */]>, Requires<[IsARM]> {
bits<2> imod;
bits<3> iflags;
bits<5> mode;
bit M;
let Inst{31-28} = 0b1111;
let Inst{27-20} = 0b00010000;
let Inst{19-18} = imod;
let Inst{17} = M; // Enabled if mode is set;
let Inst{16} = 0;
let Inst{8-6} = iflags;
let Inst{5} = 0;
let Inst{4-0} = mode;
}
let M = 1 in
def CPS3p : CPS<(ins imod_op:$imod, iflags_op:$iflags, i32imm:$mode),
"$imod\t$iflags, $mode">;
let mode = 0, M = 0 in
def CPS2p : CPS<(ins imod_op:$imod, iflags_op:$iflags), "$imod\t$iflags">;
let imod = 0, iflags = 0, M = 1 in
def CPS1p : CPS<(ins i32imm:$mode), "\t$mode">;
// Preload signals the memory system of possible future data/instruction access.
// These are for disassembly only.
multiclass APreLoad<bits<1> read, bits<1> data, string opc> {

View File

@ -266,21 +266,17 @@ def tSETENDLE : T1I<(outs), (ins), NoItinerary, "setend\tle",
}
// Change Processor State is a system instruction -- for disassembly only.
// The singleton $opt operand contains the following information:
//
// opt{4-0} = mode ==> don't care
// opt{5} = changemode ==> 0 (false for 16-bit Thumb instr)
// opt{8-6} = AIF from Inst{2-0}
// opt{10-9} = 1:imod from Inst{4} with 0b10 as enable and 0b11 as disable
//
// The opt{4-0} and opt{5} sub-fields are to accommodate 32-bit Thumb and ARM
// CPS which has more options.
def tCPS : T1I<(outs), (ins cps_opt:$opt), NoItinerary, "cps$opt",
def tCPS : T1I<(outs), (ins imod_op:$imod, iflags_op:$iflags),
NoItinerary, "cps$imod $iflags",
[/* For disassembly only; pattern left blank */]>,
T1Misc<0b0110011> {
// A8.6.38 & B6.1.1
bit imod;
bits<3> iflags;
let Inst{4} = imod;
let Inst{3} = 0;
// FIXME: Finish encoding.
let Inst{2-0} = iflags;
}
// For both thumb1 and thumb2.

View File

@ -3127,41 +3127,40 @@ def t2BXJ : T2I<(outs), (ins rGPR:$func), NoItinerary, "bxj", "\t$func",
let Inst{19-16} = func;
}
// Change Processor State is a system instruction -- for disassembly only.
// The singleton $opt operand contains the following information:
// opt{4-0} = mode from Inst{4-0}
// opt{5} = changemode from Inst{17}
// opt{8-6} = AIF from Inst{8-6}
// opt{10-9} = imod from Inst{19-18} with 0b10 as enable and 0b11 as disable
def t2CPS : T2XI<(outs),(ins cps_opt:$opt), NoItinerary, "cps$opt",
// Change Processor State is a system instruction -- for disassembly and
// parsing only.
// FIXME: Since the asm parser has currently no clean way to handle optional
// operands, create 3 versions of the same instruction. Once there's a clean
// framework to represent optional operands, change this behavior.
class t2CPS<dag iops, string asm_op> : T2XI<(outs), iops, NoItinerary,
!strconcat("cps", asm_op),
[/* For disassembly only; pattern left blank */]> {
bits<2> imod;
bits<3> iflags;
bits<5> mode;
bit M;
let Inst{31-27} = 0b11110;
let Inst{26} = 0;
let Inst{25-20} = 0b111010;
let Inst{19-16} = 0b1111;
let Inst{15-14} = 0b10;
let Inst{12} = 0;
bits<11> opt;
// mode number
let Inst{4-0} = opt{4-0};
// M flag
let Inst{8} = opt{5};
// F flag
let Inst{5} = opt{6};
// I flag
let Inst{6} = opt{7};
// A flag
let Inst{7} = opt{8};
// imod flag
let Inst{10-9} = opt{10-9};
let Inst{10-9} = imod;
let Inst{8} = M;
let Inst{7-5} = iflags;
let Inst{4-0} = mode;
}
let M = 1 in
def t2CPS3p : t2CPS<(ins imod_op:$imod, iflags_op:$iflags, i32imm:$mode),
"$imod.w\t$iflags, $mode">;
let mode = 0, M = 0 in
def t2CPS2p : t2CPS<(ins imod_op:$imod, iflags_op:$iflags),
"$imod.w\t$iflags">;
let imod = 0, iflags = 0, M = 1 in
def t2CPS1p : t2CPS<(ins i32imm:$mode), "\t$mode">;
// A6.3.4 Branches and miscellaneous control
// Table A6-14 Change Processor State, and hint instructions
// Helper class for disassembly only.

View File

@ -98,6 +98,8 @@ class ARMAsmParser : public TargetAsmParser {
SmallVectorImpl<MCParsedAsmOperand*>&);
OperandMatchResultTy tryParseMemBarrierOptOperand(
SmallVectorImpl<MCParsedAsmOperand*> &);
OperandMatchResultTy tryParseProcIFlagsOperand(
SmallVectorImpl<MCParsedAsmOperand*> &);
public:
ARMAsmParser(const Target &T, MCAsmParser &_Parser, TargetMachine &_TM)
@ -126,6 +128,7 @@ class ARMOperand : public MCParsedAsmOperand {
Immediate,
MemBarrierOpt,
Memory,
ProcIFlags,
Register,
RegisterList,
DPRRegisterList,
@ -149,6 +152,10 @@ class ARMOperand : public MCParsedAsmOperand {
unsigned Val;
} Cop;
struct {
ARM_PROC::IFlags Val;
} IFlags;
struct {
const char *Data;
unsigned Length;
@ -215,6 +222,8 @@ public:
case Memory:
Mem = o.Mem;
break;
case ProcIFlags:
IFlags = o.IFlags;
}
}
@ -259,6 +268,11 @@ public:
return MBOpt.Val;
}
ARM_PROC::IFlags getProcIFlags() const {
assert(Kind == ProcIFlags && "Invalid access!");
return IFlags.Val;
}
/// @name Memory Operand Accessors
/// @{
@ -333,6 +347,7 @@ public:
uint64_t Value = CE->getValue();
return ((Value & 0x3) == 0 && Value <= 124);
}
bool isProcIFlags() const { return Kind == ProcIFlags; }
void addExpr(MCInst &Inst, const MCExpr *Expr) const {
// Add as immediates when possible. Null MCExpr = 0.
@ -433,6 +448,11 @@ public:
Inst.addOperand(MCOperand::CreateImm(CE->getValue()));
}
void addProcIFlagsOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
Inst.addOperand(MCOperand::CreateImm(unsigned(getProcIFlags())));
}
virtual void dump(raw_ostream &OS) const;
static ARMOperand *CreateCondCode(ARMCC::CondCodes CC, SMLoc S) {
@ -556,6 +576,14 @@ public:
Op->EndLoc = S;
return Op;
}
static ARMOperand *CreateProcIFlags(ARM_PROC::IFlags IFlags, SMLoc S) {
ARMOperand *Op = new ARMOperand(ProcIFlags);
Op->IFlags.Val = IFlags;
Op->StartLoc = S;
Op->EndLoc = S;
return Op;
}
};
} // end anonymous namespace.
@ -604,6 +632,15 @@ void ARMOperand::dump(raw_ostream &OS) const {
OS << " (writeback)";
OS << ">";
break;
case ProcIFlags: {
OS << "<ARM_PROC::";
unsigned IFlags = getProcIFlags();
for (int i=2; i >= 0; --i)
if (IFlags & (1 << i))
OS << ARM_PROC::IFlagsToString(1 << i);
OS << ">";
break;
}
case Register:
OS << "<register " << getReg() << ">";
break;
@ -885,6 +922,35 @@ tryParseMemBarrierOptOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands) {
return MatchOperand_Success;
}
/// ParseProcIFlagsOperand - Try to parse iflags from CPS instruction.
ARMAsmParser::OperandMatchResultTy ARMAsmParser::
tryParseProcIFlagsOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands) {
SMLoc S = Parser.getTok().getLoc();
const AsmToken &Tok = Parser.getTok();
assert(Tok.is(AsmToken::Identifier) && "Token is not an Identifier");
StringRef IFlagsStr = Tok.getString();
unsigned IFlags = 0;
for (int i = 0, e = IFlagsStr.size(); i != e; ++i) {
unsigned Flag = StringSwitch<unsigned>(IFlagsStr.substr(i, 1))
.Case("a", ARM_PROC::A)
.Case("i", ARM_PROC::I)
.Case("f", ARM_PROC::F)
.Default(~0U);
// If some specific iflag is already set, it means that some letter is
// present more than once, this is not acceptable.
if (Flag == ~0U || (IFlags & Flag))
return MatchOperand_NoMatch;
IFlags |= Flag;
}
Parser.Lex(); // Eat identifier token.
Operands.push_back(ARMOperand::CreateProcIFlags((ARM_PROC::IFlags)IFlags, S));
return MatchOperand_Success;
}
/// Parse an ARM memory expression, return false if successful else return true
/// or an error. The first token must be a '[' when called.
///
@ -1254,11 +1320,13 @@ ARMAsmParser::ApplyPrefixToExpr(const MCExpr *E,
/// setting letters to form a canonical mnemonic and flags.
//
// FIXME: Would be nice to autogen this.
static StringRef SplitMnemonicAndCC(StringRef Mnemonic,
static StringRef SplitMnemonic(StringRef Mnemonic,
unsigned &PredicationCode,
bool &CarrySetting) {
bool &CarrySetting,
unsigned &ProcessorIMod) {
PredicationCode = ARMCC::AL;
CarrySetting = false;
ProcessorIMod = 0;
// Ignore some mnemonics we know aren't predicated forms.
//
@ -1312,6 +1380,21 @@ static StringRef SplitMnemonicAndCC(StringRef Mnemonic,
CarrySetting = true;
}
// The "cps" instruction can have a interrupt mode operand which is glued into
// the mnemonic. Check if this is the case, split it and parse the imod op
if (Mnemonic.startswith("cps")) {
// Split out any imod code.
unsigned IMod =
StringSwitch<unsigned>(Mnemonic.substr(Mnemonic.size()-2, 2))
.Case("ie", ARM_PROC::IE)
.Case("id", ARM_PROC::ID)
.Default(~0U);
if (IMod != ~0U) {
Mnemonic = Mnemonic.slice(0, Mnemonic.size()-2);
ProcessorIMod = IMod;
}
}
return Mnemonic;
}
@ -1342,7 +1425,7 @@ GetMnemonicAcceptInfo(StringRef Mnemonic, bool &CanAcceptCarrySet,
Mnemonic == "mcrr2" || Mnemonic == "cbz" || Mnemonic == "cdp2" ||
Mnemonic == "trap" || Mnemonic == "mrc2" || Mnemonic == "mrrc2" ||
Mnemonic == "dsb" || Mnemonic == "movs" || Mnemonic == "isb" ||
Mnemonic == "clrex") {
Mnemonic == "clrex" || Mnemonic.startswith("cps")) {
CanAcceptPredicationCode = false;
} else {
CanAcceptPredicationCode = true;
@ -1363,8 +1446,10 @@ bool ARMAsmParser::ParseInstruction(StringRef Name, SMLoc NameLoc,
// Split out the predication code and carry setting flag from the mnemonic.
unsigned PredicationCode;
unsigned ProcessorIMod;
bool CarrySetting;
Head = SplitMnemonicAndCC(Head, PredicationCode, CarrySetting);
Head = SplitMnemonic(Head, PredicationCode, CarrySetting,
ProcessorIMod);
Operands.push_back(ARMOperand::CreateToken(Head, NameLoc));
@ -1404,13 +1489,25 @@ bool ARMAsmParser::ParseInstruction(StringRef Name, SMLoc NameLoc,
// FIXME: Issue a nice error.
}
// Add the processor imod operand, if necessary.
if (ProcessorIMod) {
Operands.push_back(ARMOperand::CreateImm(
MCConstantExpr::Create(ProcessorIMod, getContext()),
NameLoc, NameLoc));
} else {
// This mnemonic can't ever accept a imod, but the user wrote
// one (or misspelled another mnemonic).
// FIXME: Issue a nice error.
}
// Add the remaining tokens in the mnemonic.
while (Next != StringRef::npos) {
Start = Next;
Next = Name.find('.', Start + 1);
Head = Name.slice(Start, Next);
StringRef ExtraToken = Name.slice(Start, Next);
Operands.push_back(ARMOperand::CreateToken(Head, NameLoc));
Operands.push_back(ARMOperand::CreateToken(ExtraToken, NameLoc));
}
// Read the remaining operands.

View File

@ -2942,15 +2942,25 @@ static bool DisassembleMiscFrm(MCInst &MI, unsigned Opcode, uint32_t insn,
return true;
}
// CPS has a singleton $opt operand that contains the following information:
// opt{4-0} = mode from Inst{4-0}
// opt{5} = changemode from Inst{17}
// opt{8-6} = AIF from Inst{8-6}
// opt{10-9} = imod from Inst{19-18} with 0b10 as enable and 0b11 as disable
if (Opcode == ARM::CPS) {
unsigned Option = slice(insn, 4, 0) | slice(insn, 17, 17) << 5 |
slice(insn, 8, 6) << 6 | slice(insn, 19, 18) << 9;
MI.addOperand(MCOperand::CreateImm(Option));
// FIXME: To enable correct asm parsing and disasm of CPS we need 3 different
// opcodes which match the same real instruction. This is needed since there's
// no current handling of optional arguments. Fix here when a better handling
// of optional arguments is implemented.
if (Opcode == ARM::CPS3p) {
MI.addOperand(MCOperand::CreateImm(slice(insn, 19, 18))); // imod
MI.addOperand(MCOperand::CreateImm(slice(insn, 8, 6))); // iflags
MI.addOperand(MCOperand::CreateImm(slice(insn, 4, 0))); // mode
NumOpsAdded = 3;
return true;
}
if (Opcode == ARM::CPS2p) {
MI.addOperand(MCOperand::CreateImm(slice(insn, 19, 18))); // imod
MI.addOperand(MCOperand::CreateImm(slice(insn, 8, 6))); // iflags
NumOpsAdded = 2;
return true;
}
if (Opcode == ARM::CPS1p) {
MI.addOperand(MCOperand::CreateImm(slice(insn, 4, 0))); // mode
NumOpsAdded = 1;
return true;
}

View File

@ -828,14 +828,13 @@ static bool DisassembleThumb1Misc(MCInst &MI, unsigned Opcode, uint32_t insn,
}
// CPS has a singleton $opt operand that contains the following information:
// opt{4-0} = don't care
// opt{5} = 0 (false)
// opt{8-6} = AIF from Inst{2-0}
// opt{10-9} = 1:imod from Inst{4} with 0b10 as enable and 0b11 as disable
// The first op would be 0b10 as enable and 0b11 as disable in regular ARM,
// but in Thumb it's is 0 as enable and 1 as disable. So map it to ARM's
// default one. The second get the AIF flags from Inst{2-0}.
if (Opcode == ARM::tCPS) {
unsigned Option = slice(insn, 2, 0) << 6 | slice(insn, 4, 4) << 9 | 1 << 10;
MI.addOperand(MCOperand::CreateImm(Option));
NumOpsAdded = 1;
MI.addOperand(MCOperand::CreateImm(2 + slice(insn, 4, 4)));
MI.addOperand(MCOperand::CreateImm(slice(insn, 2, 0)));
NumOpsAdded = 2;
return true;
}
@ -1659,15 +1658,25 @@ static bool DisassembleThumb2BrMiscCtrl(MCInst &MI, unsigned Opcode,
break;
}
// CPS has a singleton $opt operand that contains the following information:
// opt{4-0} = mode from Inst{4-0}
// opt{5} = changemode from Inst{8}
// opt{8-6} = AIF from Inst{7-5}
// opt{10-9} = imod from Inst{10-9} with 0b10 as enable and 0b11 as disable
if (Opcode == ARM::t2CPS) {
unsigned Option = slice(insn, 4, 0) | slice(insn, 8, 8) << 5 |
slice(insn, 7, 5) << 6 | slice(insn, 10, 9) << 9;
MI.addOperand(MCOperand::CreateImm(Option));
// FIXME: To enable correct asm parsing and disasm of CPS we need 3 different
// opcodes which match the same real instruction. This is needed since there's
// no current handling of optional arguments. Fix here when a better handling
// of optional arguments is implemented.
if (Opcode == ARM::t2CPS3p) {
MI.addOperand(MCOperand::CreateImm(slice(insn, 10, 9))); // imod
MI.addOperand(MCOperand::CreateImm(slice(insn, 7, 5))); // iflags
MI.addOperand(MCOperand::CreateImm(slice(insn, 4, 0))); // mode
NumOpsAdded = 3;
return true;
}
if (Opcode == ARM::t2CPS2p) {
MI.addOperand(MCOperand::CreateImm(slice(insn, 10, 9))); // imod
MI.addOperand(MCOperand::CreateImm(slice(insn, 7, 5))); // iflags
NumOpsAdded = 2;
return true;
}
if (Opcode == ARM::t2CPS1p) {
MI.addOperand(MCOperand::CreateImm(slice(insn, 4, 0))); // mode
NumOpsAdded = 1;
return true;
}

View File

@ -379,27 +379,19 @@ void ARMInstPrinter::printSetendOperand(const MCInst *MI, unsigned OpNum,
O << "le";
}
void ARMInstPrinter::printCPSOptionOperand(const MCInst *MI, unsigned OpNum,
void ARMInstPrinter::printCPSIMod(const MCInst *MI, unsigned OpNum,
raw_ostream &O) {
const MCOperand &Op = MI->getOperand(OpNum);
unsigned option = Op.getImm();
unsigned mode = option & 31;
bool changemode = option >> 5 & 1;
unsigned AIF = option >> 6 & 7;
unsigned imod = option >> 9 & 3;
if (imod == 2)
O << "ie";
else if (imod == 3)
O << "id";
O << '\t';
if (imod > 1) {
if (AIF & 4) O << 'a';
if (AIF & 2) O << 'i';
if (AIF & 1) O << 'f';
if (AIF > 0 && changemode) O << ", ";
O << ARM_PROC::IModToString(Op.getImm());
}
if (changemode)
O << '#' << mode;
void ARMInstPrinter::printCPSIFlag(const MCInst *MI, unsigned OpNum,
raw_ostream &O) {
const MCOperand &Op = MI->getOperand(OpNum);
unsigned IFlags = Op.getImm();
for (int i=2; i >= 0; --i)
if (IFlags & (1 << i))
O << ARM_PROC::IFlagsToString(1 << i);
}
void ARMInstPrinter::printMSRMaskOperand(const MCInst *MI, unsigned OpNum,

View File

@ -85,6 +85,8 @@ public:
raw_ostream &O);
void printSetendOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O);
void printCPSIMod(const MCInst *MI, unsigned OpNum, raw_ostream &O);
void printCPSIFlag(const MCInst *MI, unsigned OpNum, raw_ostream &O);
void printCPSOptionOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O);
void printMSRMaskOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O);
void printNegZeroOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O);

View File

@ -237,3 +237,12 @@
@ CHECK: dsb oshst @ encoding: [0x42,0xf0,0x7f,0xf5]
dsb oshst
@ CHECK: cpsie aif @ encoding: [0xc0,0x01,0x08,0xf1]
cpsie aif
@ CHECK: cps #15 @ encoding: [0x0f,0x00,0x02,0xf1]
cps #15
@ CHECK: cpsie if, #10 @ encoding: [0xca,0x00,0x0a,0xf1]
cpsie if, #10

View File

@ -65,3 +65,6 @@
@ CHECK: wfi @ encoding: [0x30,0xbf]
wfi
@ CHECK: cpsie aif @ encoding: [0x67,0xb6]
cpsie aif

View File

@ -252,3 +252,10 @@
@ CHECK: dsb oshst @ encoding: [0xbf,0xf3,0x42,0x8f]
dsb oshst
@ CHECK: cpsie.w aif @ encoding: [0xaf,0xf3,0xe0,0x84]
cpsie.w aif
@ CHECK: cps #15 @ encoding: [0xaf,0xf3,0x0f,0x81]
cps #15
@ CHECK: cpsie.w if, #10 @ encoding: [0xaf,0xf3,0x6a,0x85]
cpsie.w if, #10

View File

@ -118,3 +118,12 @@
# CHECK: setend le
0x00 0x00 0x01 0xf1
# CHECK: cpsie aif
0xc0 0x01 0x08 0xf1
# CHECK: cps #15
0x0f 0x00 0x02 0xf1
# CHECK: cpsie if, #10
0xca 0x00 0x0a 0xf1

View File

@ -103,3 +103,15 @@
# IT block end
# CHECK: rsbs r1, r2, #0
0x51 0x42
# CHECK: cpsid.w f
0xaf 0xf3 0x20 0x86
# CHECK: cps #15
0xaf 0xf3 0x0f 0x81
# CHECK: cpsie.w if, #10
0xaf 0xf3 0x6a 0x85
# CHECK: cpsie aif
0x67 0xb6

View File

@ -576,6 +576,8 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type,
IMM("nohash_imm");
IMM("p_imm");
IMM("c_imm");
IMM("imod_op");
IMM("iflags_op");
IMM("cpinst_operand");
IMM("setend_op");
IMM("cps_opt");