Skip to content

Quadratic code size due to combination of ? and Drop for linear code #120604

Open
@psychon

Description

@psychon

Hi,

first: Sorry if this is a duplicate. I tried, but did not find anything.

There is a bug report for x11rb about code size. Some macro caused 111 KiB of code to be emited: psychon/x11rb#912

I tried to produce a self-contained example of what is going on and here is what I came up with the following

Playground link

macro_rules! the_macro {
    {
        $cookie_name:ident { $($field_name:ident,)* }
    } => {
        struct $cookie_name {
            $($field_name: support::Cookie,)*
        }
        
        #[inline(never)]
        fn send_requests() -> Result<$cookie_name, support::Error> {
            Ok($cookie_name {
                $($field_name: support::send_request()?,)*
            })
        }
    }
}

the_macro!{Cookies { a, b, c, d, e, } }

fn main() {
    let _ = send_requests();
}

mod support {
    #[inline(never)]
    pub fn send_request() -> Result<Cookie, Error> {
        // Some code to trick the optimizer into not optimising everything away
        if std::env::args().next().is_some() {
            Ok(Cookie(42))
        } else {
            Err(Error)
        }
    }
    
    pub struct Error;
    
    pub struct Cookie(u16);
    impl Drop for Cookie {
        #[inline(never)]
        fn drop(&mut self) {
            println!("{}", self.0);
        }
    }
}

(#[inline(never)] just exists so that I can read the resulting assembly; the original code does not use inline hints, but instead the functions are big enough not to be inlined. I'm not sure whether drop() of the Cookie perhaps ends up being inlined and being part of the huge code size.)

This code uses a macro, so just to make it clear what the problem is, here is the relevant function after macro expansion:

#[inline(never)]
fn send_requests() -> Result<Cookies, support::Error> {
    Ok(Cookies {
            a: support::send_request()?,
            b: support::send_request()?,
            c: support::send_request()?,
            d: support::send_request()?,
            e: support::send_request()?,
        })
}

I expected to see this happen: Linear code size (= small code)

Instead, this happened: This results in quadratic code size due to the drop calls.

Looking at the generated ASM in release mode and trying to translate this back into something rust-y, the code looks like this:

fn send_requests() -> Result<Cookies, support::Error> {
    let a = match support_send_request() {
       Err(e) => return Err(e);
       Ok(a) => a;
    };
    let b = match support_send_request() {
       Err(e) => { drop(a); return Err(e) };
       Ok(b) => b;
    };
    let c = match support_send_request() {
       Err(e) => { drop(a); drop(b); return Err(e) };
       Ok(c) => c;
    };
    let d = match support_send_request() {
       Err(e) => { drop(a); drop(b); drop(c); return Err(e) };
       Ok(d) => d;
    };
    let e = match support_send_request() {
       Err(e) => { drop(a); drop(b); drop(c); drop(d); return Err(e) };
       Ok(e) => e;
    };
    Ok(Cookies { a, b, c, d, e })
}

The actual code where this was observed has not just 5 cases (a, b, c, d, e) but 59 ones. Here, the quadratic behaviour starts to hurt a lot. I counted 1709 calls to functions with drop_in_place in their name.

Meta

rustc --version --verbose: Uhm... I tried this on the playground. But I can also reproduce this with my ancient version locally:

rustc 1.70.0
binary: rustc
commit-hash: unknown
commit-date: unknown
host: x86_64-unknown-linux-gnu
release: 1.70.0
LLVM version: 16.0.6

Metadata

Metadata

Assignees

Labels

C-bugCategory: This is a bug.C-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-heavyIssue: Problems and improvements with respect to binary size of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions