Skip to content

[BasicAA] Treat IntToPtr(Argument) similarly to Argument in relation to function-local objects. #134505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions llvm/lib/Analysis/BasicAliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,23 @@ AliasResult BasicAAResult::aliasPHI(const PHINode *PN, LocationSize PNSize,
return Alias;
}

// Return true for an Argument, IntToPtr(Argument) or
// IntToPtr(ExtractValue(Argument)). These are all known
// to not alias with FunctionLocal objects and can come up
// from coerced function arguments.
static bool isArgumentOrArgumentLike(const Value *V) {
if (isa<Argument>(V))
return true;
if (auto *P = dyn_cast<IntToPtrInst>(V)) {
const Value *POp = P->getOperand(0);
if (isa<Argument>(POp))
return true;
if (auto *E = dyn_cast<ExtractValueInst>(POp))
return isa<Argument>(E->getOperand(0));
}
return false;
}

/// Provides a bunch of ad-hoc rules to disambiguate in common cases, such as
/// array references.
AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size,
Expand Down Expand Up @@ -1585,8 +1602,8 @@ AliasResult BasicAAResult::aliasCheck(const Value *V1, LocationSize V1Size,

// Function arguments can't alias with things that are known to be
// unambigously identified at the function level.
if ((isa<Argument>(O1) && isIdentifiedFunctionLocal(O2)) ||
(isa<Argument>(O2) && isIdentifiedFunctionLocal(O1)))
if ((isArgumentOrArgumentLike(O1) && isIdentifiedFunctionLocal(O2)) ||
(isArgumentOrArgumentLike(O2) && isIdentifiedFunctionLocal(O1)))
return AliasResult::NoAlias;

// If one pointer is the result of a call/invoke or load and the other is a
Expand Down
20 changes: 16 additions & 4 deletions llvm/test/Analysis/BasicAA/noalias-inttoptr.ll
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@ define void @test2(i64 %Q_as_int) {
ret void
}

; Verify that escaped noalias parameter may alias inttoptr
; Verify that escaped noalias parameter are no alias inttoptr
define void @test3(ptr noalias %P, i64 %Q_as_int) {
; CHECK-LABEL: Function: test3:
; CHECK: MayAlias: i8* %P, i8* %Q
; CHECK: NoAlias: i8* %P, i8* %Q
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not correct. %P and %Q can alias if the caller did ptr2int on %Q.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, sorry, I missed the noalias bit.
I'm sympathetic to accepting this as valid, but I'm not sure LLVM is ready for that. Probably in most places we only check nolias against other pointer arguments and not against integers.

Copy link
Contributor

@nikic nikic Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see how we could accept this, regardless of noalias. Maybe to make it more explicit, we expect that the following code is not UB (at the LLVM IR level):

void test(int * restrict P, uintptr_t Q_as_int) {
    if ((uintptr_t)P == Q_as_int) {
        (int *)Q_as_int = 1; // Can alias P.
    }
}

Here (uintptr_t)P exposes the provenance of P and then (int *)Q_as_int chooses it.

(In the test case, the call to @escape stands in for the provenance exposure.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's one interpretation of noalias. But you can also choose to say that P cannot alias with the result of int2ptr since noalias means you cannot do any access through another pointer that points to the same location as P.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you are suggesting that for a noalias pointer %P, doing store inttoptr(ptrtoint(%P)) would be UB? I don't think that is viable due to frontend requirements.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true, it's not easy to get working. the only solution for now is to lose precision and mark these as may alias.

call void @escape(ptr %P)
%Q = inttoptr i64 %Q_as_int to ptr
store i8 0, ptr %P
store i8 1, ptr %Q
ret void
}

; Verify that escaped alloca may alias inttoptr
; Verify that escaped alloca are noalias inttoptr
define void @test4(i64 %Q_as_int) {
; CHECK-LABEL: Function: test4:
; CHECK: MayAlias: i8* %P, i8* %Q
; CHECK: NoAlias: i8* %P, i8* %Q
%P = alloca i8
call void @escape(ptr %P)
%Q = inttoptr i64 %Q_as_int to ptr
Expand All @@ -58,3 +58,15 @@ define void @test5(i64 %Q_as_int) {
store i8 1, ptr %Q
ret void
}

; Verify that extractvalue of a coerced argument are noalias a function local object
define void @test6([2 x i64] %Q.coerce) {
; CHECK-LABEL: Function: test6:
; CHECK: NoAlias: i8* %P, i8* %Q
%P = alloca i8
%Q_as_int = extractvalue [2 x i64] %Q.coerce, 1
%Q = inttoptr i64 %Q_as_int to ptr
store i8 0, ptr %P
store i8 1, ptr %Q
ret void
}
Loading