Hacker News new | past | comments | ask | show | jobs | submit login

Since LLVM does not have the necessary information to do the optimizations, I wonder whether the same problem occurs in C++ code compiled with clang.



It depends on the system. First, the int would be by value anyway, so it wouldn't matter. But assume you handed it a pointer instead.

Some are properly marked readonly in declarations, some are not[1]

But this is a common problem with unmarked logging. The compiler will infer attributes all the way through things it has bitcode for. (IE it can determine it to be readonly, readnone, whatever, as long as it can see calls to either functions marked readonly/readnone, or determine the called functions to be readonly/readnone)

But, usually, if you don't give it the whole program, it can't infer that your logging function does not modify its arguments.

[1] Fun fact: glibc allows printf handler registration: https://www.gnu.org/software/libc/manual/html_node/Customizi...

So sadly, it's printf is not readonly, because it can do anything it wants in these callbacks.

(nobody uses the printf registration, though, so it may be time to just add a compiler option required to make it work, and not screw everyone else using printf over)


In this particular case, you'd likely be using printf or cout, both of which perform a copy of integer parameters.

So you wouldn't see this there.


It may not. It's possible that this may be happening because rustc is failing to mark the reference as both readonly and unaliased, whereas clang might mark the equivalent as both. You'd need to examine the IR from both front ends to really figure out the source of any differences in the generated ASM


Both Rust playgrounds (official[1], my take[2]) allow viewing the LLVM IR of a Rust program.

The call to the print is

      call void @_ZN3std2io5stdio6_print17h690779b3bd8114d5E(%"core::fmt::Arguments"* noalias nocapture nonnull dereferenceable(48) %_3)
The entire chunk of `main` preceding that call:

      %size = alloca i64, align 8
      %_3 = alloca %"core::fmt::Arguments", align 8
      %_8 = alloca [1 x %"core::fmt::ArgumentV1"], align 8
      %0 = bitcast i64* %size to i8*
      call void @llvm.lifetime.start(i64 8, i8* %0)
      store i64 33554432, i64* %size, align 8
      %1 = bitcast %"core::fmt::Arguments"* %_3 to i8*
      call void @llvm.lifetime.start(i64 48, i8* %1)
      %2 = bitcast [1 x %"core::fmt::ArgumentV1"]* %_8 to i8*
      call void @llvm.lifetime.start(i64 16, i8* %2)
      %3 = ptrtoint i64* %size to i64
      %4 = bitcast [1 x %"core::fmt::ArgumentV1"]* %_8 to i64*
      store i64 %3, i64* %4, align 8
      %5 = getelementptr inbounds [1 x %"core::fmt::ArgumentV1"], [1 x %"core::fmt::ArgumentV1"]* %_8, i64 0, i64 0, i32 1
      %6 = bitcast i8 (%"core::fmt::Void"*, %"core::fmt::Formatter"*)** %5 to i64*
      store i64 ptrtoint (i8 (i64*, %"core::fmt::Formatter"*)* @"_ZN4core3fmt3num54_$LT$impl$u20$core..fmt..Display$u20$for$u20$usize$GT$3fmt17hb872170870cc06d9E" to i64), i64* %6, align 8
      %7 = getelementptr inbounds [1 x %"core::fmt::ArgumentV1"], [1 x %"core::fmt::ArgumentV1"]* %_8, i64 0, i64 0
      %8 = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 0, i32 0
      store %str_slice* getelementptr inbounds ([2 x %str_slice], [2 x %str_slice]* @ref.8, i64 0, i64 0), %str_slice** %8, align 8, !alias.scope !1, !noalias !4
      %9 = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 0, i32 1
      store i64 2, i64* %9, align 8, !alias.scope !1, !noalias !4
      %_6.sroa.0.0..sroa_idx.i = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 1, i32 0, i32 0
      store %"core::fmt::rt::v1::Argument"* null, %"core::fmt::rt::v1::Argument"** %_6.sroa.0.0..sroa_idx.i, align 8, !alias.scope !1, !noalias !4
      %10 = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 2, i32 0
      store %"core::fmt::ArgumentV1"* %7, %"core::fmt::ArgumentV1"** %10, align 8, !alias.scope !1, !noalias !4
      %11 = getelementptr inbounds %"core::fmt::Arguments", %"core::fmt::Arguments"* %_3, i64 0, i32 2, i32 1
      store i64 1, i64* %11, align 8, !alias.scope !1, !noalias !4
      call void @_ZN3std2io5stdio6_print17h690779b3bd8114d5E(%"core::fmt::Arguments"* noalias nocapture nonnull dereferenceable(48) %_3)
[1]: https://play.rust-lang.org/

[2]: http://play.integer32.com/


Am I reading it right? It looks to me like rustc didn't mark the immutable borrow as readonly, so LLVM went ahead and assumed print could mutate it.


I'm not 100% sure, but I believe LLVM doesn't have a way to understand the (im)mutability of pointers rewritten into memory, like the borrow is here.


If you use printf and cout in the same program then yes you can have dramatic performance problems with cout unless you use `std::ios_base::sync_with_stdio(false);`

It's not the same issue but it's the same area of stupid stuff you have to find out about and roll your eyes.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: