| ; 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 |