Hi - I've been trying to track down why a function pointer isn't being inlined in some cases, and have nailed it down to the following repro: struct s { int (*cmp) (const void *, const void *); int its[2]; }; static int __uint_compare(const void *e1, const void *e2) { const int *i1 = e1; const int *i2 = e2; return *i2 - *i1; } int main() { struct s hp; hp.cmp = __uint_compare; hp.its[0] = 1; hp.its[1] = 2; for (int i = 0; i < 1; i++) { int cmp = hp.cmp(&hp.its[i], &hp.its[i + 1]); if(cmp < 0) { return 1; } } return 0; } I've compiled it (as test2.c) with the following commands, and have attached the unoptimised and optimised outputs. clang -O0 -S -emit-llvm -o test2.ll test2.c opt -O3 -debug -S test2.ll > test2.opt.ll 2>log2 However, if I replace the main function with the code below (test.c), the function call to __uint_compare is inlined and the whole function is correctly replaced with a "ret i32 0". int main() { struct s hp; hp.cmp = __uint_compare; hp.its[0] = 1; hp.its[1] = 2; int cmp = hp.cmp(&hp.its[0], &hp.its[1]); if(cmp < 0) { return 1; } return 0; } As you can see, the first example always executes the for-loop exactly once, so the semantics of both examples are identical. From looking at the differences in the two (attached) log files, it seems like the problem's in the GVN pass, as only the log for the second example has: GVN iteration: 0 GVN removed: %5 = load i32 (i8*, i8*)** %1, align 8 GVN iteration: 1 Is this analysis correct? Or is some other pass getting tricked by the for loop (maybe because the load & store are no longer in the same basic block)? I was thinking of trying to fix it myself, but am not sure exactly where in the codebase I should get started. Thanks - Nick -------------- next part -------------- A non-text attachment was scrubbed... Name: test.c Type: text/x-csrc Size: 368 bytes Desc: not available URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140924/b915e04d/attachment.c> -------------- next part -------------- A non-text attachment was scrubbed... Name: test.ll Type: application/octet-stream Size: 2787 bytes Desc: not available URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140924/b915e04d/attachment.obj> -------------- next part -------------- A non-text attachment was scrubbed... Name: test.opt.ll Type: application/octet-stream Size: 716 bytes Desc: not available URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140924/b915e04d/attachment-0001.obj> -------------- next part -------------- A non-text attachment was scrubbed... Name: test2.c Type: text/x-csrc Size: 412 bytes Desc: not available URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140924/b915e04d/attachment-0001.c> -------------- next part -------------- A non-text attachment was scrubbed... Name: test2.ll Type: application/octet-stream Size: 3486 bytes Desc: not available URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140924/b915e04d/attachment-0002.obj> -------------- next part -------------- A non-text attachment was scrubbed... Name: test2.opt.ll Type: application/octet-stream Size: 2404 bytes Desc: not available URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140924/b915e04d/attachment-0003.obj> -------------- next part -------------- A non-text attachment was scrubbed... Name: log Type: application/octet-stream Size: 33397 bytes Desc: not available URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140924/b915e04d/attachment-0004.obj> -------------- next part -------------- A non-text attachment was scrubbed... Name: log2 Type: application/octet-stream Size: 49499 bytes Desc: not available URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20140924/b915e04d/attachment-0005.obj>
Looks a bit similar to http://llvm.org/bugs/show_bug.cgi?id=20801 2014-09-24 16:51 GMT+04:00 Nicholas White <n.j.white at gmail.com>:> Hi - I've been trying to track down why a function pointer isn't being > inlined in some cases, and have nailed it down to the following repro: > > struct s > { > int (*cmp) (const void *, const void *); > int its[2]; > }; > > static int __uint_compare(const void *e1, const void *e2) > { > const int *i1 = e1; > const int *i2 = e2; > return *i2 - *i1; > } > > int main() > { > struct s hp; > hp.cmp = __uint_compare; > hp.its[0] = 1; > hp.its[1] = 2; > > for (int i = 0; i < 1; i++) > { > int cmp = hp.cmp(&hp.its[i], &hp.its[i + 1]); > if(cmp < 0) > { > return 1; > } > } > > return 0; > } > > I've compiled it (as test2.c) with the following commands, and have > attached the unoptimised and optimised outputs. > > clang -O0 -S -emit-llvm -o test2.ll test2.c > opt -O3 -debug -S test2.ll > test2.opt.ll 2>log2 > > However, if I replace the main function with the code below (test.c), > the function call to __uint_compare is inlined and the whole function > is correctly replaced with a "ret i32 0". > > int main() > { > struct s hp; > hp.cmp = __uint_compare; > hp.its[0] = 1; > hp.its[1] = 2; > > int cmp = hp.cmp(&hp.its[0], &hp.its[1]); > if(cmp < 0) > { > return 1; > } > > return 0; > } > > As you can see, the first example always executes the for-loop exactly > once, so the semantics of both examples are identical. From looking at > the differences in the two (attached) log files, it seems like the > problem's in the GVN pass, as only the log for the second example has: > > GVN iteration: 0 > GVN removed: %5 = load i32 (i8*, i8*)** %1, align 8 > GVN iteration: 1 > > Is this analysis correct? Or is some other pass getting tricked by the > for loop (maybe because the load & store are no longer in the same > basic block)? I was thinking of trying to fix it myself, but am not > sure exactly where in the codebase I should get started. > > Thanks - > > Nick > > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev >
On 24 September 2014 13:51, Nicholas White <n.j.white at gmail.com> wrote:> Or is some other pass getting tricked by the > for loop (maybe because the load & store are no longer in the same > basic block)?That'd be my guess. Maybe looking for where the debug messages are printed out and follow the code up. cheers, --renato
I've CC'ed Chad Rosier as I think this behaviour is a side-effect of his revert of IndVarSimplify.cpp (git c6b1a7e577a0b9e9cff9f9b7ac35a2cde7c448d8, SVN 217962). The change basically makes the IndVar pass change: ; <label>:4 ; preds = %6, %0 %i.0 = phi i32 [ 0, %0 ], [ %11, %6 ] %5 = icmp eq i32 %i.0, 0 br i1 %5, label %6, label %17 To: ; <label>:4 ; preds = %7, %0 %indvars.iv = phi i64 [ %indvars.iv.next, %7 ], [ 0, %0 ] %5 = trunc i64 %indvars.iv to i32 %6 = icmp eq i32 %5, 0 br i1 %6, label %7, label %15 Whereas before the patch it would be: ; <label>:4 ; preds = %7, %0 %indvars.iv = phi i64 [ %indvars.iv.next, %7 ], [ 0, %0 ] %5 = icmp eq i64 %indvars.iv, 0 br i1 %5, label %6, label %15 This has a knock-on effect on the GVN pass; in the basic block that is the body of the loop it only knows that "trunc i64 %indvars.iv to i32" is zero, not that "%indvars.iv" is zero, so it can't convert the "add i64 %indvars.iv, 1" to a constant 1, and so doesn't realise the body of the loop is only ever executed once. I'll keep digging... Thanks, Nick