ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.99k stars 2.49k forks source link

[proposal] Mark external function as pure #5838

Open pfgithub opened 4 years ago

pfgithub commented 4 years ago

Right now, there is no way to tell llvm to optimize external functions as if they were pure

godbolt example: https://godbolt.org/z/7xbo8r

godbolt screenshot

It would be useful if there was a way to mark external functions as pure to allow repeated calls to be optimized away

uniboi commented 2 weeks ago

I think this would fit nicely into the callconvention configuration proposed in #21209, for example like extern fn square(n: i64) callconv(.C : .{ .pure = true }) i64;.

mlugg commented 2 weeks ago

No, function purity is not related to calling convention.

wooster0 commented 2 weeks ago

It would also be nice if additionally only specific calls of an external function could be marked as pure, for example if you don't care whether there are side effects (maybe because it doesn't affect you) or if a function will not have side effects with specific input (e.g. behave_pure: bool).

This proposal doesn't propose specific syntax for Zig. If Zig had tags/annotations/attributes it might use those but because it doesn't have them for now I would propose adding a pure keyword, which can only appear after extern, after the library name, if any:

extern fn "library" pure square(num: c_int) callconv(.C) c_int;

Alternatively it could go after the parameters, before the return type.

To mark only specific calls of an external function as pure I propose:

@call(.pure, square, .{ 3 });

Calling non-external functions this way would be a compile error.

This could for example be used in the standard library to perhaps allow the compiler to optimize away unused memory allocations by marking call sites like this as pure:

https://github.com/ziglang/zig/blob/218cf059dd215282aa96d6b4715e68d533a4238e/lib/std/heap/PageAllocator.zig#L39

Status quo:

~$ cat x.zig
const std = @import("std");

pub fn main() void {
    _ = std.heap.page_allocator.alloc(u8, 1) catch return;
}
~$ zig build-exe x.zig -OReleaseFast && strace ./x 2>&1 | grep mmap
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f23fb1cb000
~$
mlugg commented 2 weeks ago

Ehhh, I don't think reserving pure as a keyword is a good idea. As a general rule, we try to minimise keywords, and when they do exist we want to get a lot of "bang for our buck" (e.g. inline is reserved as a keyword, but that gives us a whole bunch of constructs). This pure keyword would reserve a short identifier (=> more likely people want to use it), to have exactly one usage, which is itself a very rare usage.

wooster0 commented 2 weeks ago

I completely agree, it's just that I can't really think of any other way to specify it considering that you can't combine it with any existing constructs such as align, addrspace, linksection, or callconv. You need a new construct as far as I can tell. If not a keyword I guess you could introduce some other syntax using sigils or something but not sure how readable that would be.