diff --git a/llvm/test/Transforms/InstCombine/nocapture-attribute.ll b/llvm/test/Transforms/InstCombine/nocapture-attribute.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/nocapture-attribute.ll @@ -0,0 +1,373 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare void @ptrs_maybe_capture(ptr, ptr, ptr) +declare void @ptr_maybe_capture(ptr) +declare i64 @ptr_maybe_capture.i64(ptr) +declare ptr @ptr_maybe_capture.ptr(ptr) +declare noalias ptr @malloc_like(i64) +declare i64 @barrier(i64) + +define void @simple_propegate_all_maybe_capture(ptr %a0, ptr %a1, ptr %a2) { +; CHECK-LABEL: define void @simple_propegate_all_maybe_capture +; CHECK-SAME: (ptr [[A0:%.*]], ptr [[A1:%.*]], ptr [[A2:%.*]]) { +; CHECK-NEXT: tail call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A1]], ptr [[A2]]) +; CHECK-NEXT: ret void +; + tail call void @ptrs_maybe_capture(ptr %a0, ptr %a1, ptr %a2) + ret void +} + +define void @simple_propegated_a0_a1_nocapture_a2_maybe_capture(ptr nocapture %a0, ptr nocapture %a1, ptr %a2) local_unnamed_addr { +; CHECK-LABEL: define void @simple_propegated_a0_a1_nocapture_a2_maybe_capture +; CHECK-SAME: (ptr nocapture [[A0:%.*]], ptr nocapture [[A1:%.*]], ptr [[A2:%.*]]) local_unnamed_addr { +; CHECK-NEXT: tail call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A1]], ptr [[A2]]) +; CHECK-NEXT: ret void +; + tail call void @ptrs_maybe_capture(ptr %a0, ptr %a1, ptr %a2) + ret void +} + +define void @simple_propegate_a2_nocapture2x_a1_maybe_capture(ptr nocapture %a0, ptr %a1, ptr nocapture %a2) { +; CHECK-LABEL: define void @simple_propegate_a2_nocapture2x_a1_maybe_capture +; CHECK-SAME: (ptr nocapture [[A0:%.*]], ptr [[A1:%.*]], ptr nocapture [[A2:%.*]]) { +; CHECK-NEXT: tail call void @ptrs_maybe_capture(ptr [[A2]], ptr [[A1]], ptr [[A2]]) +; CHECK-NEXT: ret void +; + tail call void @ptrs_maybe_capture(ptr %a2, ptr %a1, ptr %a2) + ret void +} + +define i64 @propegate_past_trivially_read_only(ptr nocapture %a0, i64 %r) local_unnamed_addr { +; CHECK-LABEL: define i64 @propegate_past_trivially_read_only +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]]) local_unnamed_addr { +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A0]], ptr [[A0]]) +; CHECK-NEXT: [[R0:%.*]] = mul i64 [[R]], [[R]] +; CHECK-NEXT: [[R1:%.*]] = mul i64 [[R0]], [[R0]] +; CHECK-NEXT: [[R2:%.*]] = shl i64 [[R1]], 1 +; CHECK-NEXT: [[R3:%.*]] = mul i64 [[R2]], [[R2]] +; CHECK-NEXT: [[R4:%.*]] = load i64, ptr [[A0]], align 4 +; CHECK-NEXT: [[R5:%.*]] = add i64 [[R4]], [[R3]] +; CHECK-NEXT: [[R6:%.*]] = call i64 @barrier(i64 [[R5]]) #[[ATTR0:[0-9]+]] +; CHECK-NEXT: ret i64 [[R6]] +; + call void @ptrs_maybe_capture(ptr %a0, ptr %a0, ptr %a0) + %r0 = mul i64 %r, %r + %r1 = mul i64 %r0, %r0 + %r2 = add i64 %r1, %r1 + %r3 = mul i64 %r2, %r2 + %r4 = load i64, ptr %a0 + %r5 = add i64 %r4, %r3 + %r6 = call i64 @barrier(i64 %r5) readonly + ret i64 %r6 +} + +define i64 @no_propegate_past_rw_function(ptr nocapture %a0, i64 %r) local_unnamed_addr { +; CHECK-LABEL: define i64 @no_propegate_past_rw_function +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]]) local_unnamed_addr { +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A0]], ptr [[A0]]) +; CHECK-NEXT: [[R0:%.*]] = mul i64 [[R]], [[R]] +; CHECK-NEXT: [[R1:%.*]] = mul i64 [[R0]], [[R0]] +; CHECK-NEXT: [[R2:%.*]] = shl i64 [[R1]], 1 +; CHECK-NEXT: [[R3:%.*]] = mul i64 [[R2]], [[R2]] +; CHECK-NEXT: [[R4:%.*]] = load i64, ptr [[A0]], align 4 +; CHECK-NEXT: [[R5:%.*]] = add i64 [[R4]], [[R3]] +; CHECK-NEXT: [[R6:%.*]] = call i64 @barrier(i64 [[R5]]) +; CHECK-NEXT: ret i64 [[R6]] +; + call void @ptrs_maybe_capture(ptr %a0, ptr %a0, ptr %a0) + %r0 = mul i64 %r, %r + %r1 = mul i64 %r0, %r0 + %r2 = add i64 %r1, %r1 + %r3 = mul i64 %r2, %r2 + %r4 = load i64, ptr %a0 + %r5 = add i64 %r4, %r3 + %r6 = call i64 @barrier(i64 %r5) + ret i64 %r6 +} + +define i64 @no_propegate_any_reaching_alloca(ptr nocapture %a0, i64 %r, i1 %c, i1 %c2) local_unnamed_addr { +; CHECK-LABEL: define i64 @no_propegate_any_reaching_alloca +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]], i1 [[C:%.*]], i1 [[C2:%.*]]) local_unnamed_addr { +; CHECK-NEXT: [[PN:%.*]] = alloca i64, align 8 +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CHECK: T: +; CHECK-NEXT: [[UNUSED:%.*]] = call i64 @barrier(i64 0) +; CHECK-NEXT: br i1 [[C2]], label [[TT:%.*]], label [[F]] +; CHECK: TT: +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A0]], ptr [[A0]]) +; CHECK-NEXT: ret i64 0 +; CHECK: F: +; CHECK-NEXT: [[TMP1:%.*]] = call i64 @ptr_maybe_capture(ptr nonnull [[PN]]) #[[ATTR0]] +; CHECK-NEXT: [[R7:%.*]] = call i64 @barrier(i64 9) #[[ATTR1:[0-9]+]] +; CHECK-NEXT: ret i64 [[R7]] +; + %pN = alloca i64 + br i1 %c, label %T, label %F +T: + %unused = call i64 @barrier(i64 0) + br i1 %c2, label %TT, label %F +TT: + call void @ptrs_maybe_capture(ptr %a0, ptr %a0, ptr %a0) + ret i64 0 +F: + call i64 @ptr_maybe_capture(ptr %pN) readonly + %r7 = call i64 @barrier(i64 9) noreturn + ret i64 %r7 +} + + +define i64 @propegate_non_reaching_alloca(ptr nocapture %a0, i64 %r, i1 %c, i1 %c2) local_unnamed_addr { +; CHECK-LABEL: define i64 @propegate_non_reaching_alloca +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]], i1 [[C:%.*]], i1 [[C2:%.*]]) local_unnamed_addr { +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CHECK: T: +; CHECK-NEXT: [[UNUSED:%.*]] = call i64 @barrier(i64 0) +; CHECK-NEXT: br i1 [[C2]], label [[TT:%.*]], label [[F]] +; CHECK: TT: +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A0]], ptr [[A0]]) +; CHECK-NEXT: ret i64 0 +; CHECK: F: +; CHECK-NEXT: [[PN:%.*]] = alloca i64, align 8 +; CHECK-NEXT: [[TMP1:%.*]] = call i64 @ptr_maybe_capture(ptr nonnull [[PN]]) #[[ATTR0]] +; CHECK-NEXT: [[R7:%.*]] = call i64 @barrier(i64 9) #[[ATTR1]] +; CHECK-NEXT: ret i64 [[R7]] +; + br i1 %c, label %T, label %F +T: + %unused = call i64 @barrier(i64 0) + br i1 %c2, label %TT, label %F +TT: + call void @ptrs_maybe_capture(ptr %a0, ptr %a0, ptr %a0) + ret i64 0 +F: + %pN = alloca i64 + call i64 @ptr_maybe_capture(ptr %pN) readonly + %r7 = call i64 @barrier(i64 9) noreturn + ret i64 %r7 +} + + +define i64 @no_propegate_any_leaked_malloc(ptr nocapture %a0, i64 %r, i1 %c) local_unnamed_addr { +; CHECK-LABEL: define i64 @no_propegate_any_leaked_malloc +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]], i1 [[C:%.*]]) local_unnamed_addr { +; CHECK-NEXT: [[PN:%.*]] = call noalias ptr @malloc_like(i64 [[R]]) +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CHECK: T: +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A0]], ptr [[A0]]) +; CHECK-NEXT: ret i64 0 +; CHECK: F: +; CHECK-NEXT: [[TMP1:%.*]] = call i64 @ptr_maybe_capture(ptr [[PN]]) #[[ATTR0]] +; CHECK-NEXT: [[R7:%.*]] = call i64 @barrier(i64 9) #[[ATTR1]] +; CHECK-NEXT: ret i64 [[R7]] +; + %pN = call noalias ptr @malloc_like(i64 %r) + br i1 %c, label %T, label %F +T: + call void @ptrs_maybe_capture(ptr %a0, ptr %a0, ptr %a0) + ret i64 0 +F: + call i64 @ptr_maybe_capture(ptr %pN) readonly + %r7 = call i64 @barrier(i64 9) noreturn + ret i64 %r7 +} + +define ptr @propegate_non_leaked_malloc(ptr nocapture %a0, i64 %r, i1 %c) local_unnamed_addr { +; CHECK-LABEL: define ptr @propegate_non_leaked_malloc +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]], i1 [[C:%.*]]) local_unnamed_addr { +; CHECK-NEXT: [[PN:%.*]] = call noalias ptr @malloc_like(i64 [[R]]) +; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]] +; CHECK: T: +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A0]], ptr [[A0]]) +; CHECK-NEXT: ret ptr [[PN]] +; CHECK: F: +; CHECK-NEXT: [[TMP1:%.*]] = call i64 @ptr_maybe_capture(ptr [[PN]]) #[[ATTR0]] +; CHECK-NEXT: [[R7:%.*]] = call i64 @barrier(i64 9) #[[ATTR1]] +; CHECK-NEXT: ret ptr null +; + %pN = call noalias ptr @malloc_like(i64 %r) + br i1 %c, label %T, label %F +T: + call void @ptrs_maybe_capture(ptr %a0, ptr %a0, ptr %a0) + ret ptr %pN +F: + call i64 @ptr_maybe_capture(ptr %pN) readonly + %r7 = call i64 @barrier(i64 9) noreturn + ret ptr null +} + +define i64 @no_propegate_past_write_prop_at_end(ptr nocapture %a0, i64 %r) local_unnamed_addr { +; CHECK-LABEL: define i64 @no_propegate_past_write_prop_at_end +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]]) local_unnamed_addr { +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr [[A0]], ptr [[A0]]) +; CHECK-NEXT: [[R0:%.*]] = mul i64 [[R]], [[R]] +; CHECK-NEXT: [[R1:%.*]] = mul i64 [[R0]], [[R0]] +; CHECK-NEXT: [[R2:%.*]] = shl i64 [[R1]], 1 +; CHECK-NEXT: [[R3:%.*]] = mul i64 [[R2]], [[R2]] +; CHECK-NEXT: [[R4:%.*]] = load i64, ptr [[A0]], align 4 +; CHECK-NEXT: [[R5:%.*]] = add i64 [[R4]], [[R3]] +; CHECK-NEXT: [[R6:%.*]] = call i64 @barrier(i64 [[R5]]) #[[ATTR0]] +; CHECK-NEXT: store i64 [[R6]], ptr [[A0]], align 4 +; CHECK-NEXT: [[TMP1:%.*]] = call i64 @ptr_maybe_capture(ptr nonnull [[A0]]) #[[ATTR0]] +; CHECK-NEXT: ret i64 [[R6]] +; + call void @ptrs_maybe_capture(ptr %a0, ptr %a0, ptr %a0) + %r0 = mul i64 %r, %r + %r1 = mul i64 %r0, %r0 + %r2 = add i64 %r1, %r1 + %r3 = mul i64 %r2, %r2 + %r4 = load i64, ptr %a0 + %r5 = add i64 %r4, %r3 + %r6 = call i64 @barrier(i64 %r5) readonly + store i64 %r6, ptr %a0 + call i64 @ptr_maybe_capture(ptr %a0) readonly + ret i64 %r6 +} + +define void @no_propegate_wrong_argument(ptr nocapture %a0, i64 %r) local_unnamed_addr { +; CHECK-LABEL: define void @no_propegate_wrong_argument +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]]) local_unnamed_addr { +; CHECK-NEXT: [[A0_OFF1:%.*]] = getelementptr inbounds i32, ptr [[A0]], i64 [[R]] +; CHECK-NEXT: call void @ptr_maybe_capture(ptr [[A0_OFF1]]) +; CHECK-NEXT: ret void +; + %a0_off1 = getelementptr inbounds i32, ptr %a0, i64 %r + call void @ptr_maybe_capture(ptr %a0_off1) + ret void +} + +define void @no_propegate_past_dead_alloca(ptr nocapture %a0, i64 %r) { +; CHECK-LABEL: define void @no_propegate_past_dead_alloca +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: call void @ptr_maybe_capture(ptr [[A0]]) +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr nonnull [[A]], ptr nonnull [[A]]) +; CHECK-NEXT: ret void +; + %a = alloca ptr + call void @ptr_maybe_capture(ptr %a0) + call void @ptrs_maybe_capture(ptr %a0, ptr %a, ptr %a) + ret void +} + +define i32 @no_propegate_past_dead_alloca2(ptr nocapture %a0, i64 %r) { +; CHECK-LABEL: define i32 @no_propegate_past_dead_alloca2 +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr nonnull [[A]], ptr nonnull [[A]]) #[[ATTR0]] +; CHECK-NEXT: call void @ptr_maybe_capture(ptr [[A0]]) #[[ATTR0]] +; CHECK-NEXT: ret i32 0 +; + %a = alloca ptr + call void @ptrs_maybe_capture(ptr %a0, ptr %a, ptr %a) readonly + call void @ptr_maybe_capture(ptr %a0) readonly + ret i32 0 +} + +define i32 @no_propegate_past_live_alloca(ptr nocapture %a0, i64 %r) { +; CHECK-LABEL: define i32 @no_propegate_past_live_alloca +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[R:%.*]]) { +; CHECK-NEXT: [[A:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr nonnull [[A]], ptr nonnull [[A]]) +; CHECK-NEXT: call void @ptrs_maybe_capture(ptr [[A0]], ptr nonnull [[A]], ptr nonnull [[A]]) +; CHECK-NEXT: ret i32 0 +; + %a = alloca ptr + call void @ptrs_maybe_capture(ptr %a0, ptr %a, ptr %a) + call void @ptrs_maybe_capture(ptr %a0, ptr %a, ptr %a) + ret i32 0 +} + +define i32 @no_propegate_past_bb(ptr nocapture %a0, i1 %c) { +; CHECK-LABEL: define i32 @no_propegate_past_bb +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i1 [[C:%.*]]) { +; CHECK-NEXT: br i1 [[C]], label [[TRUE:%.*]], label [[FALSE:%.*]] +; CHECK: true: +; CHECK-NEXT: call void @ptr_maybe_capture(ptr [[A0]]) +; CHECK-NEXT: br label [[FALSE]] +; CHECK: false: +; CHECK-NEXT: ret i32 0 +; + br i1 %c, label %true, label %false +true: + call void @ptr_maybe_capture(ptr %a0) + br label %false +false: + ret i32 0 +} + +define i64 @no_propegate_past_bb2(ptr nocapture %a0, i1 %c) { +; CHECK-LABEL: define i64 @no_propegate_past_bb2 +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i1 [[C:%.*]]) { +; CHECK-NEXT: call void @ptr_maybe_capture(ptr [[A0]]) +; CHECK-NEXT: br i1 [[C]], label [[TRUE:%.*]], label [[FALSE:%.*]] +; CHECK: true: +; CHECK-NEXT: [[R:%.*]] = call i64 @barrier(i64 0) #[[ATTR0]] +; CHECK-NEXT: ret i64 [[R]] +; CHECK: false: +; CHECK-NEXT: ret i64 0 +; + call void @ptr_maybe_capture(ptr %a0) + br i1 %c, label %true, label %false +true: + %r = call i64 @barrier(i64 0) readonly + ret i64 %r +false: + ret i64 0 +} + +define i64 @propegate_return(ptr nocapture %a0) { +; CHECK-LABEL: define i64 @propegate_return +; CHECK-SAME: (ptr nocapture [[A0:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = call i64 @ptr_maybe_capture.i64(ptr [[A0]]) +; CHECK-NEXT: [[RR:%.*]] = mul i64 [[R]], [[R]] +; CHECK-NEXT: ret i64 [[RR]] +; + %r = call i64 @ptr_maybe_capture.i64(ptr %a0) + %rr = mul i64 %r, %r + ret i64 %rr +} + +define i64 @no_propegate_loaded_return(ptr nocapture %a0) { +; CHECK-LABEL: define i64 @no_propegate_loaded_return +; CHECK-SAME: (ptr nocapture [[A0:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = call ptr @ptr_maybe_capture.ptr(ptr [[A0]]) +; CHECK-NEXT: [[RR:%.*]] = load i64, ptr [[R]], align 4 +; CHECK-NEXT: ret i64 [[RR]] +; + %r = call ptr @ptr_maybe_capture.ptr(ptr %a0) + %rr = load i64, ptr %r + ret i64 %rr +} + +define i64 @no_propegate_loaded_return_ind(ptr nocapture %a0) { +; CHECK-LABEL: define i64 @no_propegate_loaded_return_ind +; CHECK-SAME: (ptr nocapture [[A0:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = call ptr @ptr_maybe_capture.ptr(ptr [[A0]]) +; CHECK-NEXT: [[R_OFF1:%.*]] = getelementptr inbounds i64, ptr [[R]], i64 1 +; CHECK-NEXT: [[RR:%.*]] = load i64, ptr [[R_OFF1]], align 4 +; CHECK-NEXT: ret i64 [[RR]] +; + %r = call ptr @ptr_maybe_capture.ptr(ptr %a0) + %r_off1 = getelementptr inbounds i64, ptr %r, i64 1 + %rr = load i64, ptr %r_off1 + ret i64 %rr +} + +define i64 @no_propegate_loaded_return2(ptr nocapture %a0, i64 %v) { +; CHECK-LABEL: define i64 @no_propegate_loaded_return2 +; CHECK-SAME: (ptr nocapture [[A0:%.*]], i64 [[V:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = call i64 @ptr_maybe_capture.i64(ptr [[A0]]) +; CHECK-NEXT: [[R2:%.*]] = or i64 [[R]], [[V]] +; CHECK-NEXT: [[R2P:%.*]] = inttoptr i64 [[R2]] to ptr +; CHECK-NEXT: [[R2_OFF1:%.*]] = getelementptr inbounds i64, ptr [[R2P]], i64 1 +; CHECK-NEXT: [[RR:%.*]] = load i64, ptr [[R2_OFF1]], align 4 +; CHECK-NEXT: ret i64 [[RR]] +; + %r = call i64 @ptr_maybe_capture.i64(ptr %a0) + %r2 = or i64 %r, %v + %r2p = inttoptr i64 %r2 to ptr + %r2_off1 = getelementptr inbounds i64, ptr %r2p, i64 1 + %rr = load i64, ptr %r2_off1 + ret i64 %rr +}