diff --git a/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp b/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp index 93c4ce4b5ccc79..b58d20fc49ba20 100644 --- a/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp +++ b/llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp @@ -1304,14 +1304,23 @@ SystemZAsmParser::parsePCRel(OperandVector &Operands, int64_t MinVal, if (getParser().parseExpression(Expr)) return MatchOperand_NoMatch; + auto isOutOfRangeConstant = [&](const MCExpr *E) -> bool { + if (auto *CE = dyn_cast(E)) { + int64_t Value = CE->getValue(); + if ((Value & 1) || Value < MinVal || Value > MaxVal) + return true; + } + return false; + }; + // For consistency with the GNU assembler, treat immediates as offsets // from ".". if (auto *CE = dyn_cast(Expr)) { - int64_t Value = CE->getValue(); - if ((Value & 1) || Value < MinVal || Value > MaxVal) { + if (isOutOfRangeConstant(CE)) { Error(StartLoc, "offset out of range"); return MatchOperand_ParseFail; } + int64_t Value = CE->getValue(); MCSymbol *Sym = Ctx.createTempSymbol(); Out.EmitLabel(Sym); const MCExpr *Base = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, @@ -1319,6 +1328,15 @@ SystemZAsmParser::parsePCRel(OperandVector &Operands, int64_t MinVal, Expr = Value == 0 ? Base : MCBinaryExpr::createAdd(Base, Expr, Ctx); } + // For consistency with the GNU assembler, conservatively assume that a + // constant offset must by itself be within the given size range. + if (const auto *BE = dyn_cast(Expr)) + if (isOutOfRangeConstant(BE->getLHS()) || + isOutOfRangeConstant(BE->getRHS())) { + Error(StartLoc, "offset out of range"); + return MatchOperand_ParseFail; + } + // Optionally match :tls_gdcall: or :tls_ldcall: followed by a TLS symbol. const MCExpr *Sym = nullptr; if (AllowTLS && getLexer().is(AsmToken::Colon)) { diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp index e0ca9da93561c6..8e71d83425629e 100644 --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -2828,17 +2828,26 @@ SDValue SystemZTargetLowering::lowerGlobalAddress(GlobalAddressSDNode *Node, SDValue Result; if (Subtarget.isPC32DBLSymbol(GV, CM)) { - // Assign anchors at 1<<12 byte boundaries. - uint64_t Anchor = Offset & ~uint64_t(0xfff); - Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, Anchor); - Result = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Result); - - // The offset can be folded into the address if it is aligned to a halfword. - Offset -= Anchor; - if (Offset != 0 && (Offset & 1) == 0) { - SDValue Full = DAG.getTargetGlobalAddress(GV, DL, PtrVT, Anchor + Offset); - Result = DAG.getNode(SystemZISD::PCREL_OFFSET, DL, PtrVT, Full, Result); - Offset = 0; + if (isInt<32>(Offset)) { + // Assign anchors at 1<<12 byte boundaries. + uint64_t Anchor = Offset & ~uint64_t(0xfff); + Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, Anchor); + Result = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Result); + + // The offset can be folded into the address if it is aligned to a + // halfword. + Offset -= Anchor; + if (Offset != 0 && (Offset & 1) == 0) { + SDValue Full = + DAG.getTargetGlobalAddress(GV, DL, PtrVT, Anchor + Offset); + Result = DAG.getNode(SystemZISD::PCREL_OFFSET, DL, PtrVT, Full, Result); + Offset = 0; + } + } else { + // Conservatively load a constant offset greater than 32 bits into a + // register below. + Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT); + Result = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Result); } } else { Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, SystemZII::MO_GOT); diff --git a/llvm/test/CodeGen/SystemZ/la-05.ll b/llvm/test/CodeGen/SystemZ/la-05.ll new file mode 100644 index 00000000000000..27d7d91e6c8ab1 --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/la-05.ll @@ -0,0 +1,31 @@ +; Test that a huge address offset is loaded into a register and then added +; separately. +; +; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s + +@a = common dso_local global i32 0, align 4 + +define i64 @f1() { +; CHECK-LABEL: f1: +; CHECK: llihl %r0, 829 +; CHECK: oilf %r0, 4294966308 +; CHECK: larl %r2, a +; CHECK: agr %r2, %r0 +; CHECK: br %r14 + ret i64 add (i64 ptrtoint (i32* @a to i64), i64 3564822854692) +} + +define signext i32 @f2() { +; CHECK-LABEL: f2: +; CHECK: llihl %r0, 829 +; CHECK: oilf %r0, 4294966308 +; CHECK: larl %r1, a +; CHECK: agr %r1, %r0 +; CHECK: lgf %r2, 0(%r1) +; CHECK: br %r14 +entry: + %0 = load i32, i32* inttoptr (i64 add (i64 ptrtoint (i32* @a to i64), + i64 3564822854692) to i32*) + ret i32 %0 +} + diff --git a/llvm/test/MC/SystemZ/insn-bad.s b/llvm/test/MC/SystemZ/insn-bad.s index 57c69f60361bf1..7dfbba743b60eb 100644 --- a/llvm/test/MC/SystemZ/insn-bad.s +++ b/llvm/test/MC/SystemZ/insn-bad.s @@ -3104,11 +3104,14 @@ #CHECK: larl %r0, 1 #CHECK: error: offset out of range #CHECK: larl %r0, 0x100000000 +#CHECK: error: offset out of range +#CHECK: larl %r1, __unnamed_1+3564822854692 larl %r0, -0x1000000002 larl %r0, -1 larl %r0, 1 larl %r0, 0x100000000 + larl %r1, __unnamed_1+3564822854692 #CHECK: error: invalid use of indexed addressing #CHECK: lasp 160(%r1,%r15),160(%r15) @@ -3840,11 +3843,14 @@ #CHECK: lrl %r0, 1 #CHECK: error: offset out of range #CHECK: lrl %r0, 0x100000000 +#CHECK: error: offset out of range +#CHECK: lrl %r1, __unnamed_1+3564822854692 lrl %r0, -0x1000000002 lrl %r0, -1 lrl %r0, 1 lrl %r0, 0x100000000 + lrl %r1, __unnamed_1+3564822854692 #CHECK: error: invalid operand #CHECK: lrv %r0, -524289