Skip to content

Commit 2f3b7d3

Browse files
committed
[Inliner] Fix bug when propagating poison generating return attributes
Poison generating return attributes can't be propagated the same as others, as they can change the behavior of other uses and/or create UB where it otherwise wouldn't have occurred. For example: ``` define nonnull ptr @foo() { %p = call ptr @bar() call void @use(ptr %p) ret ptr %p } ``` If we inline `@foo` and propagate `nonnull` to `@bar`, it could change the behavior of `@use` as instead of taking `null`, `@use` will now be passed `poison`. This can be even worth in a case like: ``` define nonnull ptr @foo() { %p = call noundef ptr @bar() ret ptr %p } ``` Where propagating `nonnull` to `@bar` will cause UB on `null` return of `@bar` (`noundef` + `poison`) where it previously wouldn't have occurred. To fix this, we only propagate poison generating return attributes if either 1) The only use of the callsite to propagate too is return and the callsite to propagate too doesn't have `noundef`. Or 2) the callsite to be be inlined has `noundef`. The former case ensures no new UB or `poison` values will be added. The latter is UB anyways if the value is `poison` so we can go ahead without worrying about behavior changes.
1 parent bf8d039 commit 2f3b7d3

File tree

2 files changed

+69
-10
lines changed

2 files changed

+69
-10
lines changed

llvm/lib/Transforms/Utils/InlineFunction.cpp

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,25 +1344,36 @@ static bool MayContainThrowingOrExitingCallAfterCB(CallBase *Begin,
13441344
++BeginIt, End->getIterator(), InlinerAttributeWindow + 1);
13451345
}
13461346

1347-
static AttrBuilder IdentifyValidAttributes(CallBase &CB) {
1347+
// Only allow these white listed attributes to be propagated back to the
1348+
// callee. This is because other attributes may only be valid on the call
1349+
// itself, i.e. attributes such as signext and zeroext.
1350+
1351+
// Attributes that are always okay to propagate as if they are violated its
1352+
// immediate UB.
1353+
static AttrBuilder IdentifyValidUBGeneratingAttributes(CallBase &CB) {
13481354
AttrBuilder Valid(CB.getContext());
1349-
// Only allow these white listed attributes to be propagated back to the
1350-
// callee. This is because other attributes may only be valid on the call
1351-
// itself, i.e. attributes such as signext and zeroext.
13521355
if (auto DerefBytes = CB.getRetDereferenceableBytes())
13531356
Valid.addDereferenceableAttr(DerefBytes);
13541357
if (auto DerefOrNullBytes = CB.getRetDereferenceableOrNullBytes())
13551358
Valid.addDereferenceableOrNullAttr(DerefOrNullBytes);
13561359
if (CB.hasRetAttr(Attribute::NoAlias))
13571360
Valid.addAttribute(Attribute::NoAlias);
1361+
return Valid;
1362+
}
1363+
1364+
// Attributes that need additional checks as propagating them may change
1365+
// behavior or cause new UB.
1366+
static AttrBuilder IdentifyValidPoisonGeneratingAttributes(CallBase &CB) {
1367+
AttrBuilder Valid(CB.getContext());
13581368
if (CB.hasRetAttr(Attribute::NonNull))
13591369
Valid.addAttribute(Attribute::NonNull);
13601370
return Valid;
13611371
}
13621372

13631373
static void AddReturnAttributes(CallBase &CB, ValueToValueMapTy &VMap) {
1364-
AttrBuilder Valid = IdentifyValidAttributes(CB);
1365-
if (!Valid.hasAttributes())
1374+
AttrBuilder ValidUB = IdentifyValidUBGeneratingAttributes(CB);
1375+
AttrBuilder ValidPG = IdentifyValidPoisonGeneratingAttributes(CB);
1376+
if (!ValidUB.hasAttributes() && !ValidPG.hasAttributes())
13661377
return;
13671378
auto *CalledFunction = CB.getCalledFunction();
13681379
auto &Context = CalledFunction->getContext();
@@ -1406,7 +1417,55 @@ static void AddReturnAttributes(CallBase &CB, ValueToValueMapTy &VMap) {
14061417
// existing attribute value (i.e. attributes such as dereferenceable,
14071418
// dereferenceable_or_null etc). See AttrBuilder::merge for more details.
14081419
AttributeList AL = NewRetVal->getAttributes();
1409-
AttributeList NewAL = AL.addRetAttributes(Context, Valid);
1420+
AttributeList NewAL = AL.addRetAttributes(Context, ValidUB);
1421+
// Attributes that may generate poison returns are a bit tricky. If we
1422+
// propagate them, other uses of the callsite might have their behavior
1423+
// change or cause UB (if they have noundef) b.c of the new potential
1424+
// poison.
1425+
// Take the following three cases:
1426+
//
1427+
// 1)
1428+
// define nonnull ptr @foo() {
1429+
// %p = call ptr @bar()
1430+
// call void @use(ptr %p) willreturn nounwind
1431+
// ret ptr %p
1432+
// }
1433+
//
1434+
// 2)
1435+
// define noundef nonnull ptr @foo() {
1436+
// %p = call ptr @bar()
1437+
// call void @use(ptr %p) willreturn nounwind
1438+
// ret ptr %p
1439+
// }
1440+
//
1441+
// 3)
1442+
// define nonnull ptr @foo() {
1443+
// %p = call noundef ptr @bar()
1444+
// ret ptr %p
1445+
// }
1446+
//
1447+
// In case 1, we can't propagate nonnull because poison value in @use may
1448+
// change behavior or trigger UB.
1449+
// In case 2, we don't need to be concerned about propagating nonnull, as
1450+
// any new poison at @use will trigger UB anyways.
1451+
// In case 3, we can never propagate nonnull because it may create UB due to
1452+
// the noundef on @bar.
1453+
if (ValidPG.hasAttributes()) {
1454+
// Three checks.
1455+
// If the callsite has `noundef`, then a poison due to violating the
1456+
// return attribute will create UB anyways so we can always propagate.
1457+
// Otherwise, if the return value (callee to be inlined) has `noundef`, we
1458+
// can't propagate as a new poison return will cause UB.
1459+
// Finally, check if the return value has no uses whose behavior may
1460+
// change/may cause UB if we potentially return poison. At the moment this
1461+
// is implemented overly conservatively with a single-use check.
1462+
// TODO: Update the single-use check to iterate through uses and only bail
1463+
// if we have a potentially dangerous use.
1464+
1465+
if (CB.hasRetAttr(Attribute::NoUndef) ||
1466+
(RetVal->hasOneUse() && !RetVal->hasRetAttr(Attribute::NoUndef)))
1467+
NewAL = NewAL.addRetAttributes(Context, ValidPG);
1468+
}
14101469
NewRetVal->setAttributes(NewAL);
14111470
}
14121471
}

llvm/test/Transforms/Inline/ret_attr_align_and_noundef.ll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ define ptr @callee9() {
206206

207207
define ptr @caller9_fail_creates_ub() {
208208
; CHECK-LABEL: define ptr @caller9_fail_creates_ub() {
209-
; CHECK-NEXT: [[R_I:%.*]] = call noundef nonnull ptr @foo()
209+
; CHECK-NEXT: [[R_I:%.*]] = call noundef ptr @foo()
210210
; CHECK-NEXT: call void @use.ptr(ptr [[R_I]])
211211
; CHECK-NEXT: ret ptr [[R_I]]
212212
;
@@ -239,7 +239,7 @@ define ptr @callee10() {
239239

240240
define ptr @caller10_fail_maybe_poison() {
241241
; CHECK-LABEL: define ptr @caller10_fail_maybe_poison() {
242-
; CHECK-NEXT: [[R_I:%.*]] = call nonnull ptr @foo()
242+
; CHECK-NEXT: [[R_I:%.*]] = call ptr @foo()
243243
; CHECK-NEXT: call void @use.ptr(ptr [[R_I]])
244244
; CHECK-NEXT: ret ptr [[R_I]]
245245
;
@@ -259,7 +259,7 @@ define ptr @caller10_okay_will_be_ub() {
259259

260260
define noundef ptr @caller10_okay_will_be_ub_todo() {
261261
; CHECK-LABEL: define noundef ptr @caller10_okay_will_be_ub_todo() {
262-
; CHECK-NEXT: [[R_I:%.*]] = call nonnull ptr @foo()
262+
; CHECK-NEXT: [[R_I:%.*]] = call ptr @foo()
263263
; CHECK-NEXT: call void @use.ptr(ptr [[R_I]])
264264
; CHECK-NEXT: ret ptr [[R_I]]
265265
;

0 commit comments

Comments
 (0)