diff --git a/llvm/test/Transforms/FunctionAttrs/argmemonly.ll b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll new file mode 100644 index 0000000000000..a0dec5c53bf93 --- /dev/null +++ b/llvm/test/Transforms/FunctionAttrs/argmemonly.ll @@ -0,0 +1,192 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes +; RUN: opt -passes=function-attrs -S %s | FileCheck %s + +@g = global i32 20 + +define void @test_no_read_or_write() { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readnone willreturn +; CHECK-LABEL: @test_no_read_or_write( +; CHECK-NEXT: entry: +; CHECK-NEXT: ret void +; +entry: + ret void +} + +define i32 @test_only_read_arg(i32* %ptr) { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK-LABEL: @test_only_read_arg( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[PTR:%.*]], align 4 +; CHECK-NEXT: ret i32 [[L]] +; +entry: + %l = load i32, i32* %ptr + ret i32 %l +} + +define i32 @test_read_global() { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK-LABEL: @test_read_global( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[L:%.*]] = load i32, i32* @g, align 4 +; CHECK-NEXT: ret i32 [[L]] +; +entry: + %l = load i32, i32* @g + ret i32 %l +} + +define i32 @test_read_loaded_ptr(i32** %ptr) { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK-LABEL: @test_read_loaded_ptr( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[L:%.*]] = load i32*, i32** [[PTR:%.*]], align 8 +; CHECK-NEXT: [[L_2:%.*]] = load i32, i32* [[L]], align 4 +; CHECK-NEXT: ret i32 [[L_2]] +; +entry: + %l = load i32*, i32** %ptr + %l.2 = load i32, i32* %l + ret i32 %l.2 +} + +define void @test_only_write_arg(i32* %ptr) { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +; CHECK-LABEL: @test_only_write_arg( +; CHECK-NEXT: entry: +; CHECK-NEXT: store i32 0, i32* [[PTR:%.*]], align 4 +; CHECK-NEXT: ret void +; +entry: + store i32 0, i32* %ptr + ret void +} + +define void @test_write_global() { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +; CHECK-LABEL: @test_write_global( +; CHECK-NEXT: entry: +; CHECK-NEXT: store i32 0, i32* @g, align 4 +; CHECK-NEXT: ret void +; +entry: + store i32 0, i32* @g + ret void +} + +declare void @fn_may_access_memory() + +define void @test_call_may_access_memory() { +; CHECK-LABEL: @test_call_may_access_memory( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @fn_may_access_memory() +; CHECK-NEXT: ret void +; +entry: + call void @fn_may_access_memory() + ret void +} + +declare i32 @fn_readnone() readnone + +define void @test_call_readnone(i32* %ptr) { +; CHECK: Function Attrs: writeonly +; CHECK-LABEL: @test_call_readnone( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[C:%.*]] = call i32 @fn_readnone() +; CHECK-NEXT: store i32 [[C]], i32* [[PTR:%.*]], align 4 +; CHECK-NEXT: ret void +; +entry: + %c = call i32 @fn_readnone() + store i32 %c, i32* %ptr + ret void +} + +declare i32 @fn_argmemonly(i32*) argmemonly + +define i32 @test_call_argmemonly(i32* %ptr) { +; CHECK-LABEL: @test_call_argmemonly( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[C:%.*]] = call i32 @fn_argmemonly(i32* [[PTR:%.*]]) +; CHECK-NEXT: ret i32 [[C]] +; +entry: + %c = call i32 @fn_argmemonly(i32* %ptr) + ret i32 %c +} + +define i32 @test_call_fn_where_argmemonly_can_be_inferred(i32* %ptr) { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK-LABEL: @test_call_fn_where_argmemonly_can_be_inferred( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[C:%.*]] = call i32 @test_only_read_arg(i32* [[PTR:%.*]]) +; CHECK-NEXT: ret i32 [[C]] +; +entry: + %c = call i32 @test_only_read_arg(i32* %ptr) + ret i32 %c +} + +define void @test_memcpy_argonly(i8* %dst, i8* %src) { +; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn +; CHECK-LABEL: @test_memcpy_argonly( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[DST:%.*]], i8* [[SRC:%.*]], i64 32, i1 false) +; CHECK-NEXT: ret void +; +entry: + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 32, i1 false) + ret void +} + +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) + +@arr = global [32 x i8] zeroinitializer + +define void @test_memcpy_src_global(i8* %dst) { +; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn +; CHECK-LABEL: @test_memcpy_src_global( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BC:%.*]] = bitcast [32 x i8]* @arr to i8* +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[DST:%.*]], i8* [[BC]], i64 32, i1 false) +; CHECK-NEXT: ret void +; +entry: + %bc = bitcast [32 x i8]* @arr to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %bc, i64 32, i1 false) + ret void +} + +define void @test_memcpy_dst_global(i8* %src) { +; CHECK: Function Attrs: mustprogress nofree nosync nounwind willreturn +; CHECK-LABEL: @test_memcpy_dst_global( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[BC:%.*]] = bitcast [32 x i8]* @arr to i8* +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[BC]], i8* [[SRC:%.*]], i64 32, i1 false) +; CHECK-NEXT: ret void +; +entry: + %bc = bitcast [32 x i8]* @arr to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* %bc, i8* %src, i64 32, i1 false) + ret void +} + +define i32 @test_read_arg_access_alloca(i32* %ptr) { +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind readonly willreturn +; CHECK-LABEL: @test_read_arg_access_alloca( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[A:%.*]] = alloca i32, align 4 +; CHECK-NEXT: [[L:%.*]] = load i32, i32* [[PTR:%.*]], align 4 +; CHECK-NEXT: store i32 [[L]], i32* [[A]], align 4 +; CHECK-NEXT: [[L_2:%.*]] = load i32, i32* [[A]], align 4 +; CHECK-NEXT: ret i32 [[L_2]] +; +entry: + %a = alloca i32 + %l = load i32, i32* %ptr + store i32 %l, i32* %a + %l.2 = load i32, i32* %a + ret i32 %l.2 +}