ELF_RELOC(R_ARM_ME_TOO, 0x80)
ELF_RELOC(R_ARM_THM_TLS_DESCSEQ16, 0x81)
ELF_RELOC(R_ARM_THM_TLS_DESCSEQ32, 0x82)
+ELF_RELOC(R_ARM_THM_ALU_ABS_G0_NC, 0x84)
+ELF_RELOC(R_ARM_THM_ALU_ABS_G1_NC, 0x85)
+ELF_RELOC(R_ARM_THM_ALU_ABS_G2_NC, 0x86)
+ELF_RELOC(R_ARM_THM_ALU_ABS_G3, 0x87)
ELF_RELOC(R_ARM_THM_BF16, 0x88)
ELF_RELOC(R_ARM_THM_BF12, 0x89)
ELF_RELOC(R_ARM_THM_BF18, 0x8a)
let ParserMatchClass = Imm0_255AsmOperand;
}
+// imm0_255_expr - For Thumb1 movs/adds - 8-bit immediate that can also reference
+// a relocatable expression.
+def Imm0_255ExprAsmOperand: AsmOperandClass {
+ let Name = "Imm0_255Expr";
+ let RenderMethod = "addImmOperands";
+ let DiagnosticString = "operand must be an immediate in the range [0,255] or a relocatable expression";
+}
+
+def imm0_255_expr : Operand<i32>, ImmLeaf<i32, [{ return Imm >= 0 && Imm < 256; }]> {
+ let EncoderMethod = "getHiLoImmOpValue";
+ let ParserMatchClass = Imm0_255ExprAsmOperand;
+}
+
/// imm0_65535 - An immediate is in the range [0,65535].
def Imm0_65535AsmOperand: ImmAsmOperand<0,65535> { let Name = "Imm0_65535"; }
def imm0_65535 : Operand<i32>, ImmLeaf<i32, [{
let DiagnosticString = "operand must be an immediate in the range [0,0xffff] or a relocatable expression";
}
-def imm0_65535_expr : Operand<i32> {
- let EncoderMethod = "getHiLo16ImmOpValue";
+def imm0_65535_expr : Operand<i32>, ImmLeaf<i32, [{
+ return Imm >= 0 && Imm < 65536;
+}]> {
+ let EncoderMethod = "getHiLoImmOpValue";
let ParserMatchClass = Imm0_65535ExprAsmOperand;
}
def tADDi8 : // A8.6.4 T2
T1sItGenEncodeImm<{1,1,0,?,?}, (outs tGPR:$Rdn),
- (ins tGPR:$Rn, imm0_255:$imm8), IIC_iALUi,
+ (ins tGPR:$Rn, imm0_255_expr:$imm8), IIC_iALUi,
"add", "\t$Rdn, $imm8",
- [(set tGPR:$Rdn, (add tGPR:$Rn, imm8_255:$imm8))]>,
+ [(set tGPR:$Rdn, (add tGPR:$Rn, imm0_255_expr:$imm8))]>,
Sched<[WriteALU]>;
// Add register
T1sIGenEncode<0b01100, (outs tGPR:$Rd), (ins tGPR:$Rn, tGPR:$Rm),
IIC_iALUr,
"add", "\t$Rd, $Rn, $Rm",
- [(set tGPR:$Rd, (add tGPR:$Rn, tGPR:$Rm))]>, Sched<[WriteALU]>;
+ [(set tGPR:$Rd, (add tGPR:$Rn, tGPR:$Rm))]>,
+ Sched<[WriteALU]>;
/// Similar to the above except these set the 's' bit so the
/// instruction modifies the CPSR register.
Requires<[IsThumb1Only]>,
Sched<[WriteALU]>;
- def tADDSi8 : tPseudoInst<(outs tGPR:$Rdn), (ins tGPR:$Rn, imm0_255:$imm8),
+ def tADDSi8 : tPseudoInst<(outs tGPR:$Rdn), (ins tGPR:$Rn, imm0_255_expr:$imm8),
2, IIC_iALUi,
[(set tGPR:$Rdn, CPSR, (ARMaddc tGPR:$Rn,
- imm8_255:$imm8))]>,
+ imm0_255_expr:$imm8))]>,
Requires<[IsThumb1Only]>,
Sched<[WriteALU]>;
// Move register
let isMoveImm = 1 in
-def tMOVi8 : T1sI<(outs tGPR:$Rd), (ins imm0_255:$imm8), IIC_iMOVi,
+def tMOVi8 : T1sI<(outs tGPR:$Rd), (ins imm0_255_expr:$imm8), IIC_iMOVi,
"mov", "\t$Rd, $imm8",
- [(set tGPR:$Rd, imm0_255:$imm8)]>,
+ [(set tGPR:$Rd, imm0_255_expr:$imm8)]>,
T1General<{1,0,0,?,?}>, Sched<[WriteALU]> {
// A8.6.96
bits<3> Rd;
}
// Because we have an explicit tMOVSr below, we need an alias to handle
// the immediate "movs" form here. Blech.
-def : tInstAlias <"movs $Rdn, $imm",
- (tMOVi8 tGPR:$Rdn, CPSR, imm0_255:$imm, 14, 0)>;
+def : tInstAlias <"movs $Rdn, $imm8",
+ (tMOVi8 tGPR:$Rdn, CPSR, imm0_255_expr:$imm8, 14, 0)>;
// A7-73: MOV(2) - mov setting flag.
return isImmediate<8, 255>();
}
+ bool isImm0_255Expr() const {
+ if (!isImm())
+ return false;
+ const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
+ // If it's not a constant expression, it'll generate a fixup and be
+ // handled later.
+ if (!CE)
+ return true;
+ int64_t Value = CE->getValue();
+ return isUInt<8>(Value);
+ }
+
bool isImm256_65535Expr() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
}
case AsmToken::Colon: {
S = Parser.getTok().getLoc();
- // ":lower16:" and ":upper16:" expression prefixes
+ // ":lower16:", ":upper16:", ":lower0_7:", ":lower8_15:", ":upper0_7:" and
+ // ":upper8_15:", expression prefixes
// FIXME: Check it's an expression prefix,
// e.g. (FOO - :lower16:BAR) isn't legal.
ARMMCExpr::VariantKind RefKind;
return false;
}
-// parsePrefix - Parse ARM 16-bit relocations expression prefix, i.e.
-// :lower16: and :upper16:.
+// parsePrefix - Parse ARM 16-bit relocations expression prefixes, i.e.
+// :lower16: and :upper16: and Thumb 8-bit relocation expression prefixes, i.e.
+// :upper8_15:, :upper0_7:, :lower8_15: and :lower0_7:
bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) {
MCAsmParser &Parser = getParser();
RefKind = ARMMCExpr::VK_ARM_None;
if (getLexer().is(AsmToken::Hash))
Parser.Lex();
- // :lower16: and :upper16: modifiers
assert(getLexer().is(AsmToken::Colon) && "expected a :");
Parser.Lex(); // Eat ':'
ARMMCExpr::VariantKind VariantKind;
uint8_t SupportedFormats;
} PrefixEntries[] = {
- { "lower16", ARMMCExpr::VK_ARM_LO16, COFF | ELF | MACHO },
- { "upper16", ARMMCExpr::VK_ARM_HI16, COFF | ELF | MACHO },
+ {"upper16", ARMMCExpr::VK_ARM_HI16, COFF | ELF | MACHO},
+ {"lower16", ARMMCExpr::VK_ARM_LO16, COFF | ELF | MACHO},
+ {"upper8_15", ARMMCExpr::VK_ARM_HI_8_15, ELF},
+ {"upper0_7", ARMMCExpr::VK_ARM_HI_0_7, ELF},
+ {"lower8_15", ARMMCExpr::VK_ARM_LO_8_15, ELF},
+ {"lower0_7", ARMMCExpr::VK_ARM_LO_0_7, ELF},
};
StringRef IDVal = Parser.getTok().getIdentifier();
}
Parser.Lex(); // Eat the last ':'
+ // consume an optional trailing '#' (GNU compatibility) bla
+ parseOptionalToken(AsmToken::Hash);
+
return false;
}
}
}
+// this function returns true if the operand is one of the following
+// relocations: :upper8_15:, :upper0_7:, :lower8_15: or :lower0_7:
+static bool isThumbI8Relocation(MCParsedAsmOperand &MCOp) {
+ ARMOperand &Op = static_cast<ARMOperand &>(MCOp);
+ if (!Op.isImm())
+ return false;
+ const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
+ if (CE)
+ return false;
+ const MCExpr *E = dyn_cast<MCExpr>(Op.getImm());
+ if (!E)
+ return false;
+ const ARMMCExpr *ARM16Expr = dyn_cast<ARMMCExpr>(E);
+ if (ARM16Expr && (ARM16Expr->getKind() == ARMMCExpr::VK_ARM_HI_8_15 ||
+ ARM16Expr->getKind() == ARMMCExpr::VK_ARM_HI_0_7 ||
+ ARM16Expr->getKind() == ARMMCExpr::VK_ARM_LO_8_15 ||
+ ARM16Expr->getKind() == ARMMCExpr::VK_ARM_LO_0_7))
+ return true;
+ return false;
+}
+
bool ARMAsmParser::shouldOmitCCOutOperand(StringRef Mnemonic,
OperandVector &Operands) {
// FIXME: This is all horribly hacky. We really need a better way to deal
static_cast<ARMOperand &>(*Operands[1]).getReg() == 0)
return true;
+ if (Mnemonic == "movs" && Operands.size() > 3 && isThumb() &&
+ isThumbI8Relocation(*Operands[3]))
+ return true;
+
// Register-register 'add' for thumb does not have a cc_out operand
// when there are only two register operands.
if (isThumb() && Mnemonic == "add" && Operands.size() == 5 &&
return findFirstVectorPredOperandIdx(MCID) != -1;
}
+static bool isARMMCExpr(MCParsedAsmOperand &MCOp) {
+ ARMOperand &Op = static_cast<ARMOperand &>(MCOp);
+ if (!Op.isImm())
+ return false;
+ const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
+ if (CE)
+ return false;
+ const MCExpr *E = dyn_cast<MCExpr>(Op.getImm());
+ if (!E)
+ return false;
+ return true;
+}
+
// FIXME: We would really like to be able to tablegen'erate this.
bool ARMAsmParser::validateInstruction(MCInst &Inst,
const OperandVector &Operands) {
"immediate expression for mov requires :lower16: or :upper16");
break;
}
+ case ARM::tADDi8: {
+ MCParsedAsmOperand &Op = *Operands[4];
+ if (isARMMCExpr(Op) && !isThumbI8Relocation(Op))
+ return Error(Op.getStartLoc(),
+ "Immediate expression for Thumb adds requires :lower0_7:,"
+ " :lower8_15:, :upper0_7: or :upper8_15:");
+ break;
+ }
+ case ARM::tMOVi8: {
+ MCParsedAsmOperand &Op = *Operands[2];
+ if (isARMMCExpr(Op) && !isThumbI8Relocation(Op))
+ return Error(Op.getStartLoc(),
+ "Immediate expression for Thumb movs requires :lower0_7:,"
+ " :lower8_15:, :upper0_7: or :upper8_15:");
+ break;
+ }
case ARM::HINT:
case ARM::t2HINT: {
unsigned Imm8 = Inst.getOperand(0).getImm();
// explicitly specified. From the ARM ARM: "Encoding T1 is preferred
// to encoding T2 if <Rd> is specified and encoding T2 is preferred
// to encoding T1 if <Rd> is omitted."
- if ((unsigned)Inst.getOperand(3).getImm() < 8 && Operands.size() == 6) {
+ if (Inst.getOperand(3).isImm() &&
+ (unsigned)Inst.getOperand(3).getImm() < 8 && Operands.size() == 6) {
Inst.setOpcode(ARM::tADDi3);
return true;
}
{"fixup_arm_movw_lo16", 0, 20, 0},
{"fixup_t2_movt_hi16", 0, 20, 0},
{"fixup_t2_movw_lo16", 0, 20, 0},
+ {"fixup_arm_thumb_upper_8_15", 0, 8, 0},
+ {"fixup_arm_thumb_upper_0_7", 0, 8, 0},
+ {"fixup_arm_thumb_lower_8_15", 0, 8, 0},
+ {"fixup_arm_thumb_lower_0_7", 0, 8, 0},
{"fixup_arm_mod_imm", 0, 12, 0},
{"fixup_t2_so_imm", 0, 26, 0},
{"fixup_bf_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel},
{"fixup_arm_movw_lo16", 12, 20, 0},
{"fixup_t2_movt_hi16", 12, 20, 0},
{"fixup_t2_movw_lo16", 12, 20, 0},
+ {"fixup_arm_thumb_upper_8_15", 24, 8, 0},
+ {"fixup_arm_thumb_upper_0_7", 24, 8, 0},
+ {"fixup_arm_thumb_lower_8_15", 24, 8, 0},
+ {"fixup_arm_thumb_lower_0_7", 24, 8, 0},
{"fixup_arm_mod_imm", 20, 12, 0},
{"fixup_t2_so_imm", 26, 6, 0},
{"fixup_bf_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel},
Value = (Hi4 << 16) | (i << 26) | (Mid3 << 12) | (Lo8);
return swapHalfWords(Value, Endian == support::little);
}
+ case ARM::fixup_arm_thumb_upper_8_15:
+ if (IsResolved || !STI->getTargetTriple().isOSBinFormatELF())
+ return (Value & 0xff000000) >> 24;
+ return Value & 0xff;
+ case ARM::fixup_arm_thumb_upper_0_7:
+ if (IsResolved || !STI->getTargetTriple().isOSBinFormatELF())
+ return (Value & 0x00ff0000) >> 16;
+ return Value & 0xff;
+ case ARM::fixup_arm_thumb_lower_8_15:
+ if (IsResolved || !STI->getTargetTriple().isOSBinFormatELF())
+ return (Value & 0x0000ff00) >> 8;
+ return Value & 0xff;
+ case ARM::fixup_arm_thumb_lower_0_7:
+ return Value & 0x000000ff;
case ARM::fixup_arm_ldst_pcrel_12:
// ARM PC-relative values are offset by 8.
Value -= 4;
case ARM::fixup_arm_thumb_bcc:
case ARM::fixup_arm_thumb_cp:
case ARM::fixup_thumb_adr_pcrel_10:
+ case ARM::fixup_arm_thumb_upper_8_15:
+ case ARM::fixup_arm_thumb_upper_0_7:
+ case ARM::fixup_arm_thumb_lower_8_15:
+ case ARM::fixup_arm_thumb_lower_0_7:
return 1;
case FK_Data_2:
case ARM::fixup_thumb_adr_pcrel_10:
case ARM::fixup_arm_thumb_br:
case ARM::fixup_arm_thumb_cb:
+ case ARM::fixup_arm_thumb_upper_8_15:
+ case ARM::fixup_arm_thumb_upper_0_7:
+ case ARM::fixup_arm_thumb_lower_8_15:
+ case ARM::fixup_arm_thumb_lower_0_7:
// Instruction size is 2 bytes.
return 2;
return ELF::R_ARM_THM_MOVT_PREL;
case ARM::fixup_t2_movw_lo16:
return ELF::R_ARM_THM_MOVW_PREL_NC;
+ case ARM::fixup_arm_thumb_upper_8_15:
+ return ELF::R_ARM_THM_ALU_ABS_G3;
+ case ARM::fixup_arm_thumb_upper_0_7:
+ return ELF::R_ARM_THM_ALU_ABS_G2_NC;
+ case ARM::fixup_arm_thumb_lower_8_15:
+ return ELF::R_ARM_THM_ALU_ABS_G1_NC;
+ case ARM::fixup_arm_thumb_lower_0_7:
+ return ELF::R_ARM_THM_ALU_ABS_G0_NC;
case ARM::fixup_arm_thumb_br:
return ELF::R_ARM_THM_JUMP11;
case ARM::fixup_arm_thumb_bcc:
case MCSymbolRefExpr::VK_ARM_SBREL:
return ELF::R_ARM_THM_MOVW_BREL_NC;
}
+
+ case ARM::fixup_arm_thumb_upper_8_15:
+ return ELF::R_ARM_THM_ALU_ABS_G3;
+ case ARM::fixup_arm_thumb_upper_0_7:
+ return ELF::R_ARM_THM_ALU_ABS_G2_NC;
+ case ARM::fixup_arm_thumb_lower_8_15:
+ return ELF::R_ARM_THM_ALU_ABS_G1_NC;
+ case ARM::fixup_arm_thumb_lower_0_7:
+ return ELF::R_ARM_THM_ALU_ABS_G0_NC;
}
}
fixup_t2_movt_hi16, // :upper16:
fixup_t2_movw_lo16, // :lower16:
+ // Fixup for Thumb movs (enc T1) and adds (enc T2) 8-bit immediate field (7-0)
+ fixup_arm_thumb_upper_8_15, // :upper8_15:
+ fixup_arm_thumb_upper_0_7, // :upper0_7:
+ fixup_arm_thumb_lower_8_15, // :lower8_15:
+ fixup_arm_thumb_lower_0_7, // :lower0_7:
+
// Fixup for mod_imm
fixup_arm_mod_imm,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
- /// getHiLo16ImmOpValue - Return the encoding for the hi / low 16-bit of
- /// the specified operand. This is used for operands with :lower16: and
- /// :upper16: prefixes.
- uint32_t getHiLo16ImmOpValue(const MCInst &MI, unsigned OpIdx,
- SmallVectorImpl<MCFixup> &Fixups,
- const MCSubtargetInfo &STI) const;
+ /// getHiLoImmOpValue - Return the encoding for either the hi / low 16-bit, or
+ /// high/middle-high/middle-low/low 8 bits of the specified operand. This is
+ /// used for operands with :lower16:, :upper16: :lower0_7:, :lower8_15:,
+ /// :higher0_7:, and :higher8_15: prefixes.
+ uint32_t getHiLoImmOpValue(const MCInst &MI, unsigned OpIdx,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const;
bool EncodeAddrModeOpValues(const MCInst &MI, unsigned OpIdx,
unsigned &Reg, unsigned &Imm,
return (Reg << 8) | Imm8;
}
-uint32_t
-ARMMCCodeEmitter::getHiLo16ImmOpValue(const MCInst &MI, unsigned OpIdx,
- SmallVectorImpl<MCFixup> &Fixups,
- const MCSubtargetInfo &STI) const {
+uint32_t ARMMCCodeEmitter::getHiLoImmOpValue(const MCInst &MI, unsigned OpIdx,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const {
// {20-16} = imm{15-12}
// {11-0} = imm{11-0}
const MCOperand &MO = MI.getOperand(OpIdx);
if (MO.isImm())
- // Hi / lo 16 bits already extracted during earlier passes.
+ // Hi / lo bits already extracted during earlier passes.
return static_cast<unsigned>(MO.getImm());
- // Handle :upper16: and :lower16: assembly prefixes.
+ // Handle :upper16:, :lower16:, :upper8_15:, :upper0_7:, :lower8_15:
+ // :lower0_7: assembly prefixes.
const MCExpr *E = MO.getExpr();
MCFixupKind Kind;
if (E->getKind() == MCExpr::Target) {
return (int32_t(Value) & 0xffff0000) >> 16;
case ARMMCExpr::VK_ARM_LO16:
return (int32_t(Value) & 0x0000ffff);
+
+ case ARMMCExpr::VK_ARM_HI_8_15:
+ return (int32_t(Value) & 0xff000000) >> 24;
+ case ARMMCExpr::VK_ARM_HI_0_7:
+ return (int32_t(Value) & 0x00ff0000) >> 16;
+ case ARMMCExpr::VK_ARM_LO_8_15:
+ return (int32_t(Value) & 0x0000ff00) >> 8;
+ case ARMMCExpr::VK_ARM_LO_0_7:
+ return (int32_t(Value) & 0x000000ff);
+
default: llvm_unreachable("Unsupported ARMFixup");
}
}
Kind = MCFixupKind(isThumb(STI) ? ARM::fixup_t2_movw_lo16
: ARM::fixup_arm_movw_lo16);
break;
+ case ARMMCExpr::VK_ARM_HI_8_15:
+ if (!isThumb(STI))
+ llvm_unreachable(":upper_8_15: not supported in Arm state");
+ Kind = MCFixupKind(ARM::fixup_arm_thumb_upper_8_15);
+ break;
+ case ARMMCExpr::VK_ARM_HI_0_7:
+ if (!isThumb(STI))
+ llvm_unreachable(":upper_0_7: not supported in Arm state");
+ Kind = MCFixupKind(ARM::fixup_arm_thumb_upper_0_7);
+ break;
+ case ARMMCExpr::VK_ARM_LO_8_15:
+ if (!isThumb(STI))
+ llvm_unreachable(":lower_8_15: not supported in Arm state");
+ Kind = MCFixupKind(ARM::fixup_arm_thumb_lower_8_15);
+ break;
+ case ARMMCExpr::VK_ARM_LO_0_7:
+ if (!isThumb(STI))
+ llvm_unreachable(":lower_0_7: not supported in Arm state");
+ Kind = MCFixupKind(ARM::fixup_arm_thumb_lower_0_7);
+ break;
}
Fixups.push_back(MCFixup::create(0, E, Kind, MI.getLoc()));
return 0;
}
- // If the expression doesn't have :upper16: or :lower16: on it,
- // it's just a plain immediate expression, previously those evaluated to
- // the lower 16 bits of the expression regardless of whether
- // we have a movt or a movw, but that led to misleadingly results.
- // This is disallowed in the AsmParser in validateInstruction()
- // so this should never happen.
- llvm_unreachable("expression without :upper16: or :lower16:");
+ // If the expression doesn't have :upper16:, :lower16: on it, it's just a
+ // plain immediate expression, previously those evaluated to the lower 16 bits
+ // of the expression regardless of whether we have a movt or a movw, but that
+ // led to misleadingly results. This is disallowed in the AsmParser in
+ // validateInstruction() so this should never happen. The same holds for
+ // thumb1 :upper8_15:, :upper0_7:, lower8_15: or :lower0_7: with movs or adds.
+ llvm_unreachable("expression without :upper16:, :lower16:, :upper8_15:,"
+ ":upper0_7:, lower8_15: or :lower0_7:");
}
uint32_t ARMMCCodeEmitter::
void ARMMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const {
switch (Kind) {
default: llvm_unreachable("Invalid kind!");
- case VK_ARM_HI16: OS << ":upper16:"; break;
- case VK_ARM_LO16: OS << ":lower16:"; break;
+ case VK_ARM_HI16:
+ OS << ":upper16:";
+ break;
+ case VK_ARM_LO16:
+ OS << ":lower16:";
+ break;
+ case VK_ARM_HI_8_15:
+ OS << ":upper8_15:";
+ break;
+ case VK_ARM_HI_0_7:
+ OS << ":upper0_7:";
+ break;
+ case VK_ARM_LO_8_15:
+ OS << ":lower8_15:";
+ break;
+ case VK_ARM_LO_0_7:
+ OS << ":lower0_7:";
+ break;
}
const MCExpr *Expr = getSubExpr();
public:
enum VariantKind {
VK_ARM_None,
- VK_ARM_HI16, // The R_ARM_MOVT_ABS relocation (:upper16: in the .s file)
- VK_ARM_LO16 // The R_ARM_MOVW_ABS_NC relocation (:lower16: in the .s file)
+ VK_ARM_HI16, // The R_ARM_MOVT_ABS relocation (:upper16: in the .s file)
+ VK_ARM_LO16, // The R_ARM_MOVW_ABS_NC relocation (:lower16: in the .s file)
+
+ VK_ARM_HI_8_15, // The R_ARM_THM_ALU_ABS_G3 relocation (:upper8_15: in
+ // the .s file)
+ VK_ARM_HI_0_7, // The R_ARM_THM_ALU_ABS_G2_NC relocation (:upper0_8: in the
+ // .s file)
+ VK_ARM_LO_8_15, // The R_ARM_THM_ALU_ABS_G1_NC relocation (:lower8_15: in
+ // the .s file)
+ VK_ARM_LO_0_7, // The R_ARM_THM_ALU_ABS_G0_NC relocation (:lower0_7: in the
+ // .s file)
};
private:
return create(VK_ARM_LO16, Expr, Ctx);
}
+ static const ARMMCExpr *createUpper8_15(const MCExpr *Expr, MCContext &Ctx) {
+ return create(VK_ARM_HI_8_15, Expr, Ctx);
+ }
+
+ static const ARMMCExpr *createUpper0_7(const MCExpr *Expr, MCContext &Ctx) {
+ return create(VK_ARM_HI_0_7, Expr, Ctx);
+ }
+
+ static const ARMMCExpr *createLower8_15(const MCExpr *Expr, MCContext &Ctx) {
+ return create(VK_ARM_LO_8_15, Expr, Ctx);
+ }
+
+ static const ARMMCExpr *createLower0_7(const MCExpr *Expr, MCContext &Ctx) {
+ return create(VK_ARM_LO_0_7, Expr, Ctx);
+ }
+
/// @}
/// @name Accessors
/// @{
ADDs r0, #0xFFFFFEFF
# CHECK: error: invalid instruction, any one of the following would fix this:
# CHECK-DAG: note: invalid operand for instruction
-# CHECK-DAG: note: operand must be an immediate in the range [0,255]
+# CHECK-DAG: note: operand must be an immediate in the range [0,255] or a relocatable expression
SUBs r1, r0, #0xFFFFFFF5
# CHECK: error: invalid instruction, any one of the following would fix this:
--- /dev/null
+@ RUN: llvm-mc -triple thumbv6m-eabi -o - %s | FileCheck %s
+@ RUN: llvm-mc -triple thumbv6m-eabi -filetype obj -o - %s | llvm-readobj -r - \
+@ RUN: | FileCheck -check-prefix CHECK-RELOCATIONS %s
+@ RUN: llvm-mc -triple thumbv7m-eabi -o - %s | FileCheck %s
+@ RUN: llvm-mc -triple thumbv7m-eabi -filetype obj -o - %s | llvm-readobj -r - \
+@ RUN: | FileCheck -check-prefix CHECK-RELOCATIONS %s
+
+.syntax unified
+
+.type function,%function
+function:
+ bx lr
+
+.global external
+.type external,%function
+
+.type test,%function
+test:
+ movs r3, :upper8_15:function
+ adds r3, :upper0_7:function
+ adds r3, :lower8_15:function
+ adds r3, :lower0_7:function
+
+@ CHECK-LABEL: test:
+@ CHECK: movs r3, :upper8_15:function
+@ CHECK: adds r3, :upper0_7:function
+@ CHECK: adds r3, :lower8_15:function
+@ CHECK: adds r3, :lower0_7:function
+
+@ CHECK-RELOCATIONS: Relocations [
+@ CHECK-RELOCATIONS: 0x2 R_ARM_THM_ALU_ABS_G3 function
+@ CHECK-RELOCATIONS-NEXT: 0x4 R_ARM_THM_ALU_ABS_G2_NC function
+@ CHECK-RELOCATIONS-NEXT: 0x6 R_ARM_THM_ALU_ABS_G1_NC function
+@ CHECK-RELOCATIONS-NEXT: 0x8 R_ARM_THM_ALU_ABS_G0_NC function
+@ CHECK-RELOCATIONS: ]
@ CHECK-ERRORS: note: operand must be an immediate in the range [0,31]
@ CHECK-ERRORS: note: too many operands for instruction
+@ Out of range immediates for MOVS/ADDS instruction.
+ movs r3, #-1
+ adds r3, #256
+@ CHECK-ERRORS: error: invalid instruction, any one of the following would fix this:
+@ CHECK-ERRORS-NEXT: movs r3, #-1
+@ CHECK-ERRORS-NEXT: ^
+@ CHECK-ERRORS: note: operand must be an immediate in the range [0,255] or a relocatable expression
+@ CHECK-ERRORS-NEXT: movs r3, #-1
+@ CHECK-ERRORS-NEXT: ^
+@ CHECK-ERRORS: note: operand must be a register in range [r0, r7]
+@ CHECK-ERRORS-NEXT: movs r3, #-1
+@ CHECK-ERRORS-NEXT: ^
+@ CHECK-ERRORS: error: invalid instruction, any one of the following would fix this:
+@ CHECK-ERRORS-NEXT: adds r3, #256
+@ CHECK-ERRORS-NEXT: ^
+@ CHECK-ERRORS: note: instruction requires: thumb2
+@ CHECK-ERRORS-NEXT: adds r3, #256
+@ CHECK-ERRORS-NEXT: ^
+@ CHECK-ERRORS: note: invalid operand for instruction
+@ CHECK-ERRORS-NEXT: adds r3, #256
+@ CHECK-ERRORS-NEXT: ^
+@ CHECK-ERRORS-NEXT: note: operand must be an immediate in the range [0,255] or a relocatable expression
+@ CHECK-ERRORS-NEXT: adds r3, #256
+@ CHECK-ERRORS-NEXT: ^
+@ CHECK-ERRORS-NEXT: note: operand must be a register in range [r0, r7]
+@ CHECK-ERRORS-NEXT: adds r3, #256
+@ CHECK-ERRORS-NEXT: ^
+
@ Mismatched source/destination operands for MUL instruction.
muls r1, r2, r3
@ CHECK-ERRORS: error: destination register must match source register
--- /dev/null
+@ RUN: llvm-mc -triple armv6m-unknown-unknown %s --show-encoding -o - | \
+@ RUN: FileCheck %s
+
+ movs r3, :upper8_15:_foo
+ adds r3, :upper0_7:_foo
+ adds r3, :lower8_15:_foo
+ adds r3, :lower0_7:_foo
+
+@ CHECK: movs r3, :upper8_15:_foo @ encoding: [A,0x23]
+@ CHECK-NEXT: @ fixup A - offset: 0, value: _foo, kind: fixup_arm_thumb_upper_8_15
+@ CHECK-NEXT: adds r3, :upper0_7:_foo @ encoding: [A,0x33]
+@ CHECK-NEXT: @ fixup A - offset: 0, value: _foo, kind: fixup_arm_thumb_upper_0_7
+@ CHECK-NEXT: adds r3, :lower8_15:_foo @ encoding: [A,0x33]
+@ CHECK-NEXT: @ fixup A - offset: 0, value: _foo, kind: fixup_arm_thumb_lower_8_15
+@ CHECK-NEXT: adds r3, :lower0_7:_foo @ encoding: [A,0x33]
+@ CHECK-NEXT: @ fixup A - offset: 0, value: _foo, kind: fixup_arm_thumb_lower_0_7
+
+@ GNU syntax variants:
+ movs r3, #:upper8_15:#_foo
+ movs r3, #:upper8_15:_foo
+
+@ CHECK: movs r3, :upper8_15:_foo @ encoding: [A,0x23]
+@ CHECK-NEXT: @ fixup A - offset: 0, value: _foo, kind: fixup_arm_thumb_upper_8_15
+@ CHECK-NEXT: movs r3, :upper8_15:_foo @ encoding: [A,0x23]
+@ CHECK-NEXT: @ fixup A - offset: 0, value: _foo, kind: fixup_arm_thumb_upper_8_15