; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -aa-pipeline=basic-aa,globals-aa -S -passes='require,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