diff --git a/llvm/test/Transforms/FunctionAttrs/SCC1.ll b/llvm/test/Transforms/FunctionAttrs/SCC1.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/FunctionAttrs/SCC1.ll @@ -0,0 +1,169 @@ +; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s +; +; This is an evolved example to stress test SCC parameter attribute propagation. +; The SCC in this test is made up of the following six function, three of which +; are internal and three externally visible: +; +; static int* internal_ret0_nw(int *n0, int *w0); +; static int* internal_ret1_rw(int *r0, int *w0); +; static int* internal_ret1_rrw(int *r0, int *r1, int *w0); +; int* external_ret2_nrw(int *n0, int *r0, int *w0); +; int* external_sink_ret2_nrw(int *n0, int *r0, int *w0); +; int* external_source_ret2_nrw(int *n0, int *r0, int *w0); +; +; The top four functions call each other while the "sink" function will not +; call anything and the "source" function will not be called in this module. +; The names of the functions define the returned parameter (X for "_retX_"), +; as well as how the parameters are (transitively) used (n = readnone, +; r = readonly, w = writeonly). +; +; Currently we get the following statistics: +; 1 functionattrs - Number of functions marked as norecurse +; 6 functionattrs - Number of functions marked as nounwind +; 1 functionattrs - Number of arguments marked nocapture +; 1 functionattrs - Number of arguments marked readnone +; 1 functionattrs - Number of arguments marked readonly +; 1 functionattrs - Number of arguments marked returned +; -- +; 11 +; +; This basically describes that we only derived attributes for the "sink" +; function (except nounwind which is properly derived). +; +; +; What we should get is something along the lines of: +; 1 functionattrs - Number of functions marked as norecurse +; 6 functionattrs - Number of functions marked argmemonly +; 6 functionattrs - Number of functions marked as nounwind +; 16 functionattrs - Number of arguments marked nocapture +; 4 functionattrs - Number of arguments marked readnone +; 6 functionattrs - Number of arguments marked writeonly +; 6 functionattrs - Number of arguments marked readonly +; 6 functionattrs - Number of arguments marked returned +; -- +; 51 +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +; CHECK: define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) #[[NOUNWIND:[0-9]*]] +define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { +entry: + %call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0) + %call1 = call i32* @internal_ret1_rrw(i32* %r0, i32* %r0, i32* %w0) + %call2 = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) + %call3 = call i32* @internal_ret1_rw(i32* %r0, i32* %w0) + ret i32* %call3 +} + +; CHECK: define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) #[[NOUNWIND]] +define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) { +entry: + %r0 = alloca i32, align 4 + %r1 = alloca i32, align 4 + %tobool = icmp ne i32* %n0, null + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + store i32 3, i32* %r0, align 4 + store i32 5, i32* %r1, align 4 + store i32 1, i32* %w0, align 4 + %call = call i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) + %call1 = call i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) + %call2 = call i32* @external_ret2_nrw(i32* %n0, i32* %r1, i32* %w0) + %call3 = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) + %call4 = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r1, i32* %w0) + %call5 = call i32* @internal_ret0_nw(i32* %n0, i32* %w0) + br label %return + +return: ; preds = %if.end, %if.then + %retval.0 = phi i32* [ %call5, %if.end ], [ %n0, %if.then ] + ret i32* %retval.0 +} + +; CHECK: define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) #[[NOUNWIND]] +define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) { +entry: + %0 = load i32, i32* %r0, align 4 + %tobool = icmp ne i32 %0, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + %call = call i32* @internal_ret1_rw(i32* %r0, i32* %w0) + %1 = load i32, i32* %r0, align 4 + %2 = load i32, i32* %r1, align 4 + %add = add nsw i32 %1, %2 + store i32 %add, i32* %w0, align 4 + %call1 = call i32* @internal_ret1_rw(i32* %r1, i32* %w0) + %call2 = call i32* @internal_ret0_nw(i32* %r0, i32* %w0) + %call3 = call i32* @internal_ret0_nw(i32* %w0, i32* %w0) + %call4 = call i32* @external_ret2_nrw(i32* %r0, i32* %r1, i32* %w0) + %call5 = call i32* @external_ret2_nrw(i32* %r1, i32* %r0, i32* %w0) + %call6 = call i32* @external_sink_ret2_nrw(i32* %r0, i32* %r1, i32* %w0) + %call7 = call i32* @external_sink_ret2_nrw(i32* %r1, i32* %r0, i32* %w0) + %call8 = call i32* @internal_ret0_nw(i32* %r1, i32* %w0) + br label %return + +return: ; preds = %if.end, %if.then + %retval.0 = phi i32* [ %call8, %if.end ], [ %r1, %if.then ] + ret i32* %retval.0 +} + +; CHECK: define dso_local i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0) #[[NOREC_NOUNWIND:[0-9]*]] +define dso_local i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { +entry: + %tobool = icmp ne i32* %n0, null + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + %0 = load i32, i32* %r0, align 4 + store i32 %0, i32* %w0, align 4 + br label %return + +return: ; preds = %if.end, %if.then + ret i32* %w0 +} + +; CHECK: define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) #[[NOUNWIND]] +define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) { +entry: + %0 = load i32, i32* %r0, align 4 + %tobool = icmp ne i32 %0, 0 + br i1 %tobool, label %if.end, label %if.then + +if.then: ; preds = %entry + br label %return + +if.end: ; preds = %entry + %call = call i32* @internal_ret1_rrw(i32* %r0, i32* %r0, i32* %w0) + %1 = load i32, i32* %r0, align 4 + store i32 %1, i32* %w0, align 4 + %call1 = call i32* @internal_ret0_nw(i32* %r0, i32* %w0) + %call2 = call i32* @internal_ret0_nw(i32* %w0, i32* %w0) + %call3 = call i32* @external_sink_ret2_nrw(i32* %r0, i32* %r0, i32* %w0) + %call4 = call i32* @external_ret2_nrw(i32* %r0, i32* %r0, i32* %w0) + br label %return + +return: ; preds = %if.end, %if.then + %retval.0 = phi i32* [ %call4, %if.end ], [ %w0, %if.then ] + ret i32* %retval.0 +} + +; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) #[[NOUNWIND]] +define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { +entry: + %call = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) + %call1 = call i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) + ret i32* %call1 +} + +; CHECK: attributes #[[NOUNWIND]] = { nounwind } +; CHECK: attributes #[[NOREC_NOUNWIND]] = { norecurse nounwind } diff --git a/llvm/test/Transforms/FunctionAttrs/arg_returned.ll b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -0,0 +1,445 @@ +; RUN: opt -functionattrs -S < %s | FileCheck %s +; +; Test cases specifically designed for the "returned" argument attribute. +; We use FIXME's to indicate problems and missing attributes. +; +; TEST 1: SCC test returning an integer value argument +; TEST 2: the same SCC as in 1 returning a pointer value argument +; TEST 3: a singleton SCC with a lot of recursive calls +; TEST 4: address taken function with call to an external functions +; TEST 5: call to a function that might be redifined at link time +; TEST 6: returned argument goes through select and phi +; TEST 7: returned argument goes through recursion, select, and phi +; TEST 8: returned argument goes through bitcasts +; TEST 9: returned argument goes through select and phi interleaved with bitcasts + + +; TEST 1 +; +; CHECK: define dso_local i32 @sink_r0(i32 returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable:#[0-9]]] +; +; FIXME: returned on %r missing: +; CHECK: define dso_local i32 @scc_r1(i32 %a, i32 %r, i32 %b) [[NoInlineNoUnwindReadnoneUwtable:#[0-9]]] +; +; FIXME: returned on %r missing: +; CHECK: define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 %r) [[NoInlineNoUnwindReadnoneUwtable]] +; +; int scc_r1(int a, int b, int r); +; int scc_r2(int a, int b, int r); +; +; __attribute__((noinline)) int sink_r0(int r) { +; return r; +; } +; +; __attribute__((noinline)) int scc_r1(int a, int r, int b) { +; return scc_r2(r, a, sink_r0(r)); +; } +; +; __attribute__((noinline)) int scc_r2(int a, int b, int r) { +; if (a > b) +; return scc_r2(b, a, sink_r0(r)); +; if (a < b) +; return scc_r1(sink_r0(b), scc_r2(scc_r1(a, b, r), scc_r1(a, scc_r2(r, r, r), r), scc_r2(a, b, r)), scc_r1(a, b, r)); +; return a == b ? r : scc_r2(a, b, r); +; } +; __attribute__((noinline)) int scc_rX(int a, int b, int r) { +; if (a > b) +; return scc_r2(b, a, sink_r0(r)); +; if (a < b) // V Diff to scc_r2 +; return scc_r1(sink_r0(b), scc_r2(scc_r1(a, b, r), scc_r1(a, scc_r2(r, r, r), r), scc_r1(a, b, r)), scc_r1(a, b, r)); +; return a == b ? r : scc_r2(a, b, r); +; } +; +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + +define dso_local i32 @sink_r0(i32 %r) #0 { +entry: + ret i32 %r +} + +define dso_local i32 @scc_r1(i32 %a, i32 %r, i32 %b) #0 { +entry: + %call = call i32 @sink_r0(i32 %r) + %call1 = call i32 @scc_r2(i32 %r, i32 %a, i32 %call) + ret i32 %call1 +} + +define dso_local i32 @scc_r2(i32 %a, i32 %b, i32 %r) #0 { +entry: + %cmp = icmp sgt i32 %a, %b + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %call = call i32 @sink_r0(i32 %r) + %call1 = call i32 @scc_r2(i32 %b, i32 %a, i32 %call) + br label %return + +if.end: ; preds = %entry + %cmp2 = icmp slt i32 %a, %b + br i1 %cmp2, label %if.then3, label %if.end12 + +if.then3: ; preds = %if.end + %call4 = call i32 @sink_r0(i32 %b) + %call5 = call i32 @scc_r1(i32 %a, i32 %b, i32 %r) + %call6 = call i32 @scc_r2(i32 %r, i32 %r, i32 %r) + %call7 = call i32 @scc_r1(i32 %a, i32 %call6, i32 %r) + %call8 = call i32 @scc_r2(i32 %a, i32 %b, i32 %r) + %call9 = call i32 @scc_r2(i32 %call5, i32 %call7, i32 %call8) + %call10 = call i32 @scc_r1(i32 %a, i32 %b, i32 %r) + %call11 = call i32 @scc_r1(i32 %call4, i32 %call9, i32 %call10) + br label %return + +if.end12: ; preds = %if.end + %cmp13 = icmp eq i32 %a, %b + br i1 %cmp13, label %cond.true, label %cond.false + +cond.true: ; preds = %if.end12 + br label %cond.end + +cond.false: ; preds = %if.end12 + %call14 = call i32 @scc_r2(i32 %a, i32 %b, i32 %r) + br label %cond.end + +cond.end: ; preds = %cond.false, %cond.true + %cond = phi i32 [ %r, %cond.true ], [ %call14, %cond.false ] + br label %return + +return: ; preds = %cond.end, %if.then3, %if.then + %retval.0 = phi i32 [ %call1, %if.then ], [ %call11, %if.then3 ], [ %cond, %cond.end ] + ret i32 %retval.0 +} + +define dso_local i32 @scc_rX(i32 %a, i32 %b, i32 %r) #0 { +entry: + %cmp = icmp sgt i32 %a, %b + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %call = call i32 @sink_r0(i32 %r) + %call1 = call i32 @scc_r2(i32 %b, i32 %a, i32 %call) + br label %return + +if.end: ; preds = %entry + %cmp2 = icmp slt i32 %a, %b + br i1 %cmp2, label %if.then3, label %if.end12 + +if.then3: ; preds = %if.end + %call4 = call i32 @sink_r0(i32 %b) + %call5 = call i32 @scc_r1(i32 %a, i32 %b, i32 %r) + %call6 = call i32 @scc_r2(i32 %r, i32 %r, i32 %r) + %call7 = call i32 @scc_r1(i32 %a, i32 %call6, i32 %r) + %call8 = call i32 @scc_r1(i32 %a, i32 %b, i32 %r) + %call9 = call i32 @scc_r2(i32 %call5, i32 %call7, i32 %call8) + %call10 = call i32 @scc_r1(i32 %a, i32 %b, i32 %r) + %call11 = call i32 @scc_r1(i32 %call4, i32 %call9, i32 %call10) + br label %return + +if.end12: ; preds = %if.end + %cmp13 = icmp eq i32 %a, %b + br i1 %cmp13, label %cond.true, label %cond.false + +cond.true: ; preds = %if.end12 + br label %cond.end + +cond.false: ; preds = %if.end12 + %call14 = call i32 @scc_r2(i32 %a, i32 %b, i32 %r) + br label %cond.end + +cond.end: ; preds = %cond.false, %cond.true + %cond = phi i32 [ %r, %cond.true ], [ %call14, %cond.false ] + br label %return + +return: ; preds = %cond.end, %if.then3, %if.then + %retval.0 = phi i32 [ %call1, %if.then ], [ %call11, %if.then3 ], [ %cond, %cond.end ] + ret i32 %retval.0 +} + + +; TEST 2 +; +; CHECK: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; +; FIXME: returned on %r missing: +; CHECK: define dso_local double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]] +; +; FIXME: returned on %r missing: +; CHECK: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) [[NoInlineNoUnwindReadnoneUwtable]] +; +; double* ptr_scc_r1(double* a, double* b, double* r); +; double* ptr_scc_r2(double* a, double* b, double* r); +; +; __attribute__((noinline)) double* ptr_sink_r0(double* r) { +; return r; +; } +; +; __attribute__((noinline)) double* ptr_scc_r1(double* a, double* r, double* b) { +; return ptr_scc_r2(r, a, ptr_sink_r0(r)); +; } +; +; __attribute__((noinline)) double* ptr_scc_r2(double* a, double* b, double* r) { +; if (a > b) +; return ptr_scc_r2(b, a, ptr_sink_r0(r)); +; if (a < b) +; return ptr_scc_r1(ptr_sink_r0(b), ptr_scc_r2(ptr_scc_r1(a, b, r), ptr_scc_r1(a, ptr_scc_r2(r, r, r), r), ptr_scc_r2(a, b, r)), ptr_scc_r1(a, b, r)); +; return a == b ? r : ptr_scc_r2(a, b, r); +; } +define dso_local double* @ptr_sink_r0(double* %r) #0 { +entry: + ret double* %r +} + +define dso_local double* @ptr_scc_r1(double* %a, double* %r, double* %b) #0 { +entry: + %call = call double* @ptr_sink_r0(double* %r) + %call1 = call double* @ptr_scc_r2(double* %r, double* %a, double* %call) + ret double* %call1 +} + +define dso_local double* @ptr_scc_r2(double* %a, double* %b, double* %r) #0 { +entry: + %cmp = icmp ugt double* %a, %b + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %call = call double* @ptr_sink_r0(double* %r) + %call1 = call double* @ptr_scc_r2(double* %b, double* %a, double* %call) + br label %return + +if.end: ; preds = %entry + %cmp2 = icmp ult double* %a, %b + br i1 %cmp2, label %if.then3, label %if.end12 + +if.then3: ; preds = %if.end + %call4 = call double* @ptr_sink_r0(double* %b) + %call5 = call double* @ptr_scc_r1(double* %a, double* %b, double* %r) + %call6 = call double* @ptr_scc_r2(double* %r, double* %r, double* %r) + %call7 = call double* @ptr_scc_r1(double* %a, double* %call6, double* %r) + %call8 = call double* @ptr_scc_r2(double* %a, double* %b, double* %r) + %call9 = call double* @ptr_scc_r2(double* %call5, double* %call7, double* %call8) + %call10 = call double* @ptr_scc_r1(double* %a, double* %b, double* %r) + %call11 = call double* @ptr_scc_r1(double* %call4, double* %call9, double* %call10) + br label %return + +if.end12: ; preds = %if.end + %cmp13 = icmp eq double* %a, %b + br i1 %cmp13, label %cond.true, label %cond.false + +cond.true: ; preds = %if.end12 + br label %cond.end + +cond.false: ; preds = %if.end12 + %call14 = call double* @ptr_scc_r2(double* %a, double* %b, double* %r) + br label %cond.end + +cond.end: ; preds = %cond.false, %cond.true + %cond = phi double* [ %r, %cond.true ], [ %call14, %cond.false ] + br label %return + +return: ; preds = %cond.end, %if.then3, %if.then + %retval.0 = phi double* [ %call1, %if.then ], [ %call11, %if.then3 ], [ %cond, %cond.end ] + ret double* %retval.0 +} + + +; TEST 3 +; +; int* ret0(int *a) { +; return *a ? a : ret0(ret0(ret0(...ret0(a)...))); +; } +; +; FIXME: returned on %a missing: +; CHECK: define dso_local i32* @ret0(i32* readonly %a) [[NoInlineNoUnwindReadonlyUwtable:#[0-9]*]] +define dso_local i32* @ret0(i32* %a) #0 { +entry: + %v = load i32, i32* %a, align 4 + %tobool = icmp ne i32 %v, 0 + %call = call i32* @ret0(i32* %a) + %call1 = call i32* @ret0(i32* %call) + %call2 = call i32* @ret0(i32* %call1) + %call3 = call i32* @ret0(i32* %call2) + %call4 = call i32* @ret0(i32* %call3) + %call5 = call i32* @ret0(i32* %call4) + %call6 = call i32* @ret0(i32* %call5) + %call7 = call i32* @ret0(i32* %call6) + %call8 = call i32* @ret0(i32* %call7) + %call9 = call i32* @ret0(i32* %call8) + %call10 = call i32* @ret0(i32* %call9) + %call11 = call i32* @ret0(i32* %call10) + %call12 = call i32* @ret0(i32* %call11) + %call13 = call i32* @ret0(i32* %call12) + %call14 = call i32* @ret0(i32* %call13) + %call15 = call i32* @ret0(i32* %call14) + %call16 = call i32* @ret0(i32* %call15) + %call17 = call i32* @ret0(i32* %call16) + %sel = select i1 %tobool, i32* %a, i32* %call17 + ret i32* %sel +} + + +; TEST 4 +; +; void unknown_fn(void *); +; +; int* calls_unknown_fn(int *r) { +; unknown_fn(&calls_unknown_fn); +; return r; +; } +; +; CHECK: declare void @unknown_fn(i32* (i32*)*) [[NoInlineNoUnwindUwtable:#[0-9]*]] +; +; CHECK: define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable]] +; +declare void @unknown_fn(i32* (i32*)*) #0 + +define dso_local i32* @calls_unknown_fn(i32* %r) #0 { + tail call void @unknown_fn(i32* (i32*)* nonnull @calls_unknown_fn) + ret i32* %r +} + + +; TEST 5 +; +; int *maybe_redefined_fn(int *r) { +; return r; +; } +; +; int *calls_maybe_redefined_fn(int *r) { +; maybe_redefined_fn(r); +; return r; +; } +; +; Verify the maybe-redefined function is not annotated: +; CHECK: define linkonce_odr i32* @maybe_redefined_fn(i32* %r) [[NoInlineNoRecurseNoUnwindUwtable:#[0-9]*]] +; FIXME: We should not derive norecurse for potentially redefined functions! +; define linkonce_odr i32* @maybe_redefined_fn(i32* %r) [[NoInlineNoUnwindUwtable]] +; +; CHECK: define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoRecurseNoUnwindUwtable]] +; FIXME: We should not derive norecurse for potentially redefined functions! +; define dso_local i32* @calls_maybe_redefined_fn(i32* returned %r) [[NoInlineNoUnwindUwtable]] +; +define linkonce_odr i32* @maybe_redefined_fn(i32* %r) #0 { +entry: + ret i32* %r +} + +define dso_local i32* @calls_maybe_redefined_fn(i32* %r) #0 { +entry: + %call = call i32* @maybe_redefined_fn(i32* %r) + ret i32* %r +} + + +; TEST 6 +; +; double select_and_phi(double b) { +; double x = b; +; if (b > 0) +; x = b; +; return b == 0? b : x; +; } +; +; FIXME: returned on %b missing: +; CHECK: define dso_local double @select_and_phi(double %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; +define dso_local double @select_and_phi(double %b) #0 { +entry: + %cmp = fcmp ogt double %b, 0.000000e+00 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + br label %if.end + +if.end: ; preds = %if.then, %entry + %phi = phi double [ %b, %if.then ], [ %b, %entry ] + %cmp1 = fcmp oeq double %b, 0.000000e+00 + %sel = select i1 %cmp1, double %b, double %phi + ret double %sel +} + + +; TEST 7 +; +; double recursion_select_and_phi(int a, double b) { +; double x = b; +; if (a-- > 0) +; x = recursion_select_and_phi(a, b); +; return b == 0? b : x; +; } +; +; FIXME: returned on %b missing: +; CHECK: define dso_local double @recursion_select_and_phi(i32 %a, double %b) [[NoInlineNoUnwindReadnoneUwtable]] +; +define dso_local double @recursion_select_and_phi(i32 %a, double %b) #0 { +entry: + %dec = add nsw i32 %a, -1 + %cmp = icmp sgt i32 %a, 0 + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %call = call double @recursion_select_and_phi(i32 %dec, double %b) + br label %if.end + +if.end: ; preds = %if.then, %entry + %phi = phi double [ %call, %if.then ], [ %b, %entry ] + %cmp1 = fcmp oeq double %b, 0.000000e+00 + %sel = select i1 %cmp1, double %b, double %phi + ret double %sel +} + + +; TEST 8 +; +; double* bitcast(int* b) { +; return (double*)b; +; } +; +; FIXME: returned on %b missing: +; CHECK: define dso_local double* @bitcast(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; +define dso_local double* @bitcast(i32* %b) #0 { +entry: + %bc0 = bitcast i32* %b to double* + ret double* %bc0 +} + + +; TEST 9 +; +; double* bitcasts_select_and_phi(int* b) { +; double* x = b; +; if (b == 0) +; x = b; +; return b != 0 ? b : x; +; } +; +; FIXME: returned on %b missing: +; CHECK: define dso_local double* @bitcasts_select_and_phi(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] +; +define dso_local double* @bitcasts_select_and_phi(i32* %b) #0 { +entry: + %bc0 = bitcast i32* %b to double* + %cmp = icmp eq double* %bc0, null + br i1 %cmp, label %if.then, label %if.end + +if.then: ; preds = %entry + %bc1 = bitcast i32* %b to double* + br label %if.end + +if.end: ; preds = %if.then, %entry + %phi = phi double* [ %bc1, %if.then ], [ %bc0, %entry ] + %bc2 = bitcast double* %phi to i8* + %bc3 = bitcast i32* %b to i8* + %cmp2 = icmp ne double* %bc0, null + %sel = select i1 %cmp2, i8* %bc2, i8* %bc3 + %bc4 = bitcast i8* %sel to double* + ret double* %bc4 +} + + +attributes #0 = { noinline nounwind uwtable } + +; CHECK-DAG: attributes [[NoInlineNoRecurseNoUnwindReadnoneUwtable]] = { noinline norecurse nounwind readnone uwtable } +; CHECK-DAG: attributes [[NoInlineNoUnwindReadnoneUwtable]] = { noinline nounwind readnone uwtable } +; CHECK-DAG: attributes [[NoInlineNoUnwindReadonlyUwtable]] = { noinline nounwind readonly uwtable } +; CHECK-DAG: attributes [[NoInlineNoUnwindUwtable]] = { noinline nounwind uwtable } +; CHECK-DAG: attributes [[NoInlineNoRecurseNoUnwindUwtable]] = { noinline norecurse nounwind uwtable }