Difference between binary disassembly generated for Rust and C in compiler explorer

806 views Asked by At

I was observing assembly generated for an equal program in Rust and C in the Compiler Explorer, in "binary" mode to look at the whole linked executable, not just compiler generated assembly for specific functions.

Rust program (on Godbolt)

pub fn square(num: i32) -> i32 {
    num * num
}

fn main () {
    let n = 4;
    let _x = square(n);
}

C program (on Godbolt)

int square(int num) {
    return num * num;
}

int main() {
    int n = 4;
    int x = square(n);
}

But the disassembly generated for these two are very different. I can partially understand the assembly generated for the C program, that is much shorter and more understandable for me. But I can't understand anything from assembly generated for Rust program.

So my question is why there is so much difference in length of these assembly programs? Am I using compiler explorer in a wrong way?

2

There are 2 answers

2
user253751 On BEST ANSWER

The Rust one seems to be showing a bunch of code from the Rust standard library.

This is simply a problem with Compiler Explorer. It knows how to hide the C standard library but it doesn't know how to hide the Rust one. You can see that if you click on "Filter", with the Rust program the option to hide library functions is greyed out.

Another option is to uncheck "Compile to binary". Then, the compiler will only compile your code and disassemble it before linking in the standard library. The code might be slightly different as it won't have been linked. (Thanks to Stargateur for this suggestion)

4
0___________ On

There are plenty of internals in this listing.

If you enable optimizations (for example for size -C opt-level=s) and search in listing for interesting part (ie main method) you will see that it was almost optimized out as you do not use calculated values

If you use those values you will see something more interesting:

pub fn square(num: i32) -> i32 {
    num * num
}

fn main () {
    let n = 4;
    let _x = square(n);
    println!("{} {}", n, _x);
}

Here your method square is not invoked as compiler can calculate the result compile time. RUST code will be always less efficient and more complicated than the identical C code

example::main:
 sub rsp,0x58
 mov rax,rsp
 mov DWORD PTR [rax],0x4
 lea rcx,[rsp+0x4]
 mov DWORD PTR [rcx],0x10
 lea rdx,[rsp+0x8]
 mov QWORD PTR [rdx],rax
 lea rax,[rip+0x33e70] # 3b8f0 <core::fmt::num::imp::<impl core::fmt::Display for i32>::fmt>
 mov QWORD PTR [rdx+0x8],rax
 mov QWORD PTR [rdx+0x10],rcx
 mov QWORD PTR [rdx+0x18],rax
 lea rax,[rip+0x44755] # 4c1e8 <anon.7152d18045a77e825c19a8010d1656c2.0.llvm.7612792893684456700+0x30>
 lea rdi,[rsp+0x28]
 mov QWORD PTR [rdi],rax
 mov QWORD PTR [rdi+0x8],0x3
 mov QWORD PTR [rdi+0x10],0x0
 mov QWORD PTR [rdi+0x20],rdx
 mov QWORD PTR [rdi+0x28],0x2
 call QWORD PTR [rip+0x472bb] # 4ed78 <_GLOBAL_OFFSET_TABLE_+0x460>
 add rsp,0x58
 ret 

https://godbolt.org/z/z6sheTvjd