From c49e4d534fb8e8ec994a8ffbc62eae8ef1e4e819 Mon Sep 17 00:00:00 2001 From: Frank Denis <124872+jedisct1@users.noreply.github.com> Date: Fri, 9 Dec 2022 18:22:50 +0100 Subject: [PATCH] Improve and remove duplicate doNotOptimizeAway() implementations (#13790) * Improve and remove duplicate doNotOptimizeAway() implementations We currently have two doNotOptimizeAway() implementations, one in std.math and the other one in std.mem. Maybe we should deprecate one. In the meantime, the std.math one now just calls the std.mem one. In a comptime environment, just ignore the value. Previously, std.mem.doNotOptimizeAway() did not work at comptime. If the value fits in a CPU register, just tell the compiler we need that value to be computed, without clobbering anything else. Only clobber all possibly escaped memory on pointers or large arrays. Add tests by the way since we didn't had any (we had, but only indirect ones). --- lib/std/math.zig | 9 ++---- lib/std/mem.zig | 73 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index fdc8225709..4e1793c747 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -195,13 +195,8 @@ test "approxEqAbs and approxEqRel" { } } -pub fn doNotOptimizeAway(value: anytype) void { - // TODO: use @declareSideEffect() when it is available. - // https://github.com/ziglang/zig/issues/6168 - const T = @TypeOf(value); - var x: T = undefined; - const p = @ptrCast(*volatile T, &x); - p.* = x; +pub fn doNotOptimizeAway(val: anytype) void { + return mem.doNotOptimizeAway(val); } pub fn raiseInvalid() void { diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 16ee56b74f..c0385f6e98 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -3559,12 +3559,75 @@ pub fn alignForwardGeneric(comptime T: type, addr: T, alignment: T) T { /// Force an evaluation of the expression; this tries to prevent /// the compiler from optimizing the computation away even if the /// result eventually gets discarded. +// TODO: use @declareSideEffect() when it is available - https://github.com/ziglang/zig/issues/6168 pub fn doNotOptimizeAway(val: anytype) void { - asm volatile ("" - : - : [val] "rm" (val), - : "memory" - ); + var a: u8 = 0; + if (@typeInfo(@TypeOf(.{a})).Struct.fields[0].is_comptime) return; + + const max_gp_register_bits = @bitSizeOf(c_long); + const t = @typeInfo(@TypeOf(val)); + switch (t) { + .Void, .Null, .ComptimeInt, .ComptimeFloat => return, + .Enum => doNotOptimizeAway(@enumToInt(val)), + .Bool => doNotOptimizeAway(@boolToInt(val)), + .Int => { + const bits = t.Int.bits; + if (bits <= max_gp_register_bits) { + const val2 = @as( + std.meta.Int(t.Int.signedness, @max(8, std.math.ceilPowerOfTwoAssert(u16, bits))), + val, + ); + asm volatile ("" + : + : [val2] "r" (val2), + ); + } else doNotOptimizeAway(&val); + }, + .Float => { + if (t.Float.bits == 32 or t.Float.bits == 64) { + asm volatile ("" + : + : [val] "rm" (val), + ); + } else doNotOptimizeAway(&val); + }, + .Pointer => asm volatile ("" + : + : [val] "m" (val), + : "memory" + ), + .Array => { + if (t.Array.len * @sizeOf(t.Array.child) <= 64) { + for (val) |v| doNotOptimizeAway(v); + } else doNotOptimizeAway(&val); + }, + else => doNotOptimizeAway(&val), + } +} + +test "doNotOptimizeAway" { + comptime doNotOptimizeAway("test"); + + doNotOptimizeAway(null); + doNotOptimizeAway(true); + doNotOptimizeAway(0); + doNotOptimizeAway(0.0); + doNotOptimizeAway(@as(u1, 0)); + doNotOptimizeAway(@as(u3, 0)); + doNotOptimizeAway(@as(u8, 0)); + doNotOptimizeAway(@as(u16, 0)); + doNotOptimizeAway(@as(u32, 0)); + doNotOptimizeAway(@as(u64, 0)); + doNotOptimizeAway(@as(u128, 0)); + doNotOptimizeAway(@as(u13, 0)); + doNotOptimizeAway(@as(u37, 0)); + doNotOptimizeAway(@as(u96, 0)); + doNotOptimizeAway(@as(u200, 0)); + doNotOptimizeAway(@as(f32, 0.0)); + doNotOptimizeAway(@as(f64, 0.0)); + doNotOptimizeAway([_]u8{0} ** 4); + doNotOptimizeAway([_]u8{0} ** 100); + doNotOptimizeAway(@as(std.builtin.Endian, .Little)); } test "alignForward" {