From: Hideto Ueno Date: Mon, 2 Dec 2019 13:40:09 +0000 (+0000) Subject: [Attributor] Copy or port test cases related to Attributor to` Attributor` test folder X-Git-Tag: llvmorg-11-init~3170 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=96552036e307f7b0dd6477583c3fdb7de17e8aac;p=platform%2Fupstream%2Fllvm.git [Attributor] Copy or port test cases related to Attributor to` Attributor` test folder Summary: This patch moves the test cases related to Attributor to `Transforms/Attributor` folder. We have used `Transforms/FunctionAttrs` as the primary folder for Attributor test but we need to change testing way now. For the test cases which I think functionattrs doesn't infer anything something like (willreturn, nosync, value-simplify, h2s ..etc), I moved them with the command `git mv`. For the test cases in which functoinattrs and attributor are tested, I copied the test to the folder and remove the check only used by functoinattrs. Reviewers: jdoerfert, sstefan1 Reviewed By: jdoerfert Subscribers: jfb, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D70843 --- diff --git a/llvm/test/Transforms/FunctionAttrs/align.ll b/llvm/test/Transforms/Attributor/align.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/align.ll rename to llvm/test/Transforms/Attributor/align.ll diff --git a/llvm/test/Transforms/FunctionAttrs/callbacks.ll b/llvm/test/Transforms/Attributor/callbacks.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/callbacks.ll rename to llvm/test/Transforms/Attributor/callbacks.ll diff --git a/llvm/test/Transforms/FunctionAttrs/dereferenceable.ll b/llvm/test/Transforms/Attributor/dereferenceable-1.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/dereferenceable.ll rename to llvm/test/Transforms/Attributor/dereferenceable-1.ll diff --git a/llvm/test/Transforms/Attributor/dereferenceable-2.ll b/llvm/test/Transforms/Attributor/dereferenceable-2.ll new file mode 100644 index 0000000..b3c0440 --- /dev/null +++ b/llvm/test/Transforms/Attributor/dereferenceable-2.ll @@ -0,0 +1,356 @@ +; RUN: opt < %s -attributor --attributor-disable=false -S | FileCheck %s --check-prefix=ATTRIBUTOR +; Copied from Transforms/InferFunctionAttrs/dereferenceable.ll + +; Determine dereference-ability before unused loads get deleted: +; https://bugs.llvm.org/show_bug.cgi?id=21780 + +define <4 x double> @PR21780(double* %ptr) { +; ATTRIBUTOR-LABEL: @PR21780(double* nocapture nofree nonnull readonly align 8 dereferenceable(32) %ptr) + + ; GEP of index 0 is simplified away. + %arrayidx1 = getelementptr inbounds double, double* %ptr, i64 1 + %arrayidx2 = getelementptr inbounds double, double* %ptr, i64 2 + %arrayidx3 = getelementptr inbounds double, double* %ptr, i64 3 + + %t0 = load double, double* %ptr, align 8 + %t1 = load double, double* %arrayidx1, align 8 + %t2 = load double, double* %arrayidx2, align 8 + %t3 = load double, double* %arrayidx3, align 8 + + %vecinit0 = insertelement <4 x double> undef, double %t0, i32 0 + %vecinit1 = insertelement <4 x double> %vecinit0, double %t1, i32 1 + %vecinit2 = insertelement <4 x double> %vecinit1, double %t2, i32 2 + %vecinit3 = insertelement <4 x double> %vecinit2, double %t3, i32 3 + %shuffle = shufflevector <4 x double> %vecinit3, <4 x double> %vecinit3, <4 x i32> + ret <4 x double> %shuffle +} + + +define double @PR21780_only_access3_with_inbounds(double* %ptr) { +; ATTRIBUTOR-LABEL: @PR21780_only_access3_with_inbounds(double* nocapture nofree nonnull readonly align 8 dereferenceable(32) %ptr) + + %arrayidx3 = getelementptr inbounds double, double* %ptr, i64 3 + %t3 = load double, double* %arrayidx3, align 8 + ret double %t3 +} + +define double @PR21780_only_access3_without_inbounds(double* %ptr) { +; ATTRIBUTOR-LABEL: @PR21780_only_access3_without_inbounds(double* nocapture nofree readonly align 8 %ptr) + %arrayidx3 = getelementptr double, double* %ptr, i64 3 + %t3 = load double, double* %arrayidx3, align 8 + ret double %t3 +} + +define double @PR21780_without_inbounds(double* %ptr) { +; ATTRIBUTOR-LABEL: @PR21780_without_inbounds(double* nocapture nofree nonnull readonly align 8 dereferenceable(32) %ptr) + + %arrayidx1 = getelementptr double, double* %ptr, i64 1 + %arrayidx2 = getelementptr double, double* %ptr, i64 2 + %arrayidx3 = getelementptr double, double* %ptr, i64 3 + + %t0 = load double, double* %ptr, align 8 + %t1 = load double, double* %arrayidx1, align 8 + %t2 = load double, double* %arrayidx2, align 8 + %t3 = load double, double* %arrayidx3, align 8 + + ret double %t3 +} + +; Unsimplified, but still valid. Also, throw in some bogus arguments. + +define void @gep0(i8* %unused, i8* %other, i8* %ptr) { +; ATTRIBUTOR-LABEL: @gep0(i8* nocapture nofree readnone %unused, i8* nocapture nofree nonnull writeonly dereferenceable(1) %other, i8* nocapture nofree nonnull readonly dereferenceable(3) %ptr) + %arrayidx0 = getelementptr i8, i8* %ptr, i64 0 + %arrayidx1 = getelementptr i8, i8* %ptr, i64 1 + %arrayidx2 = getelementptr i8, i8* %ptr, i64 2 + %t0 = load i8, i8* %arrayidx0 + %t1 = load i8, i8* %arrayidx1 + %t2 = load i8, i8* %arrayidx2 + store i8 %t2, i8* %other + ret void +} + +; Order of accesses does not change computation. +; Multiple arguments may be dereferenceable. + +define void @ordering(i8* %ptr1, i32* %ptr2) { +; ATTRIBUTOR-LABEL: @ordering(i8* nocapture nofree nonnull readonly dereferenceable(3) %ptr1, i32* nocapture nofree nonnull readonly dereferenceable(8) %ptr2) + %a20 = getelementptr i32, i32* %ptr2, i64 0 + %a12 = getelementptr i8, i8* %ptr1, i64 2 + %t12 = load i8, i8* %a12 + %a11 = getelementptr i8, i8* %ptr1, i64 1 + %t20 = load i32, i32* %a20 + %a10 = getelementptr i8, i8* %ptr1, i64 0 + %t10 = load i8, i8* %a10 + %t11 = load i8, i8* %a11 + %a21 = getelementptr i32, i32* %ptr2, i64 1 + %t21 = load i32, i32* %a21 + ret void +} + +; Not in entry block. + +define void @not_entry_but_guaranteed_to_execute(i8* %ptr) { +; ATTRIBUTOR-LABEL: @not_entry_but_guaranteed_to_execute(i8* nocapture nofree nonnull readonly dereferenceable(3) %ptr) +entry: + br label %exit +exit: + %arrayidx0 = getelementptr i8, i8* %ptr, i64 0 + %arrayidx1 = getelementptr i8, i8* %ptr, i64 1 + %arrayidx2 = getelementptr i8, i8* %ptr, i64 2 + %t0 = load i8, i8* %arrayidx0 + %t1 = load i8, i8* %arrayidx1 + %t2 = load i8, i8* %arrayidx2 + ret void +} + +; Not in entry block and not guaranteed to execute. + +define void @not_entry_not_guaranteed_to_execute(i8* %ptr, i1 %cond) { +; ATTRIBUTOR-LABEL: @not_entry_not_guaranteed_to_execute(i8* nocapture nofree readonly %ptr, i1 %cond) +entry: + br i1 %cond, label %loads, label %exit +loads: + %arrayidx0 = getelementptr i8, i8* %ptr, i64 0 + %arrayidx1 = getelementptr i8, i8* %ptr, i64 1 + %arrayidx2 = getelementptr i8, i8* %ptr, i64 2 + %t0 = load i8, i8* %arrayidx0 + %t1 = load i8, i8* %arrayidx1 + %t2 = load i8, i8* %arrayidx2 + ret void +exit: + ret void +} + +; The last load may not execute, so derefenceable bytes only covers the 1st two loads. + +define void @partial_in_entry(i16* %ptr, i1 %cond) { +; ATTRIBUTOR-LABEL: @partial_in_entry(i16* nocapture nofree nonnull readonly dereferenceable(4) %ptr, i1 %cond) +entry: + %arrayidx0 = getelementptr i16, i16* %ptr, i64 0 + %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 + %arrayidx2 = getelementptr i16, i16* %ptr, i64 2 + %t0 = load i16, i16* %arrayidx0 + %t1 = load i16, i16* %arrayidx1 + br i1 %cond, label %loads, label %exit +loads: + %t2 = load i16, i16* %arrayidx2 + ret void +exit: + ret void +} + +; The volatile load can't be used to prove a non-volatile access is allowed. +; The 2nd and 3rd loads may never execute. + +define void @volatile_is_not_dereferenceable(i16* %ptr) { +; ATTRIBUTOR-LABEL: @volatile_is_not_dereferenceable(i16* nofree %ptr) + %arrayidx0 = getelementptr i16, i16* %ptr, i64 0 + %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 + %arrayidx2 = getelementptr i16, i16* %ptr, i64 2 + %t0 = load volatile i16, i16* %arrayidx0 + %t1 = load i16, i16* %arrayidx1 + %t2 = load i16, i16* %arrayidx2 + ret void +} + +; TODO: We should allow inference for atomic (but not volatile) ops. + +define void @atomic_is_alright(i16* %ptr) { +; ATTRIBUTOR-LABEL: @atomic_is_alright(i16* nocapture nofree nonnull readonly align 2 dereferenceable(6) %ptr) + %arrayidx0 = getelementptr i16, i16* %ptr, i64 0 + %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 + %arrayidx2 = getelementptr i16, i16* %ptr, i64 2 + %t0 = load atomic i16, i16* %arrayidx0 unordered, align 2 + %t1 = load i16, i16* %arrayidx1 + %t2 = load i16, i16* %arrayidx2 + ret void +} + +declare void @may_not_return() + +define void @not_guaranteed_to_transfer_execution(i16* %ptr) { +; ATTRIBUTOR-LABEL: @not_guaranteed_to_transfer_execution(i16* nocapture nonnull readonly dereferenceable(2) %ptr) + %arrayidx0 = getelementptr i16, i16* %ptr, i64 0 + %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 + %arrayidx2 = getelementptr i16, i16* %ptr, i64 2 + %t0 = load i16, i16* %arrayidx0 + call void @may_not_return() + %t1 = load i16, i16* %arrayidx1 + %t2 = load i16, i16* %arrayidx2 + ret void +} + +; We must have consecutive accesses. + +define void @variable_gep_index(i8* %unused, i8* %ptr, i64 %variable_index) { +; ATTRIBUTOR-LABEL: @variable_gep_index(i8* nocapture nofree readnone %unused, i8* nocapture nofree nonnull readonly dereferenceable(1) %ptr, i64 %variable_index) + %arrayidx1 = getelementptr i8, i8* %ptr, i64 %variable_index + %arrayidx2 = getelementptr i8, i8* %ptr, i64 2 + %t0 = load i8, i8* %ptr + %t1 = load i8, i8* %arrayidx1 + %t2 = load i8, i8* %arrayidx2 + ret void +} + +; Deal with >1 GEP index. + +define void @multi_index_gep(<4 x i8>* %ptr) { +; FIXME: %ptr should be dereferenceable(4) +; ATTRIBUTOR-LABEL: @multi_index_gep(<4 x i8>* nocapture nofree nonnull readonly dereferenceable(1) %ptr) + %arrayidx00 = getelementptr <4 x i8>, <4 x i8>* %ptr, i64 0, i64 0 + %t0 = load i8, i8* %arrayidx00 + ret void +} + +; Could round weird bitwidths down? + +define void @not_byte_multiple(i9* %ptr) { +; ATTRIBUTOR-LABEL: @not_byte_multiple(i9* nocapture nofree nonnull readonly dereferenceable(2) %ptr) + %arrayidx0 = getelementptr i9, i9* %ptr, i64 0 + %t0 = load i9, i9* %arrayidx0 + ret void +} + +; Missing direct access from the pointer. + +define void @no_pointer_deref(i16* %ptr) { +; ATTRIBUTOR-LABEL: @no_pointer_deref(i16* nocapture nofree readonly %ptr) + %arrayidx1 = getelementptr i16, i16* %ptr, i64 1 + %arrayidx2 = getelementptr i16, i16* %ptr, i64 2 + %t1 = load i16, i16* %arrayidx1 + %t2 = load i16, i16* %arrayidx2 + ret void +} + +; Out-of-order is ok, but missing access concludes dereferenceable range. + +define void @non_consecutive(i32* %ptr) { +; ATTRIBUTOR-LABEL: @non_consecutive(i32* nocapture nofree nonnull readonly dereferenceable(8) %ptr) + %arrayidx1 = getelementptr i32, i32* %ptr, i64 1 + %arrayidx0 = getelementptr i32, i32* %ptr, i64 0 + %arrayidx3 = getelementptr i32, i32* %ptr, i64 3 + %t1 = load i32, i32* %arrayidx1 + %t0 = load i32, i32* %arrayidx0 + %t3 = load i32, i32* %arrayidx3 + ret void +} + +; Improve on existing dereferenceable attribute. + +define void @more_bytes(i32* dereferenceable(8) %ptr) { +; ATTRIBUTOR-LABEL: @more_bytes(i32* nocapture nofree nonnull readonly dereferenceable(16) %ptr) + %arrayidx3 = getelementptr i32, i32* %ptr, i64 3 + %arrayidx1 = getelementptr i32, i32* %ptr, i64 1 + %arrayidx0 = getelementptr i32, i32* %ptr, i64 0 + %arrayidx2 = getelementptr i32, i32* %ptr, i64 2 + %t3 = load i32, i32* %arrayidx3 + %t1 = load i32, i32* %arrayidx1 + %t2 = load i32, i32* %arrayidx2 + %t0 = load i32, i32* %arrayidx0 + ret void +} + +; Improve on existing dereferenceable_or_null attribute. + +define void @more_bytes_and_not_null(i32* dereferenceable_or_null(8) %ptr) { +; ATTRIBUTOR-LABEL: @more_bytes_and_not_null(i32* nocapture nofree nonnull readonly dereferenceable(16) %ptr) + %arrayidx3 = getelementptr i32, i32* %ptr, i64 3 + %arrayidx1 = getelementptr i32, i32* %ptr, i64 1 + %arrayidx0 = getelementptr i32, i32* %ptr, i64 0 + %arrayidx2 = getelementptr i32, i32* %ptr, i64 2 + %t3 = load i32, i32* %arrayidx3 + %t1 = load i32, i32* %arrayidx1 + %t2 = load i32, i32* %arrayidx2 + %t0 = load i32, i32* %arrayidx0 + ret void +} + +; But don't pessimize existing dereferenceable attribute. + +define void @better_bytes(i32* dereferenceable(100) %ptr) { +; ATTRIBUTOR-LABEL: @better_bytes(i32* nocapture nofree nonnull readonly dereferenceable(100) %ptr) + %arrayidx3 = getelementptr i32, i32* %ptr, i64 3 + %arrayidx1 = getelementptr i32, i32* %ptr, i64 1 + %arrayidx0 = getelementptr i32, i32* %ptr, i64 0 + %arrayidx2 = getelementptr i32, i32* %ptr, i64 2 + %t3 = load i32, i32* %arrayidx3 + %t1 = load i32, i32* %arrayidx1 + %t2 = load i32, i32* %arrayidx2 + %t0 = load i32, i32* %arrayidx0 + ret void +} + +define void @bitcast(i32* %arg) { +; ATTRIBUTOR-LABEL: @bitcast(i32* nocapture nofree nonnull readonly dereferenceable(8) %arg) + %ptr = bitcast i32* %arg to float* + %arrayidx0 = getelementptr float, float* %ptr, i64 0 + %arrayidx1 = getelementptr float, float* %ptr, i64 1 + %t0 = load float, float* %arrayidx0 + %t1 = load float, float* %arrayidx1 + ret void +} + +define void @bitcast_different_sizes(double* %arg1, i8* %arg2) { +; ATTRIBUTOR-LABEL: @bitcast_different_sizes(double* nocapture nofree nonnull readonly dereferenceable(12) %arg1, i8* nocapture nofree nonnull readonly dereferenceable(16) %arg2) + %ptr1 = bitcast double* %arg1 to float* + %a10 = getelementptr float, float* %ptr1, i64 0 + %a11 = getelementptr float, float* %ptr1, i64 1 + %a12 = getelementptr float, float* %ptr1, i64 2 + %ld10 = load float, float* %a10 + %ld11 = load float, float* %a11 + %ld12 = load float, float* %a12 + + %ptr2 = bitcast i8* %arg2 to i64* + %a20 = getelementptr i64, i64* %ptr2, i64 0 + %a21 = getelementptr i64, i64* %ptr2, i64 1 + %ld20 = load i64, i64* %a20 + %ld21 = load i64, i64* %a21 + ret void +} + +define void @negative_offset(i32* %arg) { +; ATTRIBUTOR-LABEL: @negative_offset(i32* nocapture nofree nonnull readonly dereferenceable(4) %arg) + %ptr = bitcast i32* %arg to float* + %arrayidx0 = getelementptr float, float* %ptr, i64 0 + %arrayidx1 = getelementptr float, float* %ptr, i64 -1 + %t0 = load float, float* %arrayidx0 + %t1 = load float, float* %arrayidx1 + ret void +} + +define void @stores(i32* %arg) { +; ATTRIBUTOR-LABEL: @stores(i32* nocapture nofree nonnull writeonly dereferenceable(8) %arg) + %ptr = bitcast i32* %arg to float* + %arrayidx0 = getelementptr float, float* %ptr, i64 0 + %arrayidx1 = getelementptr float, float* %ptr, i64 1 + store float 1.0, float* %arrayidx0 + store float 2.0, float* %arrayidx1 + ret void +} + +define void @load_store(i32* %arg) { +; ATTRIBUTOR-LABEL: @load_store(i32* nocapture nofree nonnull dereferenceable(8) %arg) + %ptr = bitcast i32* %arg to float* + %arrayidx0 = getelementptr float, float* %ptr, i64 0 + %arrayidx1 = getelementptr float, float* %ptr, i64 1 + %t1 = load float, float* %arrayidx0 + store float 2.0, float* %arrayidx1 + ret void +} + +define void @different_size1(i32* %arg) { +; ATTRIBUTOR-LABEL: @different_size1(i32* nocapture nofree nonnull writeonly dereferenceable(8) %arg) + %arg-cast = bitcast i32* %arg to double* + store double 0.000000e+00, double* %arg-cast + store i32 0, i32* %arg + ret void +} + +define void @different_size2(i32* %arg) { +; ATTRIBUTOR-LABEL: @different_size2(i32* nocapture nofree nonnull writeonly dereferenceable(8) %arg) + store i32 0, i32* %arg + %arg-cast = bitcast i32* %arg to double* + store double 0.000000e+00, double* %arg-cast + ret void +} diff --git a/llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll b/llvm/test/Transforms/Attributor/heap_to_stack.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/heap_to_stack.ll rename to llvm/test/Transforms/Attributor/heap_to_stack.ll diff --git a/llvm/test/Transforms/FunctionAttrs/internal-noalias.ll b/llvm/test/Transforms/Attributor/internal-noalias.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/internal-noalias.ll rename to llvm/test/Transforms/Attributor/internal-noalias.ll diff --git a/llvm/test/Transforms/FunctionAttrs/liveness.ll b/llvm/test/Transforms/Attributor/liveness.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/liveness.ll rename to llvm/test/Transforms/Attributor/liveness.ll diff --git a/llvm/test/Transforms/FunctionAttrs/misc.ll b/llvm/test/Transforms/Attributor/misc.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/misc.ll rename to llvm/test/Transforms/Attributor/misc.ll diff --git a/llvm/test/Transforms/FunctionAttrs/new_attributes.ll b/llvm/test/Transforms/Attributor/new_attributes.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/new_attributes.ll rename to llvm/test/Transforms/Attributor/new_attributes.ll diff --git a/llvm/test/Transforms/FunctionAttrs/noalias_returned.ll b/llvm/test/Transforms/Attributor/noalias.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/noalias_returned.ll rename to llvm/test/Transforms/Attributor/noalias.ll diff --git a/llvm/test/Transforms/Attributor/nocapture-1.ll b/llvm/test/Transforms/Attributor/nocapture-1.ll new file mode 100644 index 0000000..abb148d --- /dev/null +++ b/llvm/test/Transforms/Attributor/nocapture-1.ll @@ -0,0 +1,346 @@ +; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -S -attributor-annotate-decl-cs < %s | FileCheck %s --check-prefixes=ATTRIBUTOR +; RUN: opt -passes=attributor -attributor-manifest-internal -attributor-disable=false -S -attributor-annotate-decl-cs < %s | FileCheck %s --check-prefixes=ATTRIBUTOR +; Copied from Transforms/FunctoinAttrs/nocapture.ll + +@g = global i32* null ; [#uses=1] + +; ATTRIBUTOR: define i32* @c1(i32* nofree readnone returned "no-capture-maybe-returned" %q) +define i32* @c1(i32* %q) { + ret i32* %q +} + +; ATTRIBUTOR: define void @c2(i32* nofree writeonly %q) +; It would also be acceptable to mark %q as readnone. Update @c3 too. +define void @c2(i32* %q) { + store i32* %q, i32** @g + ret void +} + +; ATTRIBUTOR: define void @c3(i32* nofree writeonly %q) +define void @c3(i32* %q) { + call void @c2(i32* %q) + ret void +} + +; ATTRIBUTOR: define i1 @c4(i32* nofree readnone %q, i32 %bitno) +define i1 @c4(i32* %q, i32 %bitno) { + %tmp = ptrtoint i32* %q to i32 + %tmp2 = lshr i32 %tmp, %bitno + %bit = trunc i32 %tmp2 to i1 + br i1 %bit, label %l1, label %l0 +l0: + ret i1 0 ; escaping value not caught by def-use chaining. +l1: + ret i1 1 ; escaping value not caught by def-use chaining. +} + +; c4b is c4 but without the escaping part +; ATTRIBUTOR: define i1 @c4b(i32* nocapture nofree readnone %q, i32 %bitno) +define i1 @c4b(i32* %q, i32 %bitno) { + %tmp = ptrtoint i32* %q to i32 + %tmp2 = lshr i32 %tmp, %bitno + %bit = trunc i32 %tmp2 to i1 + br i1 %bit, label %l1, label %l0 +l0: + ret i1 0 ; not escaping! +l1: + ret i1 0 ; not escaping! +} + +@lookup_table = global [2 x i1] [ i1 0, i1 1 ] + +; ATTRIBUTOR: define i1 @c5(i32* nofree readonly %q, i32 %bitno) +define i1 @c5(i32* %q, i32 %bitno) { + %tmp = ptrtoint i32* %q to i32 + %tmp2 = lshr i32 %tmp, %bitno + %bit = and i32 %tmp2, 1 + ; subtle escape mechanism follows + %lookup = getelementptr [2 x i1], [2 x i1]* @lookup_table, i32 0, i32 %bit + %val = load i1, i1* %lookup + ret i1 %val +} + +declare void @throw_if_bit_set(i8*, i8) readonly + +; ATTRIBUTOR: define i1 @c6(i8* readonly %q, i8 %bit) +define i1 @c6(i8* %q, i8 %bit) personality i32 (...)* @__gxx_personality_v0 { + invoke void @throw_if_bit_set(i8* %q, i8 %bit) + to label %ret0 unwind label %ret1 +ret0: + ret i1 0 +ret1: + %exn = landingpad {i8*, i32} + cleanup + ret i1 1 +} + +declare i32 @__gxx_personality_v0(...) + +define i1* @lookup_bit(i32* %q, i32 %bitno) readnone nounwind { + %tmp = ptrtoint i32* %q to i32 + %tmp2 = lshr i32 %tmp, %bitno + %bit = and i32 %tmp2, 1 + %lookup = getelementptr [2 x i1], [2 x i1]* @lookup_table, i32 0, i32 %bit + ret i1* %lookup +} + +; ATTRIBUTOR: define i1 @c7(i32* nofree readonly %q, i32 %bitno) +define i1 @c7(i32* %q, i32 %bitno) { + %ptr = call i1* @lookup_bit(i32* %q, i32 %bitno) + %val = load i1, i1* %ptr + ret i1 %val +} + + +; ATTRIBUTOR: define i32 @nc1(i32* nofree %q, i32* nocapture nofree %p, i1 %b) +define i32 @nc1(i32* %q, i32* %p, i1 %b) { +e: + br label %l +l: + %x = phi i32* [ %p, %e ] + %y = phi i32* [ %q, %e ] + %tmp = bitcast i32* %x to i32* ; [#uses=2] + %tmp2 = select i1 %b, i32* %tmp, i32* %y + %val = load i32, i32* %tmp2 ; [#uses=1] + store i32 0, i32* %tmp + store i32* %y, i32** @g + ret i32 %val +} + +; ATTRIBUTOR: define i32 @nc1_addrspace(i32* nofree %q, i32 addrspace(1)* nocapture nofree %p, i1 %b) +define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* %p, i1 %b) { +e: + br label %l +l: + %x = phi i32 addrspace(1)* [ %p, %e ] + %y = phi i32* [ %q, %e ] + %tmp = addrspacecast i32 addrspace(1)* %x to i32* ; [#uses=2] + %tmp2 = select i1 %b, i32* %tmp, i32* %y + %val = load i32, i32* %tmp2 ; [#uses=1] + store i32 0, i32* %tmp + store i32* %y, i32** @g + ret i32 %val +} + +; ATTRIBUTOR: define void @nc2(i32* nocapture nofree %p, i32* nofree %q) +define void @nc2(i32* %p, i32* %q) { + %1 = call i32 @nc1(i32* %q, i32* %p, i1 0) ; [#uses=0] + ret void +} + + +; ATTRIBUTOR: define void @nc3(void ()* nocapture nofree nonnull %p) +define void @nc3(void ()* %p) { + call void %p() + ret void +} + +declare void @external(i8*) readonly nounwind +; ATTRIBUTOR: define void @nc4(i8* nocapture readonly %p) +define void @nc4(i8* %p) { + call void @external(i8* %p) + ret void +} + +; ATTRIBUTOR: define void @nc5(void (i8*)* nocapture nofree nonnull %f, i8* nocapture %p) +define void @nc5(void (i8*)* %f, i8* %p) { + call void %f(i8* %p) readonly nounwind + call void %f(i8* nocapture %p) + ret void +} + +; ATTRIBUTOR: define void @test1_1(i8* nocapture nofree readnone %x1_1, i8* nocapture nofree readnone %y1_1, i1 %c) +; It would be acceptable to add readnone to %y1_1 and %y1_2. +define void @test1_1(i8* %x1_1, i8* %y1_1, i1 %c) { + call i8* @test1_2(i8* %x1_1, i8* %y1_1, i1 %c) + store i32* null, i32** @g + ret void +} + +; ATTRIBUTOR: define i8* @test1_2(i8* nocapture nofree readnone %x1_2, i8* nofree readnone returned "no-capture-maybe-returned" %y1_2, i1 %c) +define i8* @test1_2(i8* %x1_2, i8* %y1_2, i1 %c) { + br i1 %c, label %t, label %f +t: + call void @test1_1(i8* %x1_2, i8* %y1_2, i1 %c) + store i32* null, i32** @g + br label %f +f: + ret i8* %y1_2 +} + +; ATTRIBUTOR: define void @test2(i8* nocapture nofree readnone %x2) +define void @test2(i8* %x2) { + call void @test2(i8* %x2) + store i32* null, i32** @g + ret void +} + +; ATTRIBUTOR: define void @test3(i8* nocapture nofree readnone %x3, i8* nocapture nofree readnone %y3, i8* nocapture nofree readnone %z3) +define void @test3(i8* %x3, i8* %y3, i8* %z3) { + call void @test3(i8* %z3, i8* %y3, i8* %x3) + store i32* null, i32** @g + ret void +} + +; ATTRIBUTOR: define void @test4_1(i8* nocapture nofree readnone %x4_1, i1 %c) +define void @test4_1(i8* %x4_1, i1 %c) { + call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1, i1 %c) + store i32* null, i32** @g + ret void +} + +; ATTRIBUTOR: define i8* @test4_2(i8* nocapture nofree readnone %x4_2, i8* nofree readnone returned "no-capture-maybe-returned" %y4_2, i8* nocapture nofree readnone %z4_2, i1 %c) +define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2, i1 %c) { + br i1 %c, label %t, label %f +t: + call void @test4_1(i8* null, i1 %c) + store i32* null, i32** @g + br label %f +f: + ret i8* %y4_2 +} + +declare i8* @test5_1(i8* %x5_1) + +; ATTRIBUTOR: define void @test5_2(i8* %x5_2) +define void @test5_2(i8* %x5_2) { + call i8* @test5_1(i8* %x5_2) + store i32* null, i32** @g + ret void +} + +declare void @test6_1(i8* %x6_1, i8* nocapture %y6_1, ...) + +; ATTRIBUTOR: define void @test6_2(i8* %x6_2, i8* nocapture %y6_2, i8* %z6_2) +define void @test6_2(i8* %x6_2, i8* %y6_2, i8* %z6_2) { + call void (i8*, i8*, ...) @test6_1(i8* %x6_2, i8* %y6_2, i8* %z6_2) + store i32* null, i32** @g + ret void +} + +; ATTRIBUTOR: define void @test_cmpxchg(i32* nocapture nofree nonnull dereferenceable(4) %p) +define void @test_cmpxchg(i32* %p) { + cmpxchg i32* %p, i32 0, i32 1 acquire monotonic + ret void +} + +; ATTRIBUTOR: define void @test_cmpxchg_ptr(i32** nocapture nofree nonnull dereferenceable(8) %p, i32* nofree %q) +define void @test_cmpxchg_ptr(i32** %p, i32* %q) { + cmpxchg i32** %p, i32* null, i32* %q acquire monotonic + ret void +} + +; ATTRIBUTOR: define void @test_atomicrmw(i32* nocapture nofree nonnull dereferenceable(4) %p) +define void @test_atomicrmw(i32* %p) { + atomicrmw add i32* %p, i32 1 seq_cst + ret void +} + +; ATTRIBUTOR: define void @test_volatile(i32* nofree align 4 %x) +define void @test_volatile(i32* %x) { +entry: + %gep = getelementptr i32, i32* %x, i64 1 + store volatile i32 0, i32* %gep, align 4 + ret void +} + +; ATTRIBUTOR: nocaptureLaunder(i8* nocapture %p) +define void @nocaptureLaunder(i8* %p) { +entry: + %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p) + store i8 42, i8* %b + ret void +} + +@g2 = global i8* null +; ATTRIBUTOR: define void @captureLaunder(i8* %p) +define void @captureLaunder(i8* %p) { + %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p) + store i8* %b, i8** @g2 + ret void +} + +; ATTRIBUTOR: @nocaptureStrip(i8* nocapture writeonly %p) +define void @nocaptureStrip(i8* %p) { +entry: + %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p) + store i8 42, i8* %b + ret void +} + +@g3 = global i8* null +; ATTRIBUTOR: define void @captureStrip(i8* writeonly %p) +define void @captureStrip(i8* %p) { + %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p) + store i8* %b, i8** @g3 + ret void +} + +; ATTRIBUTOR: define i1 @captureICmp(i32* nofree readnone %x) +define i1 @captureICmp(i32* %x) { + %1 = icmp eq i32* %x, null + ret i1 %1 +} + +; ATTRIBUTOR: define i1 @captureICmpRev(i32* nofree readnone %x) +define i1 @captureICmpRev(i32* %x) { + %1 = icmp eq i32* null, %x + ret i1 %1 +} + +; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture nofree nonnull readnone %x) +define i1 @nocaptureInboundsGEPICmp(i32* %x) { + %1 = getelementptr inbounds i32, i32* %x, i32 5 + %2 = bitcast i32* %1 to i8* + %3 = icmp eq i8* %2, null + ret i1 %3 +} + +; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture nofree nonnull readnone %x) +define i1 @nocaptureInboundsGEPICmpRev(i32* %x) { + %1 = getelementptr inbounds i32, i32* %x, i32 5 + %2 = bitcast i32* %1 to i8* + %3 = icmp eq i8* null, %2 + ret i1 %3 +} + +; ATTRIBUTOR: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture nofree readnone dereferenceable_or_null(4) %x) +define i1 @nocaptureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) { + %1 = bitcast i32* %x to i8* + %2 = icmp eq i8* %1, null + ret i1 %2 +} + +; ATTRIBUTOR: define i1 @captureDereferenceableOrNullICmp(i32* nofree readnone dereferenceable_or_null(4) %x) +define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) "null-pointer-is-valid"="true" { + %1 = bitcast i32* %x to i8* + %2 = icmp eq i8* %1, null + ret i1 %2 +} + +declare void @unknown(i8*) +define void @test_callsite() { +entry: +; We know that 'null' in AS 0 does not alias anything and cannot be captured. Though the latter is not qurried -> derived atm. +; ATTRIBUTOR: call void @unknown(i8* noalias null) + call void @unknown(i8* null) + ret void +} + +declare i8* @unknownpi8pi8(i8*,i8* returned) +define i8* @test_returned1(i8* %A, i8* returned %B) nounwind readonly { +; ATTRIBUTOR: define i8* @test_returned1(i8* nocapture readonly %A, i8* readonly returned %B) +entry: + %p = call i8* @unknownpi8pi8(i8* %A, i8* %B) + ret i8* %p +} + +define i8* @test_returned2(i8* %A, i8* %B) { +; ATTRIBUTOR: define i8* @test_returned2(i8* nocapture readonly %A, i8* readonly returned %B) +entry: + %p = call i8* @unknownpi8pi8(i8* %A, i8* %B) nounwind readonly + ret i8* %p +} + +declare i8* @llvm.launder.invariant.group.p0i8(i8*) +declare i8* @llvm.strip.invariant.group.p0i8(i8*) diff --git a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll b/llvm/test/Transforms/Attributor/nocapture-2.ll similarity index 100% rename from llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll rename to llvm/test/Transforms/Attributor/nocapture-2.ll diff --git a/llvm/test/Transforms/Attributor/nofree.ll b/llvm/test/Transforms/Attributor/nofree.ll new file mode 100644 index 0000000..d06a0ea --- /dev/null +++ b/llvm/test/Transforms/Attributor/nofree.ll @@ -0,0 +1,243 @@ +; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; Copied from Transforms/FunctoinAttrs/nofree-attributor.ll + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; Test cases specifically designed for the "nofree" function attribute. +; We use FIXME's to indicate problems and missing attributes. + +; Free functions +declare void @free(i8* nocapture) local_unnamed_addr #1 +declare noalias i8* @realloc(i8* nocapture, i64) local_unnamed_addr #0 +declare void @_ZdaPv(i8*) local_unnamed_addr #2 + + +; TEST 1 (positive case) +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable +; ATTRIBUTOR-NEXT: define void @only_return() +define void @only_return() #0 { + ret void +} + + +; TEST 2 (negative case) +; Only free +; void only_free(char* p) { +; free(p); +; } + +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR-NEXT: define void @only_free(i8* nocapture %0) local_unnamed_addr #1 +define void @only_free(i8* nocapture %0) local_unnamed_addr #0 { + tail call void @free(i8* %0) #1 + ret void +} + + +; TEST 3 (negative case) +; Free occurs in same scc. +; void free_in_scc1(char*p){ +; free_in_scc2(p); +; } +; void free_in_scc2(char*p){ +; free_in_scc1(p); +; free(p); +; } + + +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR-NEXT :define void @free_in_scc1(i8* nocapture %0) local_unnamed_addr +define void @free_in_scc1(i8* nocapture %0) local_unnamed_addr #0 { + tail call void @free_in_scc2(i8* %0) #1 + ret void +} + + +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR: define void @free_in_scc2(i8* nocapture %0) local_unnamed_addr +define void @free_in_scc2(i8* nocapture %0) local_unnamed_addr #0 { + %cmp = icmp eq i8* %0, null + br i1 %cmp, label %rec, label %call +call: + tail call void @free(i8* %0) #1 + br label %end +rec: + tail call void @free_in_scc1(i8* %0) + br label %end +end: + ret void +} + + +; TEST 4 (positive case) +; Free doesn't occur. +; void mutual_recursion1(){ +; mutual_recursion2(); +; } +; void mutual_recursion2(){ +; mutual_recursion1(); +; } + + +; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable +; ATTRIBUTOR-NEXT: define void @mutual_recursion1() +define void @mutual_recursion1() #0 { + call void @mutual_recursion2() + ret void +} + +; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable +; ATTRIBUTOR-NEXT: define void @mutual_recursion2() +define void @mutual_recursion2() #0 { + call void @mutual_recursion1() + ret void +} + + +; TEST 5 +; C++ delete operation (negative case) +; void delete_op (char p[]){ +; delete [] p; +; } + +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR-NEXT: define void @_Z9delete_opPc(i8* %0) local_unnamed_addr #1 +define void @_Z9delete_opPc(i8* %0) local_unnamed_addr #0 { + %2 = icmp eq i8* %0, null + br i1 %2, label %4, label %3 + +;