Skip to content

Commit 580310f

Browse files
committed
[SystemZ] Improve handling of huge PC relative immediate offsets.
Demand that an immediate offset to a PC relative address fits in 32 bits, or else load it into a register and perform a separate add. Verify in the assembler that such immediate offsets fit the bitwidth. Even though the final address of a Load Address Relative Long may fit in 32 bits even with a >32 bit offset (depending on where the symbol lives relative to PC), the GNU toolchain demands the offset by itself to be in range. This patch adapts the same behavior for llvm. Review: Ulrich Weigand https://reviews.llvm.org/D69749
1 parent 2be1708 commit 580310f

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

llvm/lib/Target/SystemZ/AsmParser/SystemZAsmParser.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,21 +1304,39 @@ SystemZAsmParser::parsePCRel(OperandVector &Operands, int64_t MinVal,
13041304
if (getParser().parseExpression(Expr))
13051305
return MatchOperand_NoMatch;
13061306

1307+
auto isOutOfRangeConstant = [&](const MCExpr *E) -> bool {
1308+
if (auto *CE = dyn_cast<MCConstantExpr>(E)) {
1309+
int64_t Value = CE->getValue();
1310+
if ((Value & 1) || Value < MinVal || Value > MaxVal)
1311+
return true;
1312+
}
1313+
return false;
1314+
};
1315+
13071316
// For consistency with the GNU assembler, treat immediates as offsets
13081317
// from ".".
13091318
if (auto *CE = dyn_cast<MCConstantExpr>(Expr)) {
1310-
int64_t Value = CE->getValue();
1311-
if ((Value & 1) || Value < MinVal || Value > MaxVal) {
1319+
if (isOutOfRangeConstant(CE)) {
13121320
Error(StartLoc, "offset out of range");
13131321
return MatchOperand_ParseFail;
13141322
}
1323+
int64_t Value = CE->getValue();
13151324
MCSymbol *Sym = Ctx.createTempSymbol();
13161325
Out.EmitLabel(Sym);
13171326
const MCExpr *Base = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None,
13181327
Ctx);
13191328
Expr = Value == 0 ? Base : MCBinaryExpr::createAdd(Base, Expr, Ctx);
13201329
}
13211330

1331+
// For consistency with the GNU assembler, conservatively assume that a
1332+
// constant offset must by itself be within the given size range.
1333+
if (const auto *BE = dyn_cast<MCBinaryExpr>(Expr))
1334+
if (isOutOfRangeConstant(BE->getLHS()) ||
1335+
isOutOfRangeConstant(BE->getRHS())) {
1336+
Error(StartLoc, "offset out of range");
1337+
return MatchOperand_ParseFail;
1338+
}
1339+
13221340
// Optionally match :tls_gdcall: or :tls_ldcall: followed by a TLS symbol.
13231341
const MCExpr *Sym = nullptr;
13241342
if (AllowTLS && getLexer().is(AsmToken::Colon)) {

llvm/lib/Target/SystemZ/SystemZISelLowering.cpp

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2828,17 +2828,26 @@ SDValue SystemZTargetLowering::lowerGlobalAddress(GlobalAddressSDNode *Node,
28282828

28292829
SDValue Result;
28302830
if (Subtarget.isPC32DBLSymbol(GV, CM)) {
2831-
// Assign anchors at 1<<12 byte boundaries.
2832-
uint64_t Anchor = Offset & ~uint64_t(0xfff);
2833-
Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, Anchor);
2834-
Result = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Result);
2835-
2836-
// The offset can be folded into the address if it is aligned to a halfword.
2837-
Offset -= Anchor;
2838-
if (Offset != 0 && (Offset & 1) == 0) {
2839-
SDValue Full = DAG.getTargetGlobalAddress(GV, DL, PtrVT, Anchor + Offset);
2840-
Result = DAG.getNode(SystemZISD::PCREL_OFFSET, DL, PtrVT, Full, Result);
2841-
Offset = 0;
2831+
if (isInt<32>(Offset)) {
2832+
// Assign anchors at 1<<12 byte boundaries.
2833+
uint64_t Anchor = Offset & ~uint64_t(0xfff);
2834+
Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, Anchor);
2835+
Result = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Result);
2836+
2837+
// The offset can be folded into the address if it is aligned to a
2838+
// halfword.
2839+
Offset -= Anchor;
2840+
if (Offset != 0 && (Offset & 1) == 0) {
2841+
SDValue Full =
2842+
DAG.getTargetGlobalAddress(GV, DL, PtrVT, Anchor + Offset);
2843+
Result = DAG.getNode(SystemZISD::PCREL_OFFSET, DL, PtrVT, Full, Result);
2844+
Offset = 0;
2845+
}
2846+
} else {
2847+
// Conservatively load a constant offset greater than 32 bits into a
2848+
// register below.
2849+
Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT);
2850+
Result = DAG.getNode(SystemZISD::PCREL_WRAPPER, DL, PtrVT, Result);
28422851
}
28432852
} else {
28442853
Result = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, SystemZII::MO_GOT);

llvm/test/CodeGen/SystemZ/la-05.ll

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
; Test that a huge address offset is loaded into a register and then added
2+
; separately.
3+
;
4+
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
5+
6+
@a = common dso_local global i32 0, align 4
7+
8+
define i64 @f1() {
9+
; CHECK-LABEL: f1:
10+
; CHECK: llihl %r0, 829
11+
; CHECK: oilf %r0, 4294966308
12+
; CHECK: larl %r2, a
13+
; CHECK: agr %r2, %r0
14+
; CHECK: br %r14
15+
ret i64 add (i64 ptrtoint (i32* @a to i64), i64 3564822854692)
16+
}
17+
18+
define signext i32 @f2() {
19+
; CHECK-LABEL: f2:
20+
; CHECK: llihl %r0, 829
21+
; CHECK: oilf %r0, 4294966308
22+
; CHECK: larl %r1, a
23+
; CHECK: agr %r1, %r0
24+
; CHECK: lgf %r2, 0(%r1)
25+
; CHECK: br %r14
26+
entry:
27+
%0 = load i32, i32* inttoptr (i64 add (i64 ptrtoint (i32* @a to i64),
28+
i64 3564822854692) to i32*)
29+
ret i32 %0
30+
}
31+

llvm/test/MC/SystemZ/insn-bad.s

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3104,11 +3104,14 @@
31043104
#CHECK: larl %r0, 1
31053105
#CHECK: error: offset out of range
31063106
#CHECK: larl %r0, 0x100000000
3107+
#CHECK: error: offset out of range
3108+
#CHECK: larl %r1, __unnamed_1+3564822854692
31073109

31083110
larl %r0, -0x1000000002
31093111
larl %r0, -1
31103112
larl %r0, 1
31113113
larl %r0, 0x100000000
3114+
larl %r1, __unnamed_1+3564822854692
31123115

31133116
#CHECK: error: invalid use of indexed addressing
31143117
#CHECK: lasp 160(%r1,%r15),160(%r15)
@@ -3840,11 +3843,14 @@
38403843
#CHECK: lrl %r0, 1
38413844
#CHECK: error: offset out of range
38423845
#CHECK: lrl %r0, 0x100000000
3846+
#CHECK: error: offset out of range
3847+
#CHECK: lrl %r1, __unnamed_1+3564822854692
38433848

38443849
lrl %r0, -0x1000000002
38453850
lrl %r0, -1
38463851
lrl %r0, 1
38473852
lrl %r0, 0x100000000
3853+
lrl %r1, __unnamed_1+3564822854692
38483854

38493855
#CHECK: error: invalid operand
38503856
#CHECK: lrv %r0, -524289

0 commit comments

Comments
 (0)