Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[AA] A global cannot escape through nocapture/nocallback call.
When an internal global is passed to a 'nocallback' call as a 'nocapture' pointer, it cannot escape through this call and be indirectly referenced in this module. So it must not alias with any pointer in the module. This may provide some remedy for Fortran module-private array descriptors that are usually passed by address to some runtime functions (e.g. to allocation/deallocation functions). In general, a good aliasing information derived from Fortran language rules would solve the same issue, but I think this change may be beneficial as-is (given that nocapture, nocallback attributes are properly set). Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D138336
- Loading branch information
Showing
2 changed files
with
353 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
328 changes: 328 additions & 0 deletions
328
llvm/test/Analysis/GlobalsModRef/noescape-nocapture-nocallback.ll
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,328 @@ | ||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py | ||
; RUN: opt < %s -aa-pipeline=basic-aa,globals-aa -S -passes='require<globals-aa>,function(loop-mssa(licm))' | FileCheck %s | ||
|
||
;Reference C code: | ||
;struct str { | ||
; void **p; | ||
;}; | ||
;static struct str obj; | ||
;extern void nocapture_nocallback_func(struct str *); | ||
;void test(void *p) { | ||
; nocapture_nocallback_func(&obj); | ||
; for (int i = 0; i < 1000; ++i) { | ||
; unknown_call(); // optional | ||
; obj.p[i] = p; | ||
; } | ||
;} | ||
|
||
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" | ||
|
||
%struct.str = type { ptr } | ||
|
||
@obj0 = internal global %struct.str zeroinitializer, align 8 | ||
@obj1 = internal global %struct.str zeroinitializer, align 8 | ||
@obj2 = internal global %struct.str zeroinitializer, align 8 | ||
@obj3 = internal global %struct.str zeroinitializer, align 8 | ||
@obj4 = internal global %struct.str zeroinitializer, align 8 | ||
@obj5 = internal global %struct.str zeroinitializer, align 8 | ||
|
||
define dso_local void @test0(ptr %p) { | ||
; Check that load from @obj0 is hoisted from the loop, meaning | ||
; that it does not conflict with the store inside the loop: | ||
; CHECK-LABEL: @test0( | ||
; CHECK-NEXT: entry: | ||
; CHECK-NEXT: call void @nocapture_nocallback_func(ptr @obj0) | ||
; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @obj0, align 8 | ||
; CHECK-NEXT: br label [[FOR_COND:%.*]] | ||
; CHECK: for.cond: | ||
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_INC:%.*]] ] | ||
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I_0]], 1000 | ||
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] | ||
; CHECK: for.body: | ||
; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[I_0]] to i64 | ||
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]] | ||
; CHECK-NEXT: store ptr [[P:%.*]], ptr [[ARRAYIDX]], align 8 | ||
; CHECK-NEXT: br label [[FOR_INC]] | ||
; CHECK: for.inc: | ||
; CHECK-NEXT: [[INC]] = add nsw i32 [[I_0]], 1 | ||
; CHECK-NEXT: br label [[FOR_COND]] | ||
; CHECK: for.end: | ||
; CHECK-NEXT: ret void | ||
; | ||
|
||
entry: | ||
call void @nocapture_nocallback_func(ptr @obj0) | ||
br label %for.cond | ||
|
||
for.cond: ; preds = %for.inc, %entry | ||
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] | ||
%cmp = icmp slt i32 %i.0, 1000 | ||
br i1 %cmp, label %for.body, label %for.end | ||
|
||
for.body: ; preds = %for.cond | ||
%0 = load ptr, ptr @obj0, align 8 | ||
%idxprom = sext i32 %i.0 to i64 | ||
%arrayidx = getelementptr inbounds ptr, ptr %0, i64 %idxprom | ||
store ptr %p, ptr %arrayidx, align 8 | ||
br label %for.inc | ||
|
||
for.inc: ; preds = %for.body | ||
%inc = add nsw i32 %i.0, 1 | ||
br label %for.cond | ||
|
||
for.end: ; preds = %for.cond | ||
ret void | ||
} | ||
|
||
define dso_local void @test1(ptr %p) { | ||
; Check that load from @obj1 is not hoisted from the loop, | ||
; because 'nocallback' is missing: | ||
; CHECK-LABEL: @test1( | ||
; CHECK-NEXT: entry: | ||
; CHECK-NEXT: call void @nocapture_func(ptr @obj1) | ||
; CHECK-NEXT: br label [[FOR_COND:%.*]] | ||
; CHECK: for.cond: | ||
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_INC:%.*]] ] | ||
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I_0]], 1000 | ||
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] | ||
; CHECK: for.body: | ||
; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @obj1, align 8 | ||
; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[I_0]] to i64 | ||
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]] | ||
; CHECK-NEXT: store ptr [[P:%.*]], ptr [[ARRAYIDX]], align 8 | ||
; CHECK-NEXT: br label [[FOR_INC]] | ||
; CHECK: for.inc: | ||
; CHECK-NEXT: [[INC]] = add nsw i32 [[I_0]], 1 | ||
; CHECK-NEXT: br label [[FOR_COND]] | ||
; CHECK: for.end: | ||
; CHECK-NEXT: ret void | ||
; | ||
|
||
entry: | ||
call void @nocapture_func(ptr @obj1) | ||
br label %for.cond | ||
|
||
for.cond: ; preds = %for.inc, %entry | ||
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] | ||
%cmp = icmp slt i32 %i.0, 1000 | ||
br i1 %cmp, label %for.body, label %for.end | ||
|
||
for.body: ; preds = %for.cond | ||
%0 = load ptr, ptr @obj1, align 8 | ||
%idxprom = sext i32 %i.0 to i64 | ||
%arrayidx = getelementptr inbounds ptr, ptr %0, i64 %idxprom | ||
store ptr %p, ptr %arrayidx, align 8 | ||
br label %for.inc | ||
|
||
for.inc: ; preds = %for.body | ||
%inc = add nsw i32 %i.0, 1 | ||
br label %for.cond | ||
|
||
for.end: ; preds = %for.cond | ||
ret void | ||
} | ||
|
||
define dso_local void @test2(ptr %p) { | ||
; Check that load from @obj2 is not hoisted from the loop, | ||
; because 'nocapture' is missing: | ||
; CHECK-LABEL: @test2( | ||
; CHECK-NEXT: entry: | ||
; CHECK-NEXT: call void @nocallback_func(ptr @obj2) | ||
; CHECK-NEXT: br label [[FOR_COND:%.*]] | ||
; CHECK: for.cond: | ||
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_INC:%.*]] ] | ||
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I_0]], 1000 | ||
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] | ||
; CHECK: for.body: | ||
; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @obj2, align 8 | ||
; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[I_0]] to i64 | ||
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]] | ||
; CHECK-NEXT: store ptr [[P:%.*]], ptr [[ARRAYIDX]], align 8 | ||
; CHECK-NEXT: br label [[FOR_INC]] | ||
; CHECK: for.inc: | ||
; CHECK-NEXT: [[INC]] = add nsw i32 [[I_0]], 1 | ||
; CHECK-NEXT: br label [[FOR_COND]] | ||
; CHECK: for.end: | ||
; CHECK-NEXT: ret void | ||
; | ||
|
||
entry: | ||
call void @nocallback_func(ptr @obj2) | ||
br label %for.cond | ||
|
||
for.cond: ; preds = %for.inc, %entry | ||
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] | ||
%cmp = icmp slt i32 %i.0, 1000 | ||
br i1 %cmp, label %for.body, label %for.end | ||
|
||
for.body: ; preds = %for.cond | ||
%0 = load ptr, ptr @obj2, align 8 | ||
%idxprom = sext i32 %i.0 to i64 | ||
%arrayidx = getelementptr inbounds ptr, ptr %0, i64 %idxprom | ||
store ptr %p, ptr %arrayidx, align 8 | ||
br label %for.inc | ||
|
||
for.inc: ; preds = %for.body | ||
%inc = add nsw i32 %i.0, 1 | ||
br label %for.cond | ||
|
||
for.end: ; preds = %for.cond | ||
ret void | ||
} | ||
|
||
define dso_local void @test3(ptr %p) { | ||
; Check that load from @obj3 is hoisted from the loop, even though | ||
; there is unknown call in the loop. | ||
; CHECK-LABEL: @test3( | ||
; CHECK-NEXT: entry: | ||
; CHECK-NEXT: call void @nocapture_nocallback_func(ptr @obj3) | ||
; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @obj3, align 8 | ||
; CHECK-NEXT: br label [[FOR_COND:%.*]] | ||
; CHECK: for.cond: | ||
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_INC:%.*]] ] | ||
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I_0]], 1000 | ||
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] | ||
; CHECK: for.body: | ||
; CHECK-NEXT: call void @unknown_call() | ||
; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[I_0]] to i64 | ||
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]] | ||
; CHECK-NEXT: store ptr [[P:%.*]], ptr [[ARRAYIDX]], align 8 | ||
; CHECK-NEXT: br label [[FOR_INC]] | ||
; CHECK: for.inc: | ||
; CHECK-NEXT: [[INC]] = add nsw i32 [[I_0]], 1 | ||
; CHECK-NEXT: br label [[FOR_COND]] | ||
; CHECK: for.end: | ||
; CHECK-NEXT: ret void | ||
; | ||
|
||
entry: | ||
call void @nocapture_nocallback_func(ptr @obj3) | ||
br label %for.cond | ||
|
||
for.cond: ; preds = %for.inc, %entry | ||
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] | ||
%cmp = icmp slt i32 %i.0, 1000 | ||
br i1 %cmp, label %for.body, label %for.end | ||
|
||
for.body: ; preds = %for.cond | ||
%0 = load ptr, ptr @obj3, align 8 | ||
call void @unknown_call() | ||
%idxprom = sext i32 %i.0 to i64 | ||
%arrayidx = getelementptr inbounds ptr, ptr %0, i64 %idxprom | ||
store ptr %p, ptr %arrayidx, align 8 | ||
br label %for.inc | ||
|
||
for.inc: ; preds = %for.body | ||
%inc = add nsw i32 %i.0, 1 | ||
br label %for.cond | ||
|
||
for.end: ; preds = %for.cond | ||
ret void | ||
} | ||
|
||
define dso_local void @test4(ptr %p) { | ||
; Check that load from @obj4 is not hoisted from the loop, | ||
; because 'nocallback' is missing: | ||
; CHECK-LABEL: @test4( | ||
; CHECK-NEXT: entry: | ||
; CHECK-NEXT: call void @nocapture_func(ptr @obj4) | ||
; CHECK-NEXT: br label [[FOR_COND:%.*]] | ||
; CHECK: for.cond: | ||
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_INC:%.*]] ] | ||
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I_0]], 1000 | ||
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] | ||
; CHECK: for.body: | ||
; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @obj4, align 8 | ||
; CHECK-NEXT: call void @unknown_call() | ||
; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[I_0]] to i64 | ||
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]] | ||
; CHECK-NEXT: store ptr [[P:%.*]], ptr [[ARRAYIDX]], align 8 | ||
; CHECK-NEXT: br label [[FOR_INC]] | ||
; CHECK: for.inc: | ||
; CHECK-NEXT: [[INC]] = add nsw i32 [[I_0]], 1 | ||
; CHECK-NEXT: br label [[FOR_COND]] | ||
; CHECK: for.end: | ||
; CHECK-NEXT: ret void | ||
; | ||
|
||
entry: | ||
call void @nocapture_func(ptr @obj4) | ||
br label %for.cond | ||
|
||
for.cond: ; preds = %for.inc, %entry | ||
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] | ||
%cmp = icmp slt i32 %i.0, 1000 | ||
br i1 %cmp, label %for.body, label %for.end | ||
|
||
for.body: ; preds = %for.cond | ||
%0 = load ptr, ptr @obj4, align 8 | ||
call void @unknown_call() | ||
%idxprom = sext i32 %i.0 to i64 | ||
%arrayidx = getelementptr inbounds ptr, ptr %0, i64 %idxprom | ||
store ptr %p, ptr %arrayidx, align 8 | ||
br label %for.inc | ||
|
||
for.inc: ; preds = %for.body | ||
%inc = add nsw i32 %i.0, 1 | ||
br label %for.cond | ||
|
||
for.end: ; preds = %for.cond | ||
ret void | ||
} | ||
|
||
define dso_local void @test5(ptr %p) { | ||
; Check that load from @obj5 is not hoisted from the loop, | ||
; because 'nocapture' is missing: | ||
; CHECK-LABEL: @test5( | ||
; CHECK-NEXT: entry: | ||
; CHECK-NEXT: call void @nocallback_func(ptr @obj5) | ||
; CHECK-NEXT: br label [[FOR_COND:%.*]] | ||
; CHECK: for.cond: | ||
; CHECK-NEXT: [[I_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_INC:%.*]] ] | ||
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[I_0]], 1000 | ||
; CHECK-NEXT: br i1 [[CMP]], label [[FOR_BODY:%.*]], label [[FOR_END:%.*]] | ||
; CHECK: for.body: | ||
; CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @obj5, align 8 | ||
; CHECK-NEXT: call void @unknown_call() | ||
; CHECK-NEXT: [[IDXPROM:%.*]] = sext i32 [[I_0]] to i64 | ||
; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]] | ||
; CHECK-NEXT: store ptr [[P:%.*]], ptr [[ARRAYIDX]], align 8 | ||
; CHECK-NEXT: br label [[FOR_INC]] | ||
; CHECK: for.inc: | ||
; CHECK-NEXT: [[INC]] = add nsw i32 [[I_0]], 1 | ||
; CHECK-NEXT: br label [[FOR_COND]] | ||
; CHECK: for.end: | ||
; CHECK-NEXT: ret void | ||
; | ||
|
||
entry: | ||
call void @nocallback_func(ptr @obj5) | ||
br label %for.cond | ||
|
||
for.cond: ; preds = %for.inc, %entry | ||
%i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ] | ||
%cmp = icmp slt i32 %i.0, 1000 | ||
br i1 %cmp, label %for.body, label %for.end | ||
|
||
for.body: ; preds = %for.cond | ||
%0 = load ptr, ptr @obj5, align 8 | ||
call void @unknown_call() | ||
%idxprom = sext i32 %i.0 to i64 | ||
%arrayidx = getelementptr inbounds ptr, ptr %0, i64 %idxprom | ||
store ptr %p, ptr %arrayidx, align 8 | ||
br label %for.inc | ||
|
||
for.inc: ; preds = %for.body | ||
%inc = add nsw i32 %i.0, 1 | ||
br label %for.cond | ||
|
||
for.end: ; preds = %for.cond | ||
ret void | ||
} | ||
|
||
declare void @nocapture_nocallback_func(ptr nocapture) nocallback | ||
declare void @nocapture_func(ptr nocapture) | ||
declare void @nocallback_func(ptr) nocallback | ||
; nosync and nocallback are required, otherwise the call | ||
; will by ModRef for any global: | ||
declare void @unknown_call() nosync nocallback |