mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-08-08 19:25:47 +00:00
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:
@@ -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 {
|
namespace ARM_MB {
|
||||||
// The Memory Barrier Option constants map directly to the 4-bit encoding of
|
// The Memory Barrier Option constants map directly to the 4-bit encoding of
|
||||||
// the option field for memory barrier operations.
|
// the option field for memory barrier operations.
|
||||||
|
@@ -155,6 +155,22 @@ def MemBarrierOptOperand : AsmOperandClass {
|
|||||||
let ParserMethod = "tryParseMemBarrierOptOperand";
|
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
|
// ARM Predicate operand. Default to 14 = always (AL). Second part is CC
|
||||||
// register whose default is 0 (no register).
|
// register whose default is 0 (no register).
|
||||||
def pred : PredicateOperand<OtherVT, (ops i32imm, CCR),
|
def pred : PredicateOperand<OtherVT, (ops i32imm, CCR),
|
||||||
|
@@ -1102,22 +1102,38 @@ def BKPT : AI<(outs), (ins i32imm:$val), MiscFrm, NoItinerary, "bkpt", "\t$val",
|
|||||||
let Inst{7-4} = 0b0111;
|
let Inst{7-4} = 0b0111;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change Processor State is a system instruction -- for disassembly only.
|
// Change Processor State is a system instruction -- for disassembly and
|
||||||
// The singleton $opt operand contains the following information:
|
// parsing only.
|
||||||
// opt{4-0} = mode from Inst{4-0}
|
// FIXME: Since the asm parser has currently no clean way to handle optional
|
||||||
// opt{5} = changemode from Inst{17}
|
// operands, create 3 versions of the same instruction. Once there's a clean
|
||||||
// opt{8-6} = AIF from Inst{8-6}
|
// framework to represent optional operands, change this behavior.
|
||||||
// opt{10-9} = imod from Inst{19-18} with 0b10 as enable and 0b11 as disable
|
class CPS<dag iops, string asm_ops>
|
||||||
// FIXME: Integrated assembler will need these split out.
|
: AXI<(outs), iops, MiscFrm, NoItinerary, !strconcat("cps", asm_ops),
|
||||||
def CPS : AXI<(outs), (ins cps_opt:$opt), MiscFrm, NoItinerary, "cps$opt",
|
[/* For disassembly only; pattern left blank */]>, Requires<[IsARM]> {
|
||||||
[/* For disassembly only; pattern left blank */]>,
|
bits<2> imod;
|
||||||
Requires<[IsARM]> {
|
bits<3> iflags;
|
||||||
|
bits<5> mode;
|
||||||
|
bit M;
|
||||||
|
|
||||||
let Inst{31-28} = 0b1111;
|
let Inst{31-28} = 0b1111;
|
||||||
let Inst{27-20} = 0b00010000;
|
let Inst{27-20} = 0b00010000;
|
||||||
let Inst{16} = 0;
|
let Inst{19-18} = imod;
|
||||||
let Inst{5} = 0;
|
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.
|
// Preload signals the memory system of possible future data/instruction access.
|
||||||
// These are for disassembly only.
|
// These are for disassembly only.
|
||||||
multiclass APreLoad<bits<1> read, bits<1> data, string opc> {
|
multiclass APreLoad<bits<1> read, bits<1> data, string opc> {
|
||||||
|
@@ -266,21 +266,17 @@ def tSETENDLE : T1I<(outs), (ins), NoItinerary, "setend\tle",
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Change Processor State is a system instruction -- for disassembly only.
|
// Change Processor State is a system instruction -- for disassembly only.
|
||||||
// The singleton $opt operand contains the following information:
|
def tCPS : T1I<(outs), (ins imod_op:$imod, iflags_op:$iflags),
|
||||||
//
|
NoItinerary, "cps$imod $iflags",
|
||||||
// opt{4-0} = mode ==> don't care
|
[/* For disassembly only; pattern left blank */]>,
|
||||||
// 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",
|
|
||||||
[/* For disassembly only; pattern left blank */]>,
|
|
||||||
T1Misc<0b0110011> {
|
T1Misc<0b0110011> {
|
||||||
// A8.6.38 & B6.1.1
|
// A8.6.38 & B6.1.1
|
||||||
let Inst{3} = 0;
|
bit imod;
|
||||||
// FIXME: Finish encoding.
|
bits<3> iflags;
|
||||||
|
|
||||||
|
let Inst{4} = imod;
|
||||||
|
let Inst{3} = 0;
|
||||||
|
let Inst{2-0} = iflags;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For both thumb1 and thumb2.
|
// For both thumb1 and thumb2.
|
||||||
|
@@ -3127,41 +3127,40 @@ def t2BXJ : T2I<(outs), (ins rGPR:$func), NoItinerary, "bxj", "\t$func",
|
|||||||
let Inst{19-16} = func;
|
let Inst{19-16} = func;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change Processor State is a system instruction -- for disassembly only.
|
// Change Processor State is a system instruction -- for disassembly and
|
||||||
// The singleton $opt operand contains the following information:
|
// parsing only.
|
||||||
// opt{4-0} = mode from Inst{4-0}
|
// FIXME: Since the asm parser has currently no clean way to handle optional
|
||||||
// opt{5} = changemode from Inst{17}
|
// operands, create 3 versions of the same instruction. Once there's a clean
|
||||||
// opt{8-6} = AIF from Inst{8-6}
|
// framework to represent optional operands, change this behavior.
|
||||||
// opt{10-9} = imod from Inst{19-18} with 0b10 as enable and 0b11 as disable
|
class t2CPS<dag iops, string asm_op> : T2XI<(outs), iops, NoItinerary,
|
||||||
def t2CPS : T2XI<(outs),(ins cps_opt:$opt), NoItinerary, "cps$opt",
|
!strconcat("cps", asm_op),
|
||||||
[/* For disassembly only; pattern left blank */]> {
|
[/* For disassembly only; pattern left blank */]> {
|
||||||
|
bits<2> imod;
|
||||||
|
bits<3> iflags;
|
||||||
|
bits<5> mode;
|
||||||
|
bit M;
|
||||||
|
|
||||||
let Inst{31-27} = 0b11110;
|
let Inst{31-27} = 0b11110;
|
||||||
let Inst{26} = 0;
|
let Inst{26} = 0;
|
||||||
let Inst{25-20} = 0b111010;
|
let Inst{25-20} = 0b111010;
|
||||||
|
let Inst{19-16} = 0b1111;
|
||||||
let Inst{15-14} = 0b10;
|
let Inst{15-14} = 0b10;
|
||||||
let Inst{12} = 0;
|
let Inst{12} = 0;
|
||||||
|
let Inst{10-9} = imod;
|
||||||
bits<11> opt;
|
let Inst{8} = M;
|
||||||
|
let Inst{7-5} = iflags;
|
||||||
// mode number
|
let Inst{4-0} = mode;
|
||||||
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 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
|
// A6.3.4 Branches and miscellaneous control
|
||||||
// Table A6-14 Change Processor State, and hint instructions
|
// Table A6-14 Change Processor State, and hint instructions
|
||||||
// Helper class for disassembly only.
|
// Helper class for disassembly only.
|
||||||
|
@@ -98,6 +98,8 @@ class ARMAsmParser : public TargetAsmParser {
|
|||||||
SmallVectorImpl<MCParsedAsmOperand*>&);
|
SmallVectorImpl<MCParsedAsmOperand*>&);
|
||||||
OperandMatchResultTy tryParseMemBarrierOptOperand(
|
OperandMatchResultTy tryParseMemBarrierOptOperand(
|
||||||
SmallVectorImpl<MCParsedAsmOperand*> &);
|
SmallVectorImpl<MCParsedAsmOperand*> &);
|
||||||
|
OperandMatchResultTy tryParseProcIFlagsOperand(
|
||||||
|
SmallVectorImpl<MCParsedAsmOperand*> &);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ARMAsmParser(const Target &T, MCAsmParser &_Parser, TargetMachine &_TM)
|
ARMAsmParser(const Target &T, MCAsmParser &_Parser, TargetMachine &_TM)
|
||||||
@@ -126,6 +128,7 @@ class ARMOperand : public MCParsedAsmOperand {
|
|||||||
Immediate,
|
Immediate,
|
||||||
MemBarrierOpt,
|
MemBarrierOpt,
|
||||||
Memory,
|
Memory,
|
||||||
|
ProcIFlags,
|
||||||
Register,
|
Register,
|
||||||
RegisterList,
|
RegisterList,
|
||||||
DPRRegisterList,
|
DPRRegisterList,
|
||||||
@@ -149,6 +152,10 @@ class ARMOperand : public MCParsedAsmOperand {
|
|||||||
unsigned Val;
|
unsigned Val;
|
||||||
} Cop;
|
} Cop;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
ARM_PROC::IFlags Val;
|
||||||
|
} IFlags;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
const char *Data;
|
const char *Data;
|
||||||
unsigned Length;
|
unsigned Length;
|
||||||
@@ -215,6 +222,8 @@ public:
|
|||||||
case Memory:
|
case Memory:
|
||||||
Mem = o.Mem;
|
Mem = o.Mem;
|
||||||
break;
|
break;
|
||||||
|
case ProcIFlags:
|
||||||
|
IFlags = o.IFlags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +268,11 @@ public:
|
|||||||
return MBOpt.Val;
|
return MBOpt.Val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ARM_PROC::IFlags getProcIFlags() const {
|
||||||
|
assert(Kind == ProcIFlags && "Invalid access!");
|
||||||
|
return IFlags.Val;
|
||||||
|
}
|
||||||
|
|
||||||
/// @name Memory Operand Accessors
|
/// @name Memory Operand Accessors
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
@@ -333,6 +347,7 @@ public:
|
|||||||
uint64_t Value = CE->getValue();
|
uint64_t Value = CE->getValue();
|
||||||
return ((Value & 0x3) == 0 && Value <= 124);
|
return ((Value & 0x3) == 0 && Value <= 124);
|
||||||
}
|
}
|
||||||
|
bool isProcIFlags() const { return Kind == ProcIFlags; }
|
||||||
|
|
||||||
void addExpr(MCInst &Inst, const MCExpr *Expr) const {
|
void addExpr(MCInst &Inst, const MCExpr *Expr) const {
|
||||||
// Add as immediates when possible. Null MCExpr = 0.
|
// Add as immediates when possible. Null MCExpr = 0.
|
||||||
@@ -433,6 +448,11 @@ public:
|
|||||||
Inst.addOperand(MCOperand::CreateImm(CE->getValue()));
|
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;
|
virtual void dump(raw_ostream &OS) const;
|
||||||
|
|
||||||
static ARMOperand *CreateCondCode(ARMCC::CondCodes CC, SMLoc S) {
|
static ARMOperand *CreateCondCode(ARMCC::CondCodes CC, SMLoc S) {
|
||||||
@@ -556,6 +576,14 @@ public:
|
|||||||
Op->EndLoc = S;
|
Op->EndLoc = S;
|
||||||
return Op;
|
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.
|
} // end anonymous namespace.
|
||||||
@@ -604,6 +632,15 @@ void ARMOperand::dump(raw_ostream &OS) const {
|
|||||||
OS << " (writeback)";
|
OS << " (writeback)";
|
||||||
OS << ">";
|
OS << ">";
|
||||||
break;
|
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:
|
case Register:
|
||||||
OS << "<register " << getReg() << ">";
|
OS << "<register " << getReg() << ">";
|
||||||
break;
|
break;
|
||||||
@@ -885,6 +922,35 @@ tryParseMemBarrierOptOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands) {
|
|||||||
return MatchOperand_Success;
|
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
|
/// Parse an ARM memory expression, return false if successful else return true
|
||||||
/// or an error. The first token must be a '[' when called.
|
/// 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.
|
/// setting letters to form a canonical mnemonic and flags.
|
||||||
//
|
//
|
||||||
// FIXME: Would be nice to autogen this.
|
// FIXME: Would be nice to autogen this.
|
||||||
static StringRef SplitMnemonicAndCC(StringRef Mnemonic,
|
static StringRef SplitMnemonic(StringRef Mnemonic,
|
||||||
unsigned &PredicationCode,
|
unsigned &PredicationCode,
|
||||||
bool &CarrySetting) {
|
bool &CarrySetting,
|
||||||
|
unsigned &ProcessorIMod) {
|
||||||
PredicationCode = ARMCC::AL;
|
PredicationCode = ARMCC::AL;
|
||||||
CarrySetting = false;
|
CarrySetting = false;
|
||||||
|
ProcessorIMod = 0;
|
||||||
|
|
||||||
// Ignore some mnemonics we know aren't predicated forms.
|
// Ignore some mnemonics we know aren't predicated forms.
|
||||||
//
|
//
|
||||||
@@ -1312,6 +1380,21 @@ static StringRef SplitMnemonicAndCC(StringRef Mnemonic,
|
|||||||
CarrySetting = true;
|
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;
|
return Mnemonic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1342,7 +1425,7 @@ GetMnemonicAcceptInfo(StringRef Mnemonic, bool &CanAcceptCarrySet,
|
|||||||
Mnemonic == "mcrr2" || Mnemonic == "cbz" || Mnemonic == "cdp2" ||
|
Mnemonic == "mcrr2" || Mnemonic == "cbz" || Mnemonic == "cdp2" ||
|
||||||
Mnemonic == "trap" || Mnemonic == "mrc2" || Mnemonic == "mrrc2" ||
|
Mnemonic == "trap" || Mnemonic == "mrc2" || Mnemonic == "mrrc2" ||
|
||||||
Mnemonic == "dsb" || Mnemonic == "movs" || Mnemonic == "isb" ||
|
Mnemonic == "dsb" || Mnemonic == "movs" || Mnemonic == "isb" ||
|
||||||
Mnemonic == "clrex") {
|
Mnemonic == "clrex" || Mnemonic.startswith("cps")) {
|
||||||
CanAcceptPredicationCode = false;
|
CanAcceptPredicationCode = false;
|
||||||
} else {
|
} else {
|
||||||
CanAcceptPredicationCode = true;
|
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.
|
// Split out the predication code and carry setting flag from the mnemonic.
|
||||||
unsigned PredicationCode;
|
unsigned PredicationCode;
|
||||||
|
unsigned ProcessorIMod;
|
||||||
bool CarrySetting;
|
bool CarrySetting;
|
||||||
Head = SplitMnemonicAndCC(Head, PredicationCode, CarrySetting);
|
Head = SplitMnemonic(Head, PredicationCode, CarrySetting,
|
||||||
|
ProcessorIMod);
|
||||||
|
|
||||||
Operands.push_back(ARMOperand::CreateToken(Head, NameLoc));
|
Operands.push_back(ARMOperand::CreateToken(Head, NameLoc));
|
||||||
|
|
||||||
@@ -1404,13 +1489,25 @@ bool ARMAsmParser::ParseInstruction(StringRef Name, SMLoc NameLoc,
|
|||||||
// FIXME: Issue a nice error.
|
// 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.
|
// Add the remaining tokens in the mnemonic.
|
||||||
while (Next != StringRef::npos) {
|
while (Next != StringRef::npos) {
|
||||||
Start = Next;
|
Start = Next;
|
||||||
Next = Name.find('.', Start + 1);
|
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.
|
// Read the remaining operands.
|
||||||
|
@@ -2942,15 +2942,25 @@ static bool DisassembleMiscFrm(MCInst &MI, unsigned Opcode, uint32_t insn,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPS has a singleton $opt operand that contains the following information:
|
// FIXME: To enable correct asm parsing and disasm of CPS we need 3 different
|
||||||
// opt{4-0} = mode from Inst{4-0}
|
// opcodes which match the same real instruction. This is needed since there's
|
||||||
// opt{5} = changemode from Inst{17}
|
// no current handling of optional arguments. Fix here when a better handling
|
||||||
// opt{8-6} = AIF from Inst{8-6}
|
// of optional arguments is implemented.
|
||||||
// opt{10-9} = imod from Inst{19-18} with 0b10 as enable and 0b11 as disable
|
if (Opcode == ARM::CPS3p) {
|
||||||
if (Opcode == ARM::CPS) {
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 19, 18))); // imod
|
||||||
unsigned Option = slice(insn, 4, 0) | slice(insn, 17, 17) << 5 |
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 8, 6))); // iflags
|
||||||
slice(insn, 8, 6) << 6 | slice(insn, 19, 18) << 9;
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 4, 0))); // mode
|
||||||
MI.addOperand(MCOperand::CreateImm(Option));
|
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;
|
NumOpsAdded = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -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:
|
// CPS has a singleton $opt operand that contains the following information:
|
||||||
// opt{4-0} = don't care
|
// The first op would be 0b10 as enable and 0b11 as disable in regular ARM,
|
||||||
// opt{5} = 0 (false)
|
// but in Thumb it's is 0 as enable and 1 as disable. So map it to ARM's
|
||||||
// opt{8-6} = AIF from Inst{2-0}
|
// default one. The second get the AIF flags from Inst{2-0}.
|
||||||
// opt{10-9} = 1:imod from Inst{4} with 0b10 as enable and 0b11 as disable
|
|
||||||
if (Opcode == ARM::tCPS) {
|
if (Opcode == ARM::tCPS) {
|
||||||
unsigned Option = slice(insn, 2, 0) << 6 | slice(insn, 4, 4) << 9 | 1 << 10;
|
MI.addOperand(MCOperand::CreateImm(2 + slice(insn, 4, 4)));
|
||||||
MI.addOperand(MCOperand::CreateImm(Option));
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 2, 0)));
|
||||||
NumOpsAdded = 1;
|
NumOpsAdded = 2;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1659,15 +1658,25 @@ static bool DisassembleThumb2BrMiscCtrl(MCInst &MI, unsigned Opcode,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPS has a singleton $opt operand that contains the following information:
|
// FIXME: To enable correct asm parsing and disasm of CPS we need 3 different
|
||||||
// opt{4-0} = mode from Inst{4-0}
|
// opcodes which match the same real instruction. This is needed since there's
|
||||||
// opt{5} = changemode from Inst{8}
|
// no current handling of optional arguments. Fix here when a better handling
|
||||||
// opt{8-6} = AIF from Inst{7-5}
|
// of optional arguments is implemented.
|
||||||
// opt{10-9} = imod from Inst{10-9} with 0b10 as enable and 0b11 as disable
|
if (Opcode == ARM::t2CPS3p) {
|
||||||
if (Opcode == ARM::t2CPS) {
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 10, 9))); // imod
|
||||||
unsigned Option = slice(insn, 4, 0) | slice(insn, 8, 8) << 5 |
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 7, 5))); // iflags
|
||||||
slice(insn, 7, 5) << 6 | slice(insn, 10, 9) << 9;
|
MI.addOperand(MCOperand::CreateImm(slice(insn, 4, 0))); // mode
|
||||||
MI.addOperand(MCOperand::CreateImm(Option));
|
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;
|
NumOpsAdded = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -379,27 +379,19 @@ void ARMInstPrinter::printSetendOperand(const MCInst *MI, unsigned OpNum,
|
|||||||
O << "le";
|
O << "le";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMInstPrinter::printCPSOptionOperand(const MCInst *MI, unsigned OpNum,
|
void ARMInstPrinter::printCPSIMod(const MCInst *MI, unsigned OpNum,
|
||||||
raw_ostream &O) {
|
raw_ostream &O) {
|
||||||
const MCOperand &Op = MI->getOperand(OpNum);
|
const MCOperand &Op = MI->getOperand(OpNum);
|
||||||
unsigned option = Op.getImm();
|
O << ARM_PROC::IModToString(Op.getImm());
|
||||||
unsigned mode = option & 31;
|
}
|
||||||
bool changemode = option >> 5 & 1;
|
|
||||||
unsigned AIF = option >> 6 & 7;
|
void ARMInstPrinter::printCPSIFlag(const MCInst *MI, unsigned OpNum,
|
||||||
unsigned imod = option >> 9 & 3;
|
raw_ostream &O) {
|
||||||
if (imod == 2)
|
const MCOperand &Op = MI->getOperand(OpNum);
|
||||||
O << "ie";
|
unsigned IFlags = Op.getImm();
|
||||||
else if (imod == 3)
|
for (int i=2; i >= 0; --i)
|
||||||
O << "id";
|
if (IFlags & (1 << i))
|
||||||
O << '\t';
|
O << ARM_PROC::IFlagsToString(1 << i);
|
||||||
if (imod > 1) {
|
|
||||||
if (AIF & 4) O << 'a';
|
|
||||||
if (AIF & 2) O << 'i';
|
|
||||||
if (AIF & 1) O << 'f';
|
|
||||||
if (AIF > 0 && changemode) O << ", ";
|
|
||||||
}
|
|
||||||
if (changemode)
|
|
||||||
O << '#' << mode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMInstPrinter::printMSRMaskOperand(const MCInst *MI, unsigned OpNum,
|
void ARMInstPrinter::printMSRMaskOperand(const MCInst *MI, unsigned OpNum,
|
||||||
|
@@ -85,6 +85,8 @@ public:
|
|||||||
raw_ostream &O);
|
raw_ostream &O);
|
||||||
|
|
||||||
void printSetendOperand(const MCInst *MI, unsigned OpNum, 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 printCPSOptionOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O);
|
||||||
void printMSRMaskOperand(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);
|
void printNegZeroOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O);
|
||||||
|
@@ -237,3 +237,12 @@
|
|||||||
@ CHECK: dsb oshst @ encoding: [0x42,0xf0,0x7f,0xf5]
|
@ CHECK: dsb oshst @ encoding: [0x42,0xf0,0x7f,0xf5]
|
||||||
dsb oshst
|
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
|
||||||
|
|
||||||
|
@@ -65,3 +65,6 @@
|
|||||||
|
|
||||||
@ CHECK: wfi @ encoding: [0x30,0xbf]
|
@ CHECK: wfi @ encoding: [0x30,0xbf]
|
||||||
wfi
|
wfi
|
||||||
|
|
||||||
|
@ CHECK: cpsie aif @ encoding: [0x67,0xb6]
|
||||||
|
cpsie aif
|
||||||
|
@@ -252,3 +252,10 @@
|
|||||||
@ CHECK: dsb oshst @ encoding: [0xbf,0xf3,0x42,0x8f]
|
@ CHECK: dsb oshst @ encoding: [0xbf,0xf3,0x42,0x8f]
|
||||||
dsb oshst
|
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
|
||||||
|
|
||||||
|
@@ -118,3 +118,12 @@
|
|||||||
|
|
||||||
# CHECK: setend le
|
# CHECK: setend le
|
||||||
0x00 0x00 0x01 0xf1
|
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
|
||||||
|
@@ -103,3 +103,15 @@
|
|||||||
# IT block end
|
# IT block end
|
||||||
# CHECK: rsbs r1, r2, #0
|
# CHECK: rsbs r1, r2, #0
|
||||||
0x51 0x42
|
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
|
||||||
|
@@ -576,6 +576,8 @@ static int ARMFlagFromOpName(LiteralConstantEmitter *type,
|
|||||||
IMM("nohash_imm");
|
IMM("nohash_imm");
|
||||||
IMM("p_imm");
|
IMM("p_imm");
|
||||||
IMM("c_imm");
|
IMM("c_imm");
|
||||||
|
IMM("imod_op");
|
||||||
|
IMM("iflags_op");
|
||||||
IMM("cpinst_operand");
|
IMM("cpinst_operand");
|
||||||
IMM("setend_op");
|
IMM("setend_op");
|
||||||
IMM("cps_opt");
|
IMM("cps_opt");
|
||||||
|
Reference in New Issue
Block a user