diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def --- a/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def @@ -135,6 +135,10 @@ 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) diff --git a/llvm/lib/Target/ARM/ARMInstrInfo.td b/llvm/lib/Target/ARM/ARMInstrInfo.td --- a/llvm/lib/Target/ARM/ARMInstrInfo.td +++ b/llvm/lib/Target/ARM/ARMInstrInfo.td @@ -965,6 +965,19 @@ 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, ImmLeaf= 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, ImmLeaf { - let EncoderMethod = "getHiLo16ImmOpValue"; +def imm0_65535_expr : Operand, ImmLeaf= 0 && Imm < 65536; +}]> { + let EncoderMethod = "getHiLoImmOpValue"; let ParserMatchClass = Imm0_65535ExprAsmOperand; } diff --git a/llvm/lib/Target/ARM/ARMInstrThumb.td b/llvm/lib/Target/ARM/ARMInstrThumb.td --- a/llvm/lib/Target/ARM/ARMInstrThumb.td +++ b/llvm/lib/Target/ARM/ARMInstrThumb.td @@ -984,9 +984,9 @@ 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 @@ -995,7 +995,8 @@ 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. @@ -1018,10 +1019,10 @@ 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]>; @@ -1196,9 +1197,9 @@ // 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; @@ -1208,8 +1209,8 @@ } // 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. diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -1232,6 +1232,18 @@ return isImmediate<8, 255>(); } + bool isImm0_255Expr() const { + if (!isImm()) + return false; + const MCConstantExpr *CE = dyn_cast(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(getImm()); @@ -6272,7 +6284,8 @@ } 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; @@ -6319,8 +6332,9 @@ 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; @@ -6329,7 +6343,6 @@ if (getLexer().is(AsmToken::Hash)) Parser.Lex(); - // :lower16: and :upper16: modifiers assert(getLexer().is(AsmToken::Colon) && "expected a :"); Parser.Lex(); // Eat ':' @@ -6349,8 +6362,12 @@ 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(); @@ -6401,6 +6418,9 @@ } Parser.Lex(); // Eat the last ':' + // consume an optional trailing '#' (GNU compatibility) bla + parseOptionalToken(AsmToken::Hash); + return false; } @@ -6711,6 +6731,27 @@ } } +// 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(MCOp); + if (!Op.isImm()) + return false; + const MCConstantExpr *CE = dyn_cast(Op.getImm()); + if (CE) + return false; + const MCExpr *E = dyn_cast(Op.getImm()); + if (!E) + return false; + const ARMMCExpr *ARM16Expr = dyn_cast(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 @@ -6730,6 +6771,10 @@ static_cast(*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 && @@ -7614,6 +7659,19 @@ return findFirstVectorPredOperandIdx(MCID) != -1; } +static bool isARMMCExpr(MCParsedAsmOperand &MCOp) { + ARMOperand &Op = static_cast(MCOp); + if (!Op.isImm()) + return false; + const MCConstantExpr *CE = dyn_cast(Op.getImm()); + if (CE) + return false; + const MCExpr *E = dyn_cast(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) { @@ -8223,6 +8281,22 @@ "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(); @@ -10541,7 +10615,8 @@ // explicitly specified. From the ARM ARM: "Encoding T1 is preferred // to encoding T2 if is specified and encoding T2 is preferred // to encoding T1 if 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; } diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp @@ -114,6 +114,10 @@ {"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}, @@ -168,6 +172,10 @@ {"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}, @@ -487,6 +495,20 @@ 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; @@ -931,6 +953,10 @@ 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: @@ -1001,6 +1027,10 @@ 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; diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp @@ -137,6 +137,14 @@ 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: @@ -265,6 +273,15 @@ 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; } } diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h b/llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h @@ -99,6 +99,12 @@ 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, diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp @@ -87,12 +87,13 @@ SmallVectorImpl &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 &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 &Fixups, + const MCSubtargetInfo &STI) const; bool EncodeAddrModeOpValues(const MCInst &MI, unsigned OpIdx, unsigned &Reg, unsigned &Imm, @@ -1189,18 +1190,18 @@ return (Reg << 8) | Imm8; } -uint32_t -ARMMCCodeEmitter::getHiLo16ImmOpValue(const MCInst &MI, unsigned OpIdx, - SmallVectorImpl &Fixups, - const MCSubtargetInfo &STI) const { +uint32_t ARMMCCodeEmitter::getHiLoImmOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl &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(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) { @@ -1217,6 +1218,16 @@ 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"); } } @@ -1231,18 +1242,39 @@ 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:: diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h @@ -17,8 +17,17 @@ 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: @@ -43,6 +52,22 @@ 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 /// @{ diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp @@ -22,8 +22,24 @@ 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(); diff --git a/llvm/test/MC/ARM/negative-immediates-thumb1-fail.s b/llvm/test/MC/ARM/negative-immediates-thumb1-fail.s --- a/llvm/test/MC/ARM/negative-immediates-thumb1-fail.s +++ b/llvm/test/MC/ARM/negative-immediates-thumb1-fail.s @@ -12,7 +12,7 @@ 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: diff --git a/llvm/test/MC/ARM/thumb-8-bit-relocs.s b/llvm/test/MC/ARM/thumb-8-bit-relocs.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/thumb-8-bit-relocs.s @@ -0,0 +1,35 @@ +@ 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: ] diff --git a/llvm/test/MC/ARM/thumb-diagnostics.s b/llvm/test/MC/ARM/thumb-diagnostics.s --- a/llvm/test/MC/ARM/thumb-diagnostics.s +++ b/llvm/test/MC/ARM/thumb-diagnostics.s @@ -195,6 +195,34 @@ @ 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 diff --git a/llvm/test/MC/ARM/thumb-fixups.s b/llvm/test/MC/ARM/thumb-fixups.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/thumb-fixups.s @@ -0,0 +1,25 @@ +@ 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