diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index 2b436d4181326..848361e0fb9be 100644 --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -970,11 +970,15 @@ class AArch64Operand : public MCParsedAsmOperand { bool isMOVZMovAlias() const { if (!isImm()) return false; - const MCConstantExpr *CE = dyn_cast(getImm()); - if (!CE) return false; - uint64_t Value = CE->getValue(); + const MCExpr *E = getImm(); + if (const MCConstantExpr *CE = dyn_cast(E)) { + uint64_t Value = CE->getValue(); - return AArch64_AM::isMOVZMovAlias(Value, Shift, RegWidth); + return AArch64_AM::isMOVZMovAlias(Value, Shift, RegWidth); + } + // Only supports the case of Shift being 0 if an expression is used as an + // operand + return !Shift && E; } template @@ -1774,9 +1778,13 @@ class AArch64Operand : public MCParsedAsmOperand { void addMOVZMovAliasOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); - const MCConstantExpr *CE = cast(getImm()); - uint64_t Value = CE->getValue(); - Inst.addOperand(MCOperand::createImm((Value >> Shift) & 0xffff)); + const MCConstantExpr *CE = dyn_cast(getImm()); + if (CE) { + uint64_t Value = CE->getValue(); + Inst.addOperand(MCOperand::createImm((Value >> Shift) & 0xffff)); + } else { + addExpr(Inst, getImm()); + } } template diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp index a5e78f19aeba6..9f7dfdf624829 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp @@ -243,11 +243,22 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, const MCValue &Target, static_cast(Target.getRefKind()); if (AArch64MCExpr::getSymbolLoc(RefKind) != AArch64MCExpr::VK_ABS && AArch64MCExpr::getSymbolLoc(RefKind) != AArch64MCExpr::VK_SABS) { - // VK_GOTTPREL, VK_TPREL, VK_DTPREL are movw fixups, but they can't - // ever be resolved in the assembler. - Ctx.reportError(Fixup.getLoc(), - "relocation for a thread-local variable points to an " - "absolute symbol"); + if (!RefKind) { + // The fixup is an expression + if (SignedValue > 0xFFFF || SignedValue < -0xFFFF) + Ctx.reportError(Fixup.getLoc(), + "fixup value out of range [-0xFFFF, 0xFFFF]"); + + // Invert the negative immediate because it will feed into a MOVN. + if (SignedValue < 0) + SignedValue = ~SignedValue; + Value = static_cast(SignedValue); + } else + // VK_GOTTPREL, VK_TPREL, VK_DTPREL are movw fixups, but they can't + // ever be resolved in the assembler. + Ctx.reportError(Fixup.getLoc(), + "relocation for a thread-local variable points to an " + "absolute symbol"); return Value; } @@ -440,8 +451,9 @@ void AArch64AsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, // FIXME: getFixupKindInfo() and getFixupKindNumBytes() could be fixed to // handle this more cleanly. This may affect the output of -show-mc-encoding. AArch64MCExpr::VariantKind RefKind = - static_cast(Target.getRefKind()); - if (AArch64MCExpr::getSymbolLoc(RefKind) == AArch64MCExpr::VK_SABS) { + static_cast(Target.getRefKind()); + if (AArch64MCExpr::getSymbolLoc(RefKind) == AArch64MCExpr::VK_SABS || + (!RefKind && Fixup.getTargetKind() == AArch64::fixup_aarch64_movw)) { // If the immediate is negative, generate MOVN else MOVZ. // (Bit 30 = 0) ==> MOVN, (Bit 30 = 1) ==> MOVZ. if (SignedValue < 0) diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCCodeEmitter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCCodeEmitter.cpp index 8f4d9cb94d607..da8f511c650f0 100644 --- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCCodeEmitter.cpp +++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64MCCodeEmitter.cpp @@ -569,23 +569,24 @@ unsigned AArch64MCCodeEmitter::fixMOVZ(const MCInst &MI, unsigned EncodedValue, if (UImm16MO.isImm()) return EncodedValue; - const AArch64MCExpr *A64E = cast(UImm16MO.getExpr()); - switch (A64E->getKind()) { - case AArch64MCExpr::VK_DTPREL_G2: - case AArch64MCExpr::VK_DTPREL_G1: - case AArch64MCExpr::VK_DTPREL_G0: - case AArch64MCExpr::VK_GOTTPREL_G1: - case AArch64MCExpr::VK_TPREL_G2: - case AArch64MCExpr::VK_TPREL_G1: - case AArch64MCExpr::VK_TPREL_G0: - return EncodedValue & ~(1u << 30); - default: - // Nothing to do for an unsigned fixup. - return EncodedValue; + const MCExpr *E = UImm16MO.getExpr(); + if (const AArch64MCExpr *A64E = dyn_cast(E)) { + switch (A64E->getKind()) { + case AArch64MCExpr::VK_DTPREL_G2: + case AArch64MCExpr::VK_DTPREL_G1: + case AArch64MCExpr::VK_DTPREL_G0: + case AArch64MCExpr::VK_GOTTPREL_G1: + case AArch64MCExpr::VK_TPREL_G2: + case AArch64MCExpr::VK_TPREL_G1: + case AArch64MCExpr::VK_TPREL_G0: + return EncodedValue & ~(1u << 30); + default: + // Nothing to do for an unsigned fixup. + return EncodedValue; + } } - - return EncodedValue & ~(1u << 30); + return EncodedValue; } void AArch64MCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, diff --git a/llvm/test/MC/AArch64/basic-a64-diagnostics.s b/llvm/test/MC/AArch64/basic-a64-diagnostics.s index 067acbfc012a7..9114d30bc3d8a 100644 --- a/llvm/test/MC/AArch64/basic-a64-diagnostics.s +++ b/llvm/test/MC/AArch64/basic-a64-diagnostics.s @@ -167,9 +167,9 @@ // MOV alias should not accept any fiddling mov x2, xsp, #123 mov wsp, w27, #0xfff, lsl #12 -// CHECK-ERROR: error: expected compatible register or logical immediate +// CHECK-ERROR: error: invalid operand for instruction // CHECK-ERROR-NEXT: mov x2, xsp, #123 -// CHECK-ERROR-NEXT: ^ +// CHECK-ERROR-NEXT: ^ // CHECK-ERROR-NEXT: error: invalid operand for instruction // CHECK-ERROR-NEXT: mov wsp, w27, #0xfff, lsl #12 // CHECK-ERROR-NEXT: ^ diff --git a/llvm/test/MC/AArch64/mov-expr-as-immediate.s b/llvm/test/MC/AArch64/mov-expr-as-immediate.s new file mode 100644 index 0000000000000..bc3adc2923121 --- /dev/null +++ b/llvm/test/MC/AArch64/mov-expr-as-immediate.s @@ -0,0 +1,16 @@ +// RUN: llvm-mc -triple aarch64-none-linux-gnu %s -filetype=obj -o %t | llvm-objdump --triple aarch64-none-linux-gnu -Dr %t | FileCheck %s + +0: +.skip 4 +1: +mov x0, 1b - 0b +// CHECK: mov x0, #4 +mov x0, 0b - 1b +// CHECK: mov x0, #-4 +mov x0, 0b - 0b +// CHECK: mov x0, #0 +mov x0, 1b - 2 - 0b + 6 +// CHECK: mov x0, #8 +mov x0, #:abs_g0_s:1b +// CHECK: mov x0, #0 +// CHECK-NEXT: R_AARCH64_MOVW_SABS_G0 .text+0x4 diff --git a/llvm/test/MC/AArch64/mov-expression-as-immediate.s b/llvm/test/MC/AArch64/mov-expression-as-immediate.s new file mode 100644 index 0000000000000..9426293fccdeb --- /dev/null +++ b/llvm/test/MC/AArch64/mov-expression-as-immediate.s @@ -0,0 +1,9 @@ +// RUN: llvm-mc -triple aarch64-none-linux-gnu %s -filetype=obj -o %t | llvm-objdump -d %t | FileCheck %s + +0: +.skip 4 +1: +mov x0, 1b - 0b +// CHECK: mov x0, #4 +mov x0, 0b - 1b +// CHECK: mov x0, #-4 diff --git a/llvm/test/MC/AArch64/mov-unsupported-expr-as-immediate.s b/llvm/test/MC/AArch64/mov-unsupported-expr-as-immediate.s new file mode 100644 index 0000000000000..5327ab744b768 --- /dev/null +++ b/llvm/test/MC/AArch64/mov-unsupported-expr-as-immediate.s @@ -0,0 +1,17 @@ +// RUN: not llvm-mc -triple aarch64-none-linux-gnu %s -filetype=obj -o /dev/null 2>&1 | FileCheck %s + +0: +.skip 0x10000 +1: +mov x0, 1b - 0b +// CHECK: error: fixup value out of range +// CHECK: mov x0, 1b - 0b +// CHECK: ^ +mov x0, 0b - 1b +// CHECK: error: fixup value out of range +// CHECK: mov x0, 0b - 1b +// CHECK: ^ +mov x0, 1b +// CHECK: error: invalid fixup for movz/movk instruction +// CHECK: mov x0, 1b +// CHECK: ^