#safe to call `callconv(.Unspecified)` function pointer?

1 messages · Page 1 of 1 (latest)

stiff flume
#

is it safe to call these? i thought it could be, as it seemed to be working, but now i see some function arguments are not matching what got passed in.

keen anchor
#

callconv(.Unspecified) is what regular functions are that should be fine

#

any vtable in zig is relying on that being fine

stiff flume
#

ugh then why was my code breaking

#

also linking libc or not changed whether it broke

stiff flume
stiff flume
#

k repro time

#

then:

$ lldb ./zig-out/bin/bench
(lldb) b tail.zig:37
(lldb) run web-host/public/flappyai.ch8 1000
// it hits the breakpoint...
(lldb) watch set expression &self.code[0x204] // this is the index of self.code that gets written
(lldb) c
// hits the watchpoint
-> 145      cpu.code[cpu.pc] = inst;
(lldb) p inst.decoded
(tail.Decoded) {
  payload = {
    xy = "\v\r"
    xnn = (0 = '\v', 1 = '\r')
    nnn = 3339
    xyn = "\v\r"
  }
  tag = xy
}
(lldb) step
-> 146      @call(.always_tail, inst.func, .{ cpu, inst.decoded });
(lldb) step
-> 106  pub fn addRegisters(cpu: *Cpu, decoded: Decoded) void {
(lldb) p decoded
(tail.Decoded) {
  payload = {
    xy = "\b\xf9"
    xnn = (0 = '\b', 1 = '\xf9')
    nnn = 63752
    xyn = "\b\xf9\U00000003"
  }
  tag = xnn
}

the tag changed from xy to xnn

stiff flume
#

is this a miscomp or am i just stupid

#

trying to zig reduce to see if i can find anything

#

i wrote a small test passing bare unions through a function pointer and it wasn't obviously broken

tribal sun
#

To be clear, where is the function being called, and where was it passed from?

#

Ie, is it being called from C code?

#

Are there multiple Zig compilation units?

stiff flume
#

no C code, only one zig unit

#

caller is https://github.com/190n/zip8/blob/oddity/src/tail.zig#L146
it calls https://github.com/190n/zip8/blob/oddity/src/tailfuncs.zig#L107, although the inactive union field occurs on different functions in that file depending on build options

GitHub

CHIP-8 emulator in Zig. Contribute to 190n/zip8 development by creating an account on GitHub.

GitHub

CHIP-8 emulator in Zig. Contribute to 190n/zip8 development by creating an account on GitHub.

stiff flume
#

ok it also doesn't seem to happen if i don't use tail calls

stiff flume
#
const std = @import("std");

const SideData = union {
    foo: u8,
    bar: u8,
};

const Inst = struct {
    func: Gadget,
    data: SideData,
};

const Emulator = struct {
    memory: [128]u8,
    code: [64]Inst,
    pc: u8 = 0,
};

const Gadget = *const fn (*Emulator, SideData) void;

fn foo(emulator: *Emulator, side_data: SideData) void {
    _ = emulator;
    std.log.info("foo {}", .{side_data.foo});
}

fn bar(emulator: *Emulator, side_data: SideData) void {
    _ = emulator;
    _ = side_data;
    @trap();
}

fn invalid(emulator: *Emulator, _: SideData) void {
    _ = emulator;
}

fn decode(emulator: *Emulator, _: SideData) void {
    const raw_instruction = std.mem.readInt(u16, emulator.memory[emulator.pc..][0..2], .big);
    const inst: Inst = switch ((raw_instruction >> 8) & 0xf) {
        0xf => .{ .func = &foo, .data = .{ .foo = @truncate(raw_instruction) } },
        0xb => .{ .func = &bar, .data = .{ .bar = @truncate(raw_instruction) } },
        else => .{ .func = &invalid, .data = undefined },
    };
    @call(.always_tail, inst.func, .{ emulator, inst.data });
}

pub fn main() void {
    var emulator = Emulator{
        .memory = undefined,
        .code = undefined,
    };
    @memset(&emulator.code, .{ .func = &decode, .data = undefined });
    const rom = [_]u8{ 0x0f, 0x23, 0x0b, 0x80 };
    @memcpy(emulator.memory[0..rom.len], &rom);
    emulator.code[emulator.pc].func(&emulator, emulator.code[emulator.pc].data);
}

smaller example

#

inside decode, inst.data has tag .foo, but when foo is called the tag is 0x4c

keen anchor
#

always_tail is unfortunately quite buggy in general