Why Zig can't optimize out calls to `span` and `trim` here?

I want to assert string is trimmed, but when compiling ReleaseFast the generated code includes pointless (inlined) code of span and trim.

See:

Is there a way to avoid generating code for these calls?

1 Like

Interesting. Seems the compiler cannot eliminate std.mem.span() even when the result isn’t used Compiler Explorer

As for fun2, a simpler check will do the trick Compiler Explorer

3 Likes

It cannot eliminate all the functions!

const std = @import("std");

export fn fun(p: [*:0]const u8) void {
    std.debug.assert(std.mem.len(p) > 0);
}

Thank you for the insight, it will help.

But if I want to be sure that no junk code is generated for the assert condition, would if (std.debug.runtime_safety) be a sane option? It looks to way hacky for me, like #ifndef NDEBUG from C :grin:

1 Like

The reason is quite simple. The compiler is not allowed to change the result of a function call.
Despite the return type being void, there are actually two different possible results here:
Either the function returns, or it doesn’t (by e.g. containing an infinite loop).

At the heart of std.mem.span is basically a loop like this (+ some vectorization logic):

while (p[i] != 0) {
    i += 1;
}

Now the compiler cannot decide whether this will loop infinitely or ends eventually.
Now in theory the compiler should be able to deduce that the function ends by the fact that zig asserts that there is no integer overflow, but for whatever reason it doesn’t.

If we set a limit on the maximum number of iterations, then the compiler is able to remove the loop code:

while (p[i] != 0 and i < 100000000) {
    i += 1;
}

Here it is on godbolt: Compiler Explorer

6 Likes