Skip to content

Commit b9bba6c

Browse files
authored
[BasicAA] Track nuw through decomposed expressions (#106512)
When we decompose the GEP offset expression, and the arithmetic is not performed using nuw operations, we cannot retain the nuw flag on the decomposed GEP. For example, if we have `gep nuw p, (a-1)`, this is not at all the same as `gep nuw (gep nuw p, a), -1`. Fix this by tracking NUW through linear expression decomposition, similarly to what we already do for the NSW flag. This fixes the miscompilation reported in #105496 (comment).
1 parent d710011 commit b9bba6c

File tree

2 files changed

+124
-10
lines changed

2 files changed

+124
-10
lines changed

llvm/lib/Analysis/BasicAliasAnalysis.cpp

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -375,24 +375,28 @@ struct LinearExpression {
375375
APInt Scale;
376376
APInt Offset;
377377

378+
/// True if all operations in this expression are NUW.
379+
bool IsNUW;
378380
/// True if all operations in this expression are NSW.
379381
bool IsNSW;
380382

381383
LinearExpression(const CastedValue &Val, const APInt &Scale,
382-
const APInt &Offset, bool IsNSW)
383-
: Val(Val), Scale(Scale), Offset(Offset), IsNSW(IsNSW) {}
384+
const APInt &Offset, bool IsNUW, bool IsNSW)
385+
: Val(Val), Scale(Scale), Offset(Offset), IsNUW(IsNUW), IsNSW(IsNSW) {}
384386

385-
LinearExpression(const CastedValue &Val) : Val(Val), IsNSW(true) {
387+
LinearExpression(const CastedValue &Val)
388+
: Val(Val), IsNUW(true), IsNSW(true) {
386389
unsigned BitWidth = Val.getBitWidth();
387390
Scale = APInt(BitWidth, 1);
388391
Offset = APInt(BitWidth, 0);
389392
}
390393

391-
LinearExpression mul(const APInt &Other, bool MulIsNSW) const {
394+
LinearExpression mul(const APInt &Other, bool MulIsNUW, bool MulIsNSW) const {
392395
// The check for zero offset is necessary, because generally
393396
// (X +nsw Y) *nsw Z does not imply (X *nsw Z) +nsw (Y *nsw Z).
394397
bool NSW = IsNSW && (Other.isOne() || (MulIsNSW && Offset.isZero()));
395-
return LinearExpression(Val, Scale * Other, Offset * Other, NSW);
398+
bool NUW = IsNUW && (Other.isOne() || MulIsNUW);
399+
return LinearExpression(Val, Scale * Other, Offset * Other, NUW, NSW);
396400
}
397401
};
398402
}
@@ -408,7 +412,7 @@ static LinearExpression GetLinearExpression(
408412

409413
if (const ConstantInt *Const = dyn_cast<ConstantInt>(Val.V))
410414
return LinearExpression(Val, APInt(Val.getBitWidth(), 0),
411-
Val.evaluateWith(Const->getValue()), true);
415+
Val.evaluateWith(Const->getValue()), true, true);
412416

413417
if (const BinaryOperator *BOp = dyn_cast<BinaryOperator>(Val.V)) {
414418
if (ConstantInt *RHSC = dyn_cast<ConstantInt>(BOp->getOperand(1))) {
@@ -444,20 +448,22 @@ static LinearExpression GetLinearExpression(
444448
E = GetLinearExpression(Val.withValue(BOp->getOperand(0), false), DL,
445449
Depth + 1, AC, DT);
446450
E.Offset += RHS;
451+
E.IsNUW &= NUW;
447452
E.IsNSW &= NSW;
448453
break;
449454
}
450455
case Instruction::Sub: {
451456
E = GetLinearExpression(Val.withValue(BOp->getOperand(0), false), DL,
452457
Depth + 1, AC, DT);
453458
E.Offset -= RHS;
459+
E.IsNUW = false; // sub nuw x, y is not add nuw x, -y.
454460
E.IsNSW &= NSW;
455461
break;
456462
}
457463
case Instruction::Mul:
458464
E = GetLinearExpression(Val.withValue(BOp->getOperand(0), false), DL,
459465
Depth + 1, AC, DT)
460-
.mul(RHS, NSW);
466+
.mul(RHS, NUW, NSW);
461467
break;
462468
case Instruction::Shl:
463469
// We're trying to linearize an expression of the kind:
@@ -472,6 +478,7 @@ static LinearExpression GetLinearExpression(
472478
Depth + 1, AC, DT);
473479
E.Offset <<= RHS.getLimitedValue();
474480
E.Scale <<= RHS.getLimitedValue();
481+
E.IsNUW &= NUW;
475482
E.IsNSW &= NSW;
476483
break;
477484
}
@@ -697,7 +704,8 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
697704
// If the integer type is smaller than the index size, it is implicitly
698705
// sign extended or truncated to index size.
699706
bool NUSW = GEPOp->hasNoUnsignedSignedWrap();
700-
bool NonNeg = NUSW && GEPOp->hasNoUnsignedWrap();
707+
bool NUW = GEPOp->hasNoUnsignedWrap();
708+
bool NonNeg = NUSW && NUW;
701709
unsigned Width = Index->getType()->getIntegerBitWidth();
702710
unsigned SExtBits = IndexSize > Width ? IndexSize - Width : 0;
703711
unsigned TruncBits = IndexSize < Width ? Width - IndexSize : 0;
@@ -706,9 +714,11 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
706714

707715
// Scale by the type size.
708716
unsigned TypeSize = AllocTypeSize.getFixedValue();
709-
LE = LE.mul(APInt(IndexSize, TypeSize), NUSW);
717+
LE = LE.mul(APInt(IndexSize, TypeSize), NUW, NUSW);
710718
Decomposed.Offset += LE.Offset.sext(MaxIndexSize);
711719
APInt Scale = LE.Scale.sext(MaxIndexSize);
720+
if (!LE.IsNUW)
721+
Decomposed.NWFlags = Decomposed.NWFlags.withoutNoUnsignedWrap();
712722

713723
// If we already had an occurrence of this index variable, merge this
714724
// scale into it. For example, we want to handle:
@@ -719,7 +729,8 @@ BasicAAResult::DecomposeGEPExpression(const Value *V, const DataLayout &DL,
719729
areBothVScale(Decomposed.VarIndices[i].Val.V, LE.Val.V)) &&
720730
Decomposed.VarIndices[i].Val.hasSameCastsAs(LE.Val)) {
721731
Scale += Decomposed.VarIndices[i].Scale;
722-
LE.IsNSW = false; // We cannot guarantee nsw for the merge.
732+
// We cannot guarantee no-wrap for the merge.
733+
LE.IsNSW = LE.IsNUW = false;
723734
Decomposed.VarIndices.erase(Decomposed.VarIndices.begin() + i);
724735
break;
725736
}

llvm/test/Analysis/BasicAA/gep-nuw-alias.ll

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,106 @@ define void @both_var_idx(ptr %p, i64 %i, i64 %j) {
212212

213213
ret void
214214
}
215+
216+
; CHECK-LABEL: add_no_nuw
217+
; CHECK: MayAlias: i8* %gep, i8* %p
218+
define i8 @add_no_nuw(ptr %p, i64 %n) {
219+
store i8 3, ptr %p
220+
221+
%add = add i64 %n, 1
222+
%gep = getelementptr nuw i8, ptr %p, i64 %add
223+
%val = load i8, ptr %gep
224+
ret i8 %val
225+
}
226+
227+
; CHECK-LABEL: add_nuw
228+
; CHECK: NoAlias: i8* %gep, i8* %p
229+
define i8 @add_nuw(ptr %p, i64 %n) {
230+
store i8 3, ptr %p
231+
232+
%add = add nuw i64 %n, 1
233+
%gep = getelementptr nuw i8, ptr %p, i64 %add
234+
%val = load i8, ptr %gep
235+
ret i8 %val
236+
}
237+
238+
; CHECK-LABEL: add_no_nuw
239+
; CHECK: MayAlias: i8* %gep, i16* %p
240+
define i8 @add_no_nuw_scale(ptr %p, i64 %n) {
241+
store i16 3, ptr %p
242+
243+
%add = add i64 %n, 1
244+
%gep = getelementptr nuw i16, ptr %p, i64 %add
245+
%val = load i8, ptr %gep
246+
ret i8 %val
247+
}
248+
249+
; CHECK-LABEL: add_nuw
250+
; CHECK: NoAlias: i8* %gep, i16* %p
251+
define i8 @add_nuw_scale(ptr %p, i64 %n) {
252+
store i16 3, ptr %p
253+
254+
%add = add nuw i64 %n, 1
255+
%gep = getelementptr nuw i16, ptr %p, i64 %add
256+
%val = load i8, ptr %gep
257+
ret i8 %val
258+
}
259+
260+
; CHECK-LABEL: sub_nuw
261+
; CHECK: MayAlias: i8* %gep, i8* %p
262+
define i8 @sub_nuw(ptr %p, i64 %n) {
263+
store i8 3, ptr %p
264+
265+
%add = sub nuw i64 %n, 1
266+
%gep = getelementptr nuw i8, ptr %p, i64 %add
267+
%val = load i8, ptr %gep
268+
ret i8 %val
269+
}
270+
271+
; CHECK-LABEL: mul_no_nuw
272+
; CHECK: MayAlias: i8* %gep, i16* %p
273+
define i8 @mul_no_nuw(ptr %p, i64 %n) {
274+
store i16 3, ptr %p
275+
276+
%add = add nuw i64 %n, 1
277+
%mul = mul i64 %add, 2
278+
%gep = getelementptr nuw i8, ptr %p, i64 %mul
279+
%val = load i8, ptr %gep
280+
ret i8 %val
281+
}
282+
283+
; CHECK-LABEL: mul_nuw
284+
; CHECK: NoAlias: i8* %gep, i16* %p
285+
define i8 @mul_nuw(ptr %p, i64 %n) {
286+
store i16 3, ptr %p
287+
288+
%add = add nuw i64 %n, 1
289+
%mul = mul nuw i64 %add, 2
290+
%gep = getelementptr nuw i8, ptr %p, i64 %mul
291+
%val = load i8, ptr %gep
292+
ret i8 %val
293+
}
294+
295+
; CHECK-LABEL: shl_no_nuw
296+
; CHECK: MayAlias: i8* %gep, i16* %p
297+
define i8 @shl_no_nuw(ptr %p, i64 %n) {
298+
store i16 3, ptr %p
299+
300+
%add = add nuw i64 %n, 1
301+
%shl = shl i64 %add, 1
302+
%gep = getelementptr nuw i8, ptr %p, i64 %shl
303+
%val = load i8, ptr %gep
304+
ret i8 %val
305+
}
306+
307+
; CHECK-LABEL: shl_nuw
308+
; CHECK: NoAlias: i8* %gep, i16* %p
309+
define i8 @shl_nuw(ptr %p, i64 %n) {
310+
store i16 3, ptr %p
311+
312+
%add = add nuw i64 %n, 1
313+
%shl = shl nuw i64 %add, 1
314+
%gep = getelementptr nuw i8, ptr %p, i64 %shl
315+
%val = load i8, ptr %gep
316+
ret i8 %val
317+
}

0 commit comments

Comments
 (0)