5

I'd like to compare the assembly of different implementations of functions but I don't want to implement certain functions, just declare them.

In Rust, forward declaration are typically unnecessary, due to the compiler not needing the functions in order to resolve them (unlike in C). However, is it possible to do something equivalent to a forward declaration?

hellow
  • 12,430
  • 7
  • 56
  • 79
  • For a reason: I'd like to bench some functions in [godbolt](https://rust.godbolt.org) but I don't want to implement certain functions, just delcare them. – hellow Mar 16 '20 at 08:10
  • 3
    Are you asking for `extern` block? This might lead to linker errors, however. – Cerberus Mar 16 '20 at 08:18
  • `extern` seems to look good, but that needs an `unsafe` block to use. Is there a way not to use `unsafe? – hellow Mar 16 '20 at 08:24
  • 2
    If the function isn't called in your code, you can define it with `unimplemented!()` body. Could you share an example of the expected code? – Cerberus Mar 16 '20 at 08:24
  • 2
    Please note: `panic!()` will mark every code after it as unreachable and therefore optimize it away. That's not what I want. https://rust.godbolt.org/z/SK8SEL – hellow Mar 16 '20 at 09:05
  • 1
    "I'd like to bench some functions" - you want to look at the generated assembly is what you mean? – Sebastian Redl Mar 16 '20 at 12:26
  • @SebastianRedl yes. I'd like to compare different implementations of functions. – hellow Mar 16 '20 at 12:27
  • What about an empty function with `#[inline(never)]`? – rodrigo Mar 17 '20 at 08:45
  • @rodrigo does it work? If yes, post an answer :) – hellow Mar 17 '20 at 09:55

3 Answers3

7

If you declare your function as #[inline(never)] you will get a function call instruction to prevent further optimizations.

The main limitation is that your function must not be empty after optimizations, so it must have some side effect (thanks to @hellow that suggests using compiler_fence instead of println!).

For example, this code (godbolt):

pub fn test_loop(num: i32) {
    for _i in 0..num {
        dummy();
    }
}

#[inline(never)]
pub extern fn dummy() {
    use std::sync::atomic::*;
    compiler_fence(Ordering::Release);
}

Will produce the following assembly (with -O), that I think you need:

example::test_loop:
        push    r14
        push    rbx
        push    rax
        test    edi, edi
        jle     .LBB0_3
        mov     ebx, edi
        mov     r14, qword ptr [rip + example::dummy@GOTPCREL]
.LBB0_2:
        call    r14
        add     ebx, -1
        jne     .LBB0_2
.LBB0_3:
        add     rsp, 8
        pop     rbx
        pop     r14
        ret

plus the code for dummy() that is actually empty:

example::dummy:
        ret
rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • `extern "C"` is another option so you don't even have to provide a definition. [How to disable LLVM optimisations for rust in Godbolt (for asm education purposes)](https://stackoverflow.com/a/72564785) shows an example. – Peter Cordes Jun 09 '22 at 18:08
  • 1
    @PeterCordes instead of `extern "C"` I would recommend `extern "Rust"` ;) – hellow Jun 10 '22 at 05:50
  • @hellow: Oh, that works? Then sure. I barely know Rust, just occasionally dipping a toe in, and `extern "C"` was all that was mentioned on https://doc.rust-lang.org/std/keyword.extern.html which came up in my google search for "rust declare extern function". – Peter Cordes Jun 10 '22 at 06:02
  • The `extern "Rust"` is great. I was worried that using a `extern "C"` would include some cross-language code. A small ergonomic issue is that you need to use `unsafe` to call any extern function. – rodrigo Jun 10 '22 at 09:00
5

It's not possible to forward-declare functions. There is only a single declaration for any given entity in Rust.

However, you can use the unimplemented!() and todo!() macros to quickly fill in the bodies of functions you don't want to implement (yet) for some reason. Both are basically aliases for panic!() with specific error messages.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • 1
    I thought about that as well, but the compiler will optimize the panic call and mark every code afterwards as unreachable. https://rust.godbolt.org/z/SK8SEL – hellow Mar 16 '20 at 09:04
  • So? The compiler is right, the code afterwards is unreachable. If you actually want to run something, you can't just not implement it. – Sebastian Redl Mar 16 '20 at 12:26
  • Indeed, but in C you can just forward declare the function and the generated assembly will be a function call and then will continue normally. – hellow Mar 16 '20 at 12:30
  • 1
    Is black_box what you're looking for? https://doc.rust-lang.org/std/hint/fn.black_box.html – nnnmmm Mar 16 '20 at 14:07
  • @nnnmmm is that usable with functions? It's unstable as well (which is worse than `unsafe` ;) ) in my case, because I'd like to compare different stable compilers – hellow Mar 16 '20 at 14:40
  • `extern "C"` does allow a forward declaration without a definition. [How to disable LLVM optimisations for rust in Godbolt (for asm education purposes)](https://stackoverflow.com/a/72564785) shows an example of using it to make asm you can look at. https://doc.rust-lang.org/std/keyword.extern.html (@hellow) – Peter Cordes Jun 09 '22 at 18:10
  • @PeterCordes That sounds like something you should post as an answer. – Sebastian Redl Jun 10 '22 at 08:49
  • It is in my answer, on the highly related but not quite duplicate question I linked. I didn't want to close this one as a duplicate (or that newer one as a duplicate of this), but I also didn't want to copy/paste large parts of my answer. I ended up commenting here, which isn't ideal either. – Peter Cordes Jun 10 '22 at 08:52
1

Declare a trait and have your function and its signature in there. eg.

pub trait Summary {
  fn summarize(&self) -> String;
}
  • 1
    Thank you for your answer. Can you elaborate it more, e.g. add some example code? Also, do you think it is what OP (me) inteded to do? – hellow Nov 12 '21 at 10:56
  • Perhaps I mis-undertood the request. You sought too declare a function without providing the body. That is exactly what I did. And I gave an example. In Object oriented languages you can declare a method and the implement it later. Traits in rust provide that ability. – user3593503 Nov 13 '21 at 18:05
  • The goal here is similar to [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116) - you want the compiler to see a function call it doesn't know how to inline, so any reference args have to actually exist in memory, and so on. And so the function will be non-leaf. Like this C example: https://godbolt.org/z/M5KxPfqWT – Peter Cordes Jun 09 '22 at 17:33