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

Time:09-10

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?

CodePudding user response:

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)

CodePudding user response:

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

  • Related