diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCAsmBackend.cpp @@ -28,25 +28,43 @@ if (Kind < FirstTargetFixupKind) return Value; + auto checkFixupInRange = [&](int64_t Min, int64_t Max) -> bool { + int64_t SVal = int64_t(Value); + if (SVal < Min || SVal > Max) { + Ctx.reportError(Fixup.getLoc(), "operand out of range (" + Twine(SVal) + + " not between " + Twine(Min) + + " and " + Twine(Max) + ")"); + return false; + } + return true; + }; + + auto handlePCRelFixupValue = [&](unsigned W) -> uint64_t { + if (Value % 2 != 0) + Ctx.reportError(Fixup.getLoc(), "Non-even PC relative offset."); + if (!checkFixupInRange(minIntN(W) * 2, maxIntN(W) * 2)) + return 0; + return (int64_t)Value / 2; + }; + switch (unsigned(Kind)) { case SystemZ::FK_390_PC12DBL: + return handlePCRelFixupValue(12); case SystemZ::FK_390_PC16DBL: + return handlePCRelFixupValue(16); case SystemZ::FK_390_PC24DBL: + return handlePCRelFixupValue(24); case SystemZ::FK_390_PC32DBL: - return (int64_t)Value / 2; + return handlePCRelFixupValue(32); case SystemZ::FK_390_12: - if (!isUInt<12>(Value)) { - Ctx.reportError(Fixup.getLoc(), "displacement exceeds uint12"); + if (!checkFixupInRange(0, maxUIntN(12))) return 0; - } return Value; case SystemZ::FK_390_20: { - if (!isInt<20>(Value)) { - Ctx.reportError(Fixup.getLoc(), "displacement exceeds int20"); + if (!checkFixupInRange(minIntN(20), maxIntN(20))) return 0; - } // The high byte of a 20 bit displacement value comes first. uint64_t DLo = Value & 0xfff; uint64_t DHi = (Value >> 12) & 0xff; diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCCodeEmitter.cpp b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCCodeEmitter.cpp --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCCodeEmitter.cpp +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZMCCodeEmitter.cpp @@ -197,7 +197,8 @@ // All instructions follow the pattern where the first displacement has a // 2 bytes offset, and the second one 4 bytes. unsigned ByteOffs = Fixups.size() == 0 ? 2 : 4; - Fixups.push_back(MCFixup::create(ByteOffs, MO.getExpr(), (MCFixupKind)Kind)); + Fixups.push_back(MCFixup::create(ByteOffs, MO.getExpr(), (MCFixupKind)Kind, + MI.getLoc())); assert(Fixups.size() <= 2 && "More than two memory operands in MI?"); return 0; } @@ -296,6 +297,7 @@ SmallVectorImpl &Fixups, unsigned Kind, int64_t Offset, bool AllowTLS) const { + SMLoc Loc = MI.getLoc(); const MCOperand &MO = MI.getOperand(OpNum); const MCExpr *Expr; if (MO.isImm()) @@ -311,13 +313,13 @@ Expr = MCBinaryExpr::createAdd(Expr, OffsetExpr, Ctx); } } - Fixups.push_back(MCFixup::create(Offset, Expr, (MCFixupKind)Kind)); + Fixups.push_back(MCFixup::create(Offset, Expr, (MCFixupKind)Kind, Loc)); // Output the fixup for the TLS marker if present. if (AllowTLS && OpNum + 1 < MI.getNumOperands()) { const MCOperand &MOTLS = MI.getOperand(OpNum + 1); - Fixups.push_back(MCFixup::create(0, MOTLS.getExpr(), - (MCFixupKind)SystemZ::FK_390_TLS_CALL)); + Fixups.push_back(MCFixup::create( + 0, MOTLS.getExpr(), (MCFixupKind)SystemZ::FK_390_TLS_CALL, Loc)); } return 0; } diff --git a/llvm/test/MC/SystemZ/fixups-out-of-range.s b/llvm/test/MC/SystemZ/fixups-out-of-range-01.s rename from llvm/test/MC/SystemZ/fixups-out-of-range.s rename to llvm/test/MC/SystemZ/fixups-out-of-range-01.s --- a/llvm/test/MC/SystemZ/fixups-out-of-range.s +++ b/llvm/test/MC/SystemZ/fixups-out-of-range-01.s @@ -2,10 +2,14 @@ .text -# CHECK: error: displacement exceeds uint12 +# CHECK: error: operand out of range (4096 not between 0 and 4095) +# CHECK-NEXT: la %r1, b-a(%r1) +# CHECK-NEXT: ^ la %r1, b-a(%r1) -# CHECK: error: displacement exceeds int20 +# CHECK-NEXT: error: operand out of range (524288 not between -524288 and 524287) +# CHECK-NEXT: lay %r1, d-c(%r1) +# CHECK-NEXT: ^ lay %r1, d-c(%r1) # CHECK-NOT: error diff --git a/llvm/test/MC/SystemZ/fixups-out-of-range-02.s b/llvm/test/MC/SystemZ/fixups-out-of-range-02.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/SystemZ/fixups-out-of-range-02.s @@ -0,0 +1,60 @@ +# RUN: not llvm-mc -triple s390x-unknown-unknown -filetype=obj -mcpu=zEC12 \ +# RUN: -o /dev/null %s 2>&1 | FileCheck %s + + .text + +# Test fixup ranges, which are encoded as half-words. + +# 12-bit +# CHECK: error: operand out of range (4096 not between -4096 and 4094) +# CHECK-NEXT: bprp 0, .Lab1, 0 +# CHECK-NEXT: ^ +# CHECK-NEXT: error: operand out of range (-4098 not between -4096 and 4094) +# CHECK-NEXT: bprp 0, .Lab0, 0 +# CHECK-NEXT: ^ + bprp 0, .Lab1, 0 +.Lab0: + bprp 0, .Lab1, 0 + .space 4084 +.Lab1: + nopr + bprp 0, .Lab0, 0 + bprp 0, .Lab0, 0 + +# 24-bit +# CHECK-NEXT: error: operand out of range (16777220 not between -16777216 and 16777214) +# CHECK-NEXT: bprp 0, 0, .Lab3 +# CHECK-NEXT: ^ +# CHECK-NEXT: error: operand out of range (-16777222 not between -16777216 and 16777214) +# CHECK-NEXT: bprp 0, 0, .Lab2 +# CHECK-NEXT: ^ + bprp 0, 0, .Lab3 +.Lab2: + bprp 0, 0, .Lab3 + .space 16777208 +.Lab3: + nopr + bprp 0, 0, .Lab2 + bprp 0, 0, .Lab2 + +# 16-bit +# CHECK-NEXT: error: operand out of range (65540 not between -65536 and 65534) +# CHECK-NEXT: cij %r1, 0, 0, .Lab5 +# CHECK-NEXT: ^ +# CHECK-NEXT: error: operand out of range (-65542 not between -65536 and 65534) +# CHECK-NEXT: cij %r1, 0, 0, .Lab4 +# CHECK-NEXT: ^ + cij %r1, 0, 0, .Lab5 +.Lab4: + cij %r1, 0, 0, .Lab5 + .space 65528 +.Lab5: + nopr + cij %r1, 0, 0, .Lab4 + cij %r1, 0, 0, .Lab4 + +# 32-bit +# Unfortunately there is no support for offsets greater than 32 bits, so we have +# to for now assume they are in range. + +# CHECK-NOT: error