mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-14 11:32:34 +00:00
Thumb2 parsing and encoding for IT blocks.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@138773 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
fff64ca9cf
commit
f8e1e3e729
@ -289,6 +289,18 @@ public:
|
||||
return Flags & (1 << MCID::Barrier);
|
||||
}
|
||||
|
||||
/// findFirstPredOperandIdx() - Find the index of the first operand in the
|
||||
/// operand list that is used to represent the predicate. It returns -1 if
|
||||
/// none is found.
|
||||
int findFirstPredOperandIdx() const {
|
||||
if (isPredicable()) {
|
||||
for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
|
||||
if (OpInfo[i].isPredicate())
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// isTerminator - Returns true if this instruction part of the terminator for
|
||||
/// a basic block. Typically this is things like return and branch
|
||||
/// instructions.
|
||||
|
@ -942,6 +942,10 @@ MachineInstr::findRegisterDefOperandIdx(unsigned Reg, bool isDead, bool Overlap,
|
||||
/// operand list that is used to represent the predicate. It returns -1 if
|
||||
/// none is found.
|
||||
int MachineInstr::findFirstPredOperandIdx() const {
|
||||
// Don't call MCID.findFirstPredOperandIdx() because this variant
|
||||
// is sometimes called on an instruction that's not yet complete, and
|
||||
// so the number of operands is less than the MCID indicates. In
|
||||
// particular, the PTX target does this.
|
||||
const MCInstrDesc &MCID = getDesc();
|
||||
if (MCID.isPredicable()) {
|
||||
for (unsigned i = 0, e = getNumOperands(); i != e; ++i)
|
||||
|
@ -44,6 +44,28 @@ class ARMAsmParser : public MCTargetAsmParser {
|
||||
MCSubtargetInfo &STI;
|
||||
MCAsmParser &Parser;
|
||||
|
||||
struct {
|
||||
ARMCC::CondCodes Cond; // Condition for IT block.
|
||||
unsigned Mask:4; // Condition mask for instructions.
|
||||
// Starting at first 1 (from lsb).
|
||||
// '1' condition as indicated in IT.
|
||||
// '0' inverse of condition (else).
|
||||
// Count of instructions in IT block is
|
||||
// 4 - trailingzeroes(mask)
|
||||
|
||||
bool FirstCond; // Explicit flag for when we're parsing the
|
||||
// First instruction in the IT block. It's
|
||||
// implied in the mask, so needs special
|
||||
// handling.
|
||||
|
||||
unsigned CurPosition; // Current position in parsing of IT
|
||||
// block. In range [0,3]. Initialized
|
||||
// according to count of instructions in block.
|
||||
// ~0U if no active IT block.
|
||||
} ITState;
|
||||
bool inITBlock() { return ITState.CurPosition != ~0U;}
|
||||
|
||||
|
||||
MCAsmParser &getParser() const { return Parser; }
|
||||
MCAsmLexer &getLexer() const { return Parser.getLexer(); }
|
||||
|
||||
@ -165,6 +187,7 @@ class ARMAsmParser : public MCTargetAsmParser {
|
||||
public:
|
||||
enum ARMMatchResultTy {
|
||||
Match_RequiresITBlock = FIRST_TARGET_MATCH_RESULT_TY,
|
||||
Match_RequiresNotITBlock,
|
||||
Match_RequiresV6,
|
||||
Match_RequiresThumb2
|
||||
};
|
||||
@ -175,6 +198,9 @@ public:
|
||||
|
||||
// Initialize the set of available features.
|
||||
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
|
||||
|
||||
// Not in an ITBlock to start with.
|
||||
ITState.CurPosition = ~0U;
|
||||
}
|
||||
|
||||
// Implementation of the MCTargetAsmParser interface:
|
||||
@ -3085,18 +3111,23 @@ bool ARMAsmParser::ParseInstruction(StringRef Name, SMLoc NameLoc,
|
||||
// where the conditional bit0 is zero, the instruction post-processing
|
||||
// will adjust the mask accordingly.
|
||||
if (Mnemonic == "it") {
|
||||
SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + 2);
|
||||
if (ITMask.size() > 3) {
|
||||
Parser.EatToEndOfStatement();
|
||||
return Error(Loc, "too many conditions on IT instruction");
|
||||
}
|
||||
unsigned Mask = 8;
|
||||
for (unsigned i = ITMask.size(); i != 0; --i) {
|
||||
char pos = ITMask[i - 1];
|
||||
if (pos != 't' && pos != 'e') {
|
||||
Parser.EatToEndOfStatement();
|
||||
return Error(NameLoc, "illegal IT instruction mask '" + ITMask + "'");
|
||||
return Error(Loc, "illegal IT block condition mask '" + ITMask + "'");
|
||||
}
|
||||
Mask >>= 1;
|
||||
if (ITMask[i - 1] == 't')
|
||||
Mask |= 8;
|
||||
}
|
||||
Operands.push_back(ARMOperand::CreateITMask(Mask, NameLoc));
|
||||
Operands.push_back(ARMOperand::CreateITMask(Mask, Loc));
|
||||
}
|
||||
|
||||
// FIXME: This is all a pretty gross hack. We should automatically handle
|
||||
@ -3128,18 +3159,18 @@ bool ARMAsmParser::ParseInstruction(StringRef Name, SMLoc NameLoc,
|
||||
}
|
||||
|
||||
// Add the carry setting operand, if necessary.
|
||||
//
|
||||
// FIXME: It would be awesome if we could somehow invent a location such that
|
||||
// match errors on this operand would print a nice diagnostic about how the
|
||||
// 's' character in the mnemonic resulted in a CCOut operand.
|
||||
if (CanAcceptCarrySet)
|
||||
if (CanAcceptCarrySet) {
|
||||
SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + Mnemonic.size());
|
||||
Operands.push_back(ARMOperand::CreateCCOut(CarrySetting ? ARM::CPSR : 0,
|
||||
NameLoc));
|
||||
Loc));
|
||||
}
|
||||
|
||||
// Add the predication code operand, if necessary.
|
||||
if (CanAcceptPredicationCode) {
|
||||
SMLoc Loc = SMLoc::getFromPointer(NameLoc.getPointer() + Mnemonic.size() +
|
||||
CarrySetting);
|
||||
Operands.push_back(ARMOperand::CreateCondCode(
|
||||
ARMCC::CondCodes(PredicationCode), NameLoc));
|
||||
ARMCC::CondCodes(PredicationCode), Loc));
|
||||
}
|
||||
|
||||
// Add the processor imod operand, if necessary.
|
||||
@ -3261,10 +3292,57 @@ static bool checkLowRegisterList(MCInst Inst, unsigned OpNo, unsigned Reg,
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: We would really prefer to have MCInstrInfo (the wrapper around
|
||||
// the ARMInsts array) instead. Getting that here requires awkward
|
||||
// API changes, though. Better way?
|
||||
namespace llvm {
|
||||
extern MCInstrDesc ARMInsts[];
|
||||
}
|
||||
static MCInstrDesc &getInstDesc(unsigned Opcode) {
|
||||
return ARMInsts[Opcode];
|
||||
}
|
||||
|
||||
// FIXME: We would really like to be able to tablegen'erate this.
|
||||
bool ARMAsmParser::
|
||||
validateInstruction(MCInst &Inst,
|
||||
const SmallVectorImpl<MCParsedAsmOperand*> &Operands) {
|
||||
MCInstrDesc &MCID = getInstDesc(Inst.getOpcode());
|
||||
SMLoc Loc = Operands[0]->getStartLoc();
|
||||
// Check the IT block state first.
|
||||
if (inITBlock()) {
|
||||
unsigned bit = 1;
|
||||
if (ITState.FirstCond)
|
||||
ITState.FirstCond = false;
|
||||
else
|
||||
bit = (ITState.Mask >> (4 - ITState.CurPosition)) & 1;
|
||||
// Increment our position in the IT block first thing, as we want to
|
||||
// move forward even if we find an error in the IT block.
|
||||
unsigned TZ = CountTrailingZeros_32(ITState.Mask);
|
||||
if (++ITState.CurPosition == 4 - TZ)
|
||||
ITState.CurPosition = ~0U; // Done with the IT block after this.
|
||||
// The instruction must be predicable.
|
||||
if (!MCID.isPredicable())
|
||||
return Error(Loc, "instructions in IT block must be predicable");
|
||||
unsigned Cond = Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm();
|
||||
unsigned ITCond = bit ? ITState.Cond :
|
||||
ARMCC::getOppositeCondition(ITState.Cond);
|
||||
if (Cond != ITCond) {
|
||||
// Find the condition code Operand to get its SMLoc information.
|
||||
SMLoc CondLoc;
|
||||
for (unsigned i = 1; i < Operands.size(); ++i)
|
||||
if (static_cast<ARMOperand*>(Operands[i])->isCondCode())
|
||||
CondLoc = Operands[i]->getStartLoc();
|
||||
return Error(CondLoc, "incorrect condition in IT block; got '" +
|
||||
StringRef(ARMCondCodeToString(ARMCC::CondCodes(Cond))) +
|
||||
"', but expected '" +
|
||||
ARMCondCodeToString(ARMCC::CondCodes(ITCond)) + "'");
|
||||
}
|
||||
// Check for non-'al' condition codes outside of the IT block.
|
||||
} else if (isThumbTwo() && MCID.isPredicable() &&
|
||||
Inst.getOperand(MCID.findFirstPredOperandIdx()).getImm() !=
|
||||
ARMCC::AL)
|
||||
return Error(Loc, "predicated instructions must be in IT block");
|
||||
|
||||
switch (Inst.getOpcode()) {
|
||||
case ARM::LDRD:
|
||||
case ARM::LDRD_PRE:
|
||||
@ -3413,29 +3491,28 @@ processInstruction(MCInst &Inst,
|
||||
// so mask that in if needed
|
||||
MCOperand &MO = Inst.getOperand(1);
|
||||
unsigned Mask = MO.getImm();
|
||||
unsigned OrigMask = Mask;
|
||||
unsigned TZ = CountTrailingZeros_32(Mask);
|
||||
if ((Inst.getOperand(0).getImm() & 1) == 0) {
|
||||
unsigned TZ = CountTrailingZeros_32(Mask);
|
||||
assert(Mask && TZ <= 3 && "illegal IT mask value!");
|
||||
for (unsigned i = 3; i != TZ; --i)
|
||||
Mask ^= 1 << i;
|
||||
} else
|
||||
Mask |= 0x10;
|
||||
MO.setImm(Mask);
|
||||
|
||||
// Set up the IT block state according to the IT instruction we just
|
||||
// matched.
|
||||
assert(!inITBlock() && "nested IT blocks?!");
|
||||
ITState.Cond = ARMCC::CondCodes(Inst.getOperand(0).getImm());
|
||||
ITState.Mask = OrigMask; // Use the original mask, not the updated one.
|
||||
ITState.CurPosition = 0;
|
||||
ITState.FirstCond = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: We would really prefer to have MCInstrInfo (the wrapper around
|
||||
// the ARMInsts array) instead. Getting that here requires awkward
|
||||
// API changes, though. Better way?
|
||||
namespace llvm {
|
||||
extern MCInstrDesc ARMInsts[];
|
||||
}
|
||||
static MCInstrDesc &getInstDesc(unsigned Opcode) {
|
||||
return ARMInsts[Opcode];
|
||||
}
|
||||
|
||||
unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
|
||||
// 16-bit thumb arithmetic instructions either require or preclude the 'S'
|
||||
// suffix depending on whether they're in an IT block or not.
|
||||
@ -3457,10 +3534,12 @@ unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
|
||||
return Match_MnemonicFail;
|
||||
// If we're parsing Thumb2, which form is legal depends on whether we're
|
||||
// in an IT block.
|
||||
// FIXME: We don't yet do IT blocks, so just always consider it to be
|
||||
// that we aren't in one until we do.
|
||||
if (isThumbTwo() && Inst.getOperand(OpNo).getReg() != ARM::CPSR)
|
||||
if (isThumbTwo() && Inst.getOperand(OpNo).getReg() != ARM::CPSR &&
|
||||
!inITBlock())
|
||||
return Match_RequiresITBlock;
|
||||
if (isThumbTwo() && Inst.getOperand(OpNo).getReg() == ARM::CPSR &&
|
||||
inITBlock())
|
||||
return Match_RequiresNotITBlock;
|
||||
}
|
||||
// Some high-register supporting Thumb1 encodings only allow both registers
|
||||
// to be from r0-r7 when in Thumb2.
|
||||
@ -3518,6 +3597,8 @@ MatchAndEmitInstruction(SMLoc IDLoc,
|
||||
case Match_ConversionFail:
|
||||
// The converter function will have already emited a diagnostic.
|
||||
return true;
|
||||
case Match_RequiresNotITBlock:
|
||||
return Error(IDLoc, "flag setting instruction only valid outside IT block");
|
||||
case Match_RequiresITBlock:
|
||||
return Error(IDLoc, "instruction only valid inside IT block");
|
||||
case Match_RequiresV6:
|
||||
|
32
test/MC/ARM/basic-thumb2-instructions.s
Normal file
32
test/MC/ARM/basic-thumb2-instructions.s
Normal file
@ -0,0 +1,32 @@
|
||||
@ RUN: llvm-mc -triple=thumbv7-apple-darwin -show-encoding < %s | FileCheck %s
|
||||
.syntax unified
|
||||
.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.
|
||||
|
||||
|
||||
@ FIXME: Some 3-operand instructions have a 2-operand assembly syntax.
|
||||
|
||||
_func:
|
||||
@ CHECK: _func
|
||||
|
||||
@------------------------------------------------------------------------------
|
||||
@ IT
|
||||
@------------------------------------------------------------------------------
|
||||
@ Test encodings of a few full IT blocks, not just the IT instruction
|
||||
|
||||
iteet eq
|
||||
addeq r0, r1, r2
|
||||
nopne
|
||||
subne r5, r6, r7
|
||||
addeq r1, r2, #4
|
||||
|
||||
@ CHECK: iteet eq @ encoding: [0x0d,0xbf]
|
||||
@ CHECK: addeq r0, r1, r2 @ encoding: [0x88,0x18]
|
||||
@ CHECK: nopne @ encoding: [0x00,0xbf]
|
||||
@ CHECK: subne r5, r6, r7 @ encoding: [0xf5,0x1b]
|
||||
@ CHECK: addeq r1, r2, #4 @ encoding: [0x11,0x1d]
|
30
test/MC/ARM/thumb2-diagnostics.s
Normal file
30
test/MC/ARM/thumb2-diagnostics.s
Normal file
@ -0,0 +1,30 @@
|
||||
@ RUN: not llvm-mc -triple=thumbv7-apple-darwin < %s 2> %t
|
||||
@ RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s
|
||||
|
||||
@ Ill-formed IT block instructions.
|
||||
itet eq
|
||||
addle r0, r1, r2
|
||||
nop
|
||||
it le
|
||||
iteeee gt
|
||||
ittfe le
|
||||
nopeq
|
||||
|
||||
@ CHECK-ERRORS: error: incorrect condition in IT block; got 'le', but expected 'eq'
|
||||
@ CHECK-ERRORS: addle r0, r1, r2
|
||||
@ CHECK-ERRORS: ^
|
||||
@ CHECK-ERRORS: error: incorrect condition in IT block; got 'al', but expected 'ne'
|
||||
@ CHECK-ERRORS: nop
|
||||
@ CHECK-ERRORS: ^
|
||||
@ CHECK-ERRORS: error: instructions in IT block must be predicable
|
||||
@ CHECK-ERRORS: it le
|
||||
@ CHECK-ERRORS: ^
|
||||
@ CHECK-ERRORS: error: too many conditions on IT instruction
|
||||
@ CHECK-ERRORS: iteeee gt
|
||||
@ CHECK-ERRORS: ^
|
||||
@ CHECK-ERRORS: error: illegal IT block condition mask 'tfe'
|
||||
@ CHECK-ERRORS: ittfe le
|
||||
@ CHECK-ERRORS: ^
|
||||
@ CHECK-ERRORS: error: predicated instructions must be in IT block
|
||||
@ CHECK-ERRORS: nopeq
|
||||
@ CHECK-ERRORS: ^
|
Loading…
Reference in New Issue
Block a user