[ARM] add Thumb-1 8-bit movs/adds relocations to LLVM
authorTies Stuij <ties.stuij@arm.com>
Thu, 22 Jun 2023 14:47:42 +0000 (15:47 +0100)
committerTies Stuij <ties.stuij@arm.com>
Thu, 22 Jun 2023 15:35:13 +0000 (16:35 +0100)
This patch adds the LLVM-side plumbing for the following relocations:
- R_ARM_THM_ALU_ABS_G0_NC
- R_ARM_THM_ALU_ABS_G1_NC
- R_ARM_THM_ALU_ABS_G2_NC
- R_ARM_THM_ALU_ABS_G3

(see section 5.6.1.5, Static Thumb16 relocations, of the AArch32 ELF Arm ABI:
https://github.com/ARM-software/abi-aa/blob/844a79fd4c77252a11342709e3b27b2c9f590cf1/aaelf32/aaelf32.rst#5615static-thumb16-relocations)

Which can respectivly be generated by prefixing assembly symbols with:
- :lower0_7:
- :lower8_15:
- :upper0_7:
- :upper8_15:

LLD support for these relocations will be added in a follow-up patch

Reviewed By: john.brawn, MaskRay

Differential Revision: https://reviews.llvm.org/D149443

14 files changed:
llvm/include/llvm/BinaryFormat/ELFRelocs/ARM.def
llvm/lib/Target/ARM/ARMInstrInfo.td
llvm/lib/Target/ARM/ARMInstrThumb.td
llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp
llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
llvm/lib/Target/ARM/MCTargetDesc/ARMELFObjectWriter.cpp
llvm/lib/Target/ARM/MCTargetDesc/ARMFixupKinds.h
llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp
llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.cpp
llvm/lib/Target/ARM/MCTargetDesc/ARMMCExpr.h
llvm/test/MC/ARM/negative-immediates-thumb1-fail.s
llvm/test/MC/ARM/thumb-8-bit-relocs.s [new file with mode: 0644]
llvm/test/MC/ARM/thumb-diagnostics.s
llvm/test/MC/ARM/thumb-fixups.s [new file with mode: 0644]

index e0709fb..47084d1 100644 (file)
@@ -135,6 +135,10 @@ ELF_RELOC(R_ARM_PRIVATE_15,             0x7f)
 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)
index a32478e..471b706 100644 (file)
@@ -965,6 +965,19 @@ def imm0_255 : Operand<i32>, ImmLeaf<i32, [{ return Imm >= 0 && Imm < 256; }]> {
   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, [{
@@ -990,8 +1003,10 @@ def Imm0_65535ExprAsmOperand: AsmOperandClass {
   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;
 }
 
index bb03d2e..7849133 100644 (file)
@@ -984,9 +984,9 @@ let isAdd = 1 in {
 
   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 @@ let isAdd = 1 in {
     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 @@ let isAdd = 1 in {
                   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 @@ def tLSRrr :                    // A8.6.91
 
 // 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 @@ def tMOVi8 : T1sI<(outs tGPR:$Rd), (ins imm0_255:$imm8), IIC_iMOVi,
 }
 // 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.
 
index 4007161..7820cfd 100644 (file)
@@ -1232,6 +1232,18 @@ public:
     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());
@@ -6272,7 +6284,8 @@ bool ARMAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) {
   }
   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 @@ bool ARMAsmParser::parseImmExpr(int64_t &Out) {
   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 @@ bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) {
   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 @@ bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) {
     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 @@ bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) {
   }
   Parser.Lex(); // Eat the last ':'
 
+  // consume an optional trailing '#' (GNU compatibility) bla
+  parseOptionalToken(AsmToken::Hash);
+
   return false;
 }
 
@@ -6711,6 +6731,27 @@ void ARMAsmParser::tryConvertingToTwoOperandForm(StringRef Mnemonic,
   }
 }
 
+// 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
@@ -6730,6 +6771,10 @@ bool ARMAsmParser::shouldOmitCCOutOperand(StringRef Mnemonic,
       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 &&
@@ -7614,6 +7659,19 @@ static bool isVectorPredicable(const MCInstrDesc &MCID) {
   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) {
@@ -8223,6 +8281,22 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
           "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 @@ bool ARMAsmParser::processInstruction(MCInst &Inst,
     // 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;
     }
index ea13634..7016918 100644 (file)
@@ -114,6 +114,10 @@ const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
       {"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 @@ const MCFixupKindInfo &ARMAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
       {"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 @@ unsigned ARMAsmBackend::adjustFixupValue(const MCAssembler &Asm,
     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 @@ static unsigned getFixupKindNumBytes(unsigned Kind) {
   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 @@ static unsigned getFixupKindContainerSizeBytes(unsigned Kind) {
   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;
 
index 2a6bda6..caebace 100644 (file)
@@ -137,6 +137,14 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
       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 @@ unsigned ARMELFObjectWriter::GetRelocTypeInner(const MCValue &Target,
     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;
   }
 }
 
index 53258a8..3bcea57 100644 (file)
@@ -99,6 +99,12 @@ enum Fixups {
   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,
 
index 6843b13..dae323e 100644 (file)
@@ -87,12 +87,13 @@ public:
                              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,
@@ -1189,18 +1190,18 @@ getT2AddrModeImm0_1020s4OpValue(const MCInst &MI, unsigned OpIdx,
   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) {
@@ -1217,6 +1218,16 @@ ARMMCCodeEmitter::getHiLo16ImmOpValue(const MCInst &MI, unsigned OpIdx,
         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 @@ ARMMCCodeEmitter::getHiLo16ImmOpValue(const MCInst &MI, unsigned OpIdx,
       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::
index fbad05f..6be308f 100644 (file)
@@ -22,8 +22,24 @@ ARMMCExpr::create(VariantKind Kind, const MCExpr *Expr,
 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();
index 033a432..edeff9c 100644 (file)
@@ -17,8 +17,17 @@ class ARMMCExpr : public MCTargetExpr {
 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 @@ public:
     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
   /// @{
index 70e01ff..5fca211 100644 (file)
@@ -12,7 +12,7 @@ ADDs r1, r0, #0xFFFFFFF5
 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 (file)
index 0000000..cd13df9
--- /dev/null
@@ -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: ]
index 7179bd8..cacd7f2 100644 (file)
 @ 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 (file)
index 0000000..b2d54df
--- /dev/null
@@ -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