mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-06-28 06:24:57 +00:00
Flesh out ARM Parser support for shifted-register operands.
Now works for parsing register shifted register and register shifted immediate arithmetic instructions, including the 'rrx' rotate with extend. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@135049 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@ -412,14 +412,20 @@ def shift_imm : Operand<i32> {
|
|||||||
let ParserMatchClass = ShifterAsmOperand;
|
let ParserMatchClass = ShifterAsmOperand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def ShiftedRegAsmOperand : AsmOperandClass {
|
||||||
|
let Name = "ShiftedReg";
|
||||||
|
}
|
||||||
|
|
||||||
// shifter_operand operands: so_reg and so_imm.
|
// shifter_operand operands: so_reg and so_imm.
|
||||||
def so_reg : Operand<i32>, // reg reg imm
|
def so_reg : Operand<i32>, // reg reg imm
|
||||||
ComplexPattern<i32, 3, "SelectShifterOperandReg",
|
ComplexPattern<i32, 3, "SelectShifterOperandReg",
|
||||||
[shl,srl,sra,rotr]> {
|
[shl,srl,sra,rotr]> {
|
||||||
let EncoderMethod = "getSORegOpValue";
|
let EncoderMethod = "getSORegOpValue";
|
||||||
let PrintMethod = "printSORegOperand";
|
let PrintMethod = "printSORegOperand";
|
||||||
|
let ParserMatchClass = ShiftedRegAsmOperand;
|
||||||
let MIOperandInfo = (ops GPR, GPR, shift_imm);
|
let MIOperandInfo = (ops GPR, GPR, shift_imm);
|
||||||
}
|
}
|
||||||
|
// FIXME: Does this need to be distinct from so_reg?
|
||||||
def shift_so_reg : Operand<i32>, // reg reg imm
|
def shift_so_reg : Operand<i32>, // reg reg imm
|
||||||
ComplexPattern<i32, 3, "SelectShiftShifterOperandReg",
|
ComplexPattern<i32, 3, "SelectShiftShifterOperandReg",
|
||||||
[shl,srl,sra,rotr]> {
|
[shl,srl,sra,rotr]> {
|
||||||
|
@ -164,6 +164,7 @@ class ARMOperand : public MCParsedAsmOperand {
|
|||||||
RegisterList,
|
RegisterList,
|
||||||
DPRRegisterList,
|
DPRRegisterList,
|
||||||
SPRRegisterList,
|
SPRRegisterList,
|
||||||
|
ShiftedRegister,
|
||||||
Shifter,
|
Shifter,
|
||||||
Token
|
Token
|
||||||
} Kind;
|
} Kind;
|
||||||
@ -225,8 +226,14 @@ class ARMOperand : public MCParsedAsmOperand {
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
ARM_AM::ShiftOpc ShiftTy;
|
ARM_AM::ShiftOpc ShiftTy;
|
||||||
unsigned RegNum;
|
unsigned Imm;
|
||||||
} Shift;
|
} Shift;
|
||||||
|
struct {
|
||||||
|
ARM_AM::ShiftOpc ShiftTy;
|
||||||
|
unsigned SrcReg;
|
||||||
|
unsigned ShiftReg;
|
||||||
|
unsigned ShiftImm;
|
||||||
|
} ShiftedReg;
|
||||||
};
|
};
|
||||||
|
|
||||||
ARMOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {}
|
ARMOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {}
|
||||||
@ -273,6 +280,9 @@ public:
|
|||||||
case Shifter:
|
case Shifter:
|
||||||
Shift = o.Shift;
|
Shift = o.Shift;
|
||||||
break;
|
break;
|
||||||
|
case ShiftedRegister:
|
||||||
|
ShiftedReg = o.ShiftedReg;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,6 +402,7 @@ public:
|
|||||||
bool isMemBarrierOpt() const { return Kind == MemBarrierOpt; }
|
bool isMemBarrierOpt() const { return Kind == MemBarrierOpt; }
|
||||||
bool isMemory() const { return Kind == Memory; }
|
bool isMemory() const { return Kind == Memory; }
|
||||||
bool isShifter() const { return Kind == Shifter; }
|
bool isShifter() const { return Kind == Shifter; }
|
||||||
|
bool isShiftedReg() const { return Kind == ShiftedRegister; }
|
||||||
bool isMemMode2() const {
|
bool isMemMode2() const {
|
||||||
if (getMemAddrMode() != ARMII::AddrMode2)
|
if (getMemAddrMode() != ARMII::AddrMode2)
|
||||||
return false;
|
return false;
|
||||||
@ -522,6 +533,18 @@ public:
|
|||||||
Inst.addOperand(MCOperand::CreateReg(getReg()));
|
Inst.addOperand(MCOperand::CreateReg(getReg()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addShiftedRegOperands(MCInst &Inst, unsigned N) const {
|
||||||
|
assert(N == 3 && "Invalid number of operands!");
|
||||||
|
assert(isShiftedReg() && "addShiftedRegOperands() on non ShiftedReg!");
|
||||||
|
assert((ShiftedReg.ShiftReg == 0 ||
|
||||||
|
ARM_AM::getSORegOffset(ShiftedReg.ShiftImm) == 0) &&
|
||||||
|
"Invalid shifted register operand!");
|
||||||
|
Inst.addOperand(MCOperand::CreateReg(ShiftedReg.SrcReg));
|
||||||
|
Inst.addOperand(MCOperand::CreateReg(ShiftedReg.ShiftReg));
|
||||||
|
Inst.addOperand(MCOperand::CreateImm(
|
||||||
|
ARM_AM::getSORegOpc(ShiftedReg.ShiftTy, ShiftedReg.ShiftImm)));
|
||||||
|
}
|
||||||
|
|
||||||
void addShifterOperands(MCInst &Inst, unsigned N) const {
|
void addShifterOperands(MCInst &Inst, unsigned N) const {
|
||||||
assert(N == 1 && "Invalid number of operands!");
|
assert(N == 1 && "Invalid number of operands!");
|
||||||
Inst.addOperand(MCOperand::CreateImm(
|
Inst.addOperand(MCOperand::CreateImm(
|
||||||
@ -743,6 +766,21 @@ public:
|
|||||||
return Op;
|
return Op;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ARMOperand *CreateShiftedRegister(ARM_AM::ShiftOpc ShTy,
|
||||||
|
unsigned SrcReg,
|
||||||
|
unsigned ShiftReg,
|
||||||
|
unsigned ShiftImm,
|
||||||
|
SMLoc S, SMLoc E) {
|
||||||
|
ARMOperand *Op = new ARMOperand(ShiftedRegister);
|
||||||
|
Op->ShiftedReg.ShiftTy = ShTy;
|
||||||
|
Op->ShiftedReg.SrcReg = SrcReg;
|
||||||
|
Op->ShiftedReg.ShiftReg = ShiftReg;
|
||||||
|
Op->ShiftedReg.ShiftImm = ShiftImm;
|
||||||
|
Op->StartLoc = S;
|
||||||
|
Op->EndLoc = E;
|
||||||
|
return Op;
|
||||||
|
}
|
||||||
|
|
||||||
static ARMOperand *CreateShifter(ARM_AM::ShiftOpc ShTy,
|
static ARMOperand *CreateShifter(ARM_AM::ShiftOpc ShTy,
|
||||||
SMLoc S, SMLoc E) {
|
SMLoc S, SMLoc E) {
|
||||||
ARMOperand *Op = new ARMOperand(Shifter);
|
ARMOperand *Op = new ARMOperand(Shifter);
|
||||||
@ -907,7 +945,15 @@ void ARMOperand::print(raw_ostream &OS) const {
|
|||||||
OS << "<register " << getReg() << ">";
|
OS << "<register " << getReg() << ">";
|
||||||
break;
|
break;
|
||||||
case Shifter:
|
case Shifter:
|
||||||
OS << "<shifter " << getShiftOpcStr(Shift.ShiftTy) << ">";
|
OS << "<shifter " << ARM_AM::getShiftOpcStr(Shift.ShiftTy) << ">";
|
||||||
|
break;
|
||||||
|
case ShiftedRegister:
|
||||||
|
OS << "<so_reg"
|
||||||
|
<< ShiftedReg.SrcReg
|
||||||
|
<< ARM_AM::getShiftOpcStr(ARM_AM::getSORegShOp(ShiftedReg.ShiftImm))
|
||||||
|
<< ", " << ShiftedReg.ShiftReg << ", "
|
||||||
|
<< ARM_AM::getSORegOffset(ShiftedReg.ShiftImm)
|
||||||
|
<< ">";
|
||||||
break;
|
break;
|
||||||
case RegisterList:
|
case RegisterList:
|
||||||
case DPRRegisterList:
|
case DPRRegisterList:
|
||||||
@ -994,13 +1040,56 @@ bool ARMAsmParser::TryParseShiftRegister(
|
|||||||
if (ShiftTy == ARM_AM::no_shift)
|
if (ShiftTy == ARM_AM::no_shift)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Parser.Lex(); // Eat shift-type operand;
|
Parser.Lex(); // Eat the operator.
|
||||||
int RegNum = TryParseRegister();
|
|
||||||
if (RegNum == -1)
|
|
||||||
return Error(Parser.getTok().getLoc(), "register expected");
|
|
||||||
|
|
||||||
Operands.push_back(ARMOperand::CreateReg(RegNum,S, Parser.getTok().getLoc()));
|
// The source register for the shift has already been added to the
|
||||||
Operands.push_back(ARMOperand::CreateShifter(ShiftTy,
|
// operand list, so we need to pop it off and combine it into the shifted
|
||||||
|
// register operand instead.
|
||||||
|
ARMOperand *PrevOp = (ARMOperand*)Operands.pop_back_val();
|
||||||
|
if (!PrevOp->isReg())
|
||||||
|
return Error(PrevOp->getStartLoc(), "shift must be of a register");
|
||||||
|
int SrcReg = PrevOp->getReg();
|
||||||
|
int64_t Imm = 0;
|
||||||
|
int ShiftReg = 0;
|
||||||
|
if (ShiftTy == ARM_AM::rrx) {
|
||||||
|
// RRX Doesn't have an explicit shift amount. The encoder expects
|
||||||
|
// the shift register to be the same as the source register. Seems odd,
|
||||||
|
// but OK.
|
||||||
|
ShiftReg = SrcReg;
|
||||||
|
} else {
|
||||||
|
// Figure out if this is shifted by a constant or a register (for non-RRX).
|
||||||
|
if (Parser.getTok().is(AsmToken::Hash)) {
|
||||||
|
Parser.Lex(); // Eat hash.
|
||||||
|
SMLoc ImmLoc = Parser.getTok().getLoc();
|
||||||
|
const MCExpr *ShiftExpr = 0;
|
||||||
|
if (getParser().ParseExpression(ShiftExpr))
|
||||||
|
return Error(ImmLoc, "invalid immediate shift value");
|
||||||
|
// The expression must be evaluatable as an immediate.
|
||||||
|
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(ShiftExpr);
|
||||||
|
if (!CE)
|
||||||
|
return Error(ImmLoc, "invalid immediate shift value");
|
||||||
|
// Range check the immediate.
|
||||||
|
// lsl, ror: 0 <= imm <= 31
|
||||||
|
// lsr, asr: 0 <= imm <= 32
|
||||||
|
Imm = CE->getValue();
|
||||||
|
if (Imm < 0 ||
|
||||||
|
((ShiftTy == ARM_AM::lsl || ShiftTy == ARM_AM::ror) && Imm > 31) ||
|
||||||
|
((ShiftTy == ARM_AM::lsr || ShiftTy == ARM_AM::asr) && Imm > 32)) {
|
||||||
|
return Error(ImmLoc, "immediate shift value out of range");
|
||||||
|
}
|
||||||
|
} else if (Parser.getTok().is(AsmToken::Identifier)) {
|
||||||
|
ShiftReg = TryParseRegister();
|
||||||
|
SMLoc L = Parser.getTok().getLoc();
|
||||||
|
if (ShiftReg == -1)
|
||||||
|
return Error (L, "expected immediate or register in shift operand");
|
||||||
|
} else
|
||||||
|
return Error (Parser.getTok().getLoc(),
|
||||||
|
"expected immediate or register in shift operand");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Operands.push_back(ARMOperand::CreateShiftedRegister(ShiftTy, SrcReg,
|
||||||
|
ShiftReg, Imm,
|
||||||
S, Parser.getTok().getLoc()));
|
S, Parser.getTok().getLoc()));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -142,6 +142,8 @@ void ARMInstPrinter::printSORegOperand(const MCInst *MI, unsigned OpNum,
|
|||||||
// Print the shift opc.
|
// Print the shift opc.
|
||||||
ARM_AM::ShiftOpc ShOpc = ARM_AM::getSORegShOp(MO3.getImm());
|
ARM_AM::ShiftOpc ShOpc = ARM_AM::getSORegShOp(MO3.getImm());
|
||||||
O << ", " << ARM_AM::getShiftOpcStr(ShOpc);
|
O << ", " << ARM_AM::getShiftOpcStr(ShOpc);
|
||||||
|
if (ShOpc == ARM_AM::rrx)
|
||||||
|
return;
|
||||||
if (MO2.getReg()) {
|
if (MO2.getReg()) {
|
||||||
O << ' ' << getRegisterName(MO2.getReg());
|
O << ' ' << getRegisterName(MO2.getReg());
|
||||||
assert(ARM_AM::getSORegOffset(MO3.getImm()) == 0);
|
assert(ARM_AM::getSORegOffset(MO3.getImm()) == 0);
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
@ RUN: llvm-mc -triple=armv7-apple-darwin -show-encoding < %s | FileCheck %s
|
@ RUN: llvm-mc -triple=armv7-apple-darwin -show-encoding < %s | FileCheck %s
|
||||||
.syntax unified
|
.syntax unified
|
||||||
.globl _func
|
.globl _func
|
||||||
|
|
||||||
|
@ Check that the assembler can handle the documented syntax from the ARM ARM.
|
||||||
|
@ For complex constructs like shifter operands, check more thoroughly for them
|
||||||
|
@ once then spot check that following instructions accept the form generally.
|
||||||
|
@ This gives us good coverage while keeping the overall size of the test
|
||||||
|
@ more reasonable.
|
||||||
|
|
||||||
_func:
|
_func:
|
||||||
@ CHECK: _func
|
@ CHECK: _func
|
||||||
|
|
||||||
@ -31,3 +38,44 @@ _func:
|
|||||||
@ CHECK: adcs r1, r2, #3840 @ encoding: [0x0f,0x1c,0xb2,0xe2]
|
@ CHECK: adcs r1, r2, #3840 @ encoding: [0x0f,0x1c,0xb2,0xe2]
|
||||||
@ CHECK: adcseq r1, r2, #3840 @ encoding: [0x0f,0x1c,0xb2,0x02]
|
@ CHECK: adcseq r1, r2, #3840 @ encoding: [0x0f,0x1c,0xb2,0x02]
|
||||||
@ CHECK: adceq r1, r2, #3840 @ encoding: [0x0f,0x1c,0xa2,0x02]
|
@ CHECK: adceq r1, r2, #3840 @ encoding: [0x0f,0x1c,0xa2,0x02]
|
||||||
|
|
||||||
|
@ ADC (register)
|
||||||
|
adc r4, r5, r6
|
||||||
|
@ Constant shifts
|
||||||
|
adc r4, r5, r6, lsl #1
|
||||||
|
adc r4, r5, r6, lsl #31
|
||||||
|
adc r4, r5, r6, lsr #1
|
||||||
|
adc r4, r5, r6, lsr #31
|
||||||
|
adc r4, r5, r6, lsr #32
|
||||||
|
adc r4, r5, r6, asr #1
|
||||||
|
adc r4, r5, r6, asr #31
|
||||||
|
adc r4, r5, r6, asr #32
|
||||||
|
adc r4, r5, r6, ror #1
|
||||||
|
adc r4, r5, r6, ror #31
|
||||||
|
|
||||||
|
@ Register shifts
|
||||||
|
adc r6, r7, r8, lsl r9
|
||||||
|
adc r6, r7, r8, lsr r9
|
||||||
|
adc r6, r7, r8, asr r9
|
||||||
|
adc r6, r7, r8, ror r9
|
||||||
|
adc r4, r5, r6, rrx
|
||||||
|
|
||||||
|
@ CHECK: adc r4, r5, r6 @ encoding: [0x06,0x40,0xa5,0xe0]
|
||||||
|
|
||||||
|
@ CHECK: adc r4, r5, r6, lsl #1 @ encoding: [0x86,0x40,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, lsl #31 @ encoding: [0x86,0x4f,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, lsr #1 @ encoding: [0xa6,0x40,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, lsr #31 @ encoding: [0xa6,0x4f,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, lsr #32 @ encoding: [0x26,0x40,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, asr #1 @ encoding: [0xc6,0x40,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, asr #31 @ encoding: [0xc6,0x4f,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, asr #32 @ encoding: [0x46,0x40,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, ror #1 @ encoding: [0xe6,0x40,0xa5,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, ror #31 @ encoding: [0xe6,0x4f,0xa5,0xe0]
|
||||||
|
|
||||||
|
@ CHECK: adc r6, r7, r8, lsl r9 @ encoding: [0x18,0x69,0xa7,0xe0]
|
||||||
|
@ CHECK: adc r6, r7, r8, lsr r9 @ encoding: [0x38,0x69,0xa7,0xe0]
|
||||||
|
@ CHECK: adc r6, r7, r8, asr r9 @ encoding: [0x58,0x69,0xa7,0xe0]
|
||||||
|
@ CHECK: adc r6, r7, r8, ror r9 @ encoding: [0x78,0x69,0xa7,0xe0]
|
||||||
|
@ CHECK: adc r4, r5, r6, rrx @ encoding: [0x66,0x40,0xa5,0xe0]
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user