From a6fd0c802359b08a93ff6804265ec4a84645909f Mon Sep 17 00:00:00 2001 From: veclav talica Date: Wed, 20 Sep 2023 12:26:42 +0500 Subject: [PATCH] fix of stack in sysv call, test coverage for integers in registers --- src/arch/x86-64/jedino-jedro.zig | 7 +- src/arch/x86-64/ve-sistema.zig | 106 +++++++++++++++++++++++++++++-- src/main.zig | 5 +- 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/src/arch/x86-64/jedino-jedro.zig b/src/arch/x86-64/jedino-jedro.zig index d23f7a6..43a3a5b 100644 --- a/src/arch/x86-64/jedino-jedro.zig +++ b/src/arch/x86-64/jedino-jedro.zig @@ -131,10 +131,13 @@ pub fn execute(binary: []const Word, entry_addr: usize) void { const jumpstartSysV = @as(*const fn (thread: *const Word, return_stack: *Word) callconv(.SysV) void, @ptrCast(&jumpstartNakedSysV)); +// todo: Make sure to save every non-volatile register and restore it. +// todo: Should we make frames well formed for walking? fn jumpstartNakedSysV() callconv(.Naked) void { asm volatile ( \\ pushq %%rbp - \\ movq %%rsp, %%rbp + \\ pushq %%r12 + \\ pushq %%r13 \\ \\ movq %%rdi, %%r12 \\ movq %%rsi, %%r13 @@ -146,6 +149,8 @@ fn jumpstartNakedSysV() callconv(.Naked) void { \\ jmpq *(%%r12) \\ 0: \\ + \\ popq %%r12 + \\ popq %%r13 \\ popq %%rbp \\ ret ); diff --git a/src/arch/x86-64/ve-sistema.zig b/src/arch/x86-64/ve-sistema.zig index e59dee4..280024b 100644 --- a/src/arch/x86-64/ve-sistema.zig +++ b/src/arch/x86-64/ve-sistema.zig @@ -6,6 +6,7 @@ // https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf const std = @import("std"); +const tolmac = @import("../../tolmac.zig"); /// Used for stack parameter passing. pub const WordLimit = 128; @@ -31,7 +32,7 @@ fn determiteClass(comptime T: type, buffer: []Class) []Class { 0 => buffer[0] = .void, 1...64 => buffer[0] = .integer, 65...128 => @compileError("unimplemented"), - else => @compileError("unimplemented"), + else => @compileError("invalid sysv parameter"), } }, .Float => |float| { @@ -40,16 +41,13 @@ fn determiteClass(comptime T: type, buffer: []Class) []Class { 1...64 => buffer[0] = .sse, 65...80 => @compileError("unimplemented"), 81...128 => @compileError("unimplemented"), - else => @compileError("unimplemented"), + else => @compileError("invalid sysv parameter"), } }, .Bool => buffer[0] = .integer, .Pointer => |ptr| { switch (ptr.size) { - .Slice => { - buffer[0] = .integer; - buffer[1] = .integer; - }, + .Slice => @compileError("invalid sysv parameter"), else => buffer[0] = .integer, } }, @@ -69,9 +67,18 @@ pub fn generateOpZovSysvFromPrototype(prototype: anytype) !*const fn () callconv // > The direction flag DF in the %rFLAGS register must be clear (set to “forward” // > direction) on function entry and return. + // todo: Is our bool convention compatible? + // > Booleans, when stored in a memory object, are stored as single byte objects the + // > value of which is always 0 (false) or 1 (true). When stored in integer registers + // > (except for passing as arguments), all 8 bytes of the register are significant; any + // > nonzero value is considered true. + comptime { const func = @typeInfo(@TypeOf(prototype)).Fn; + if (func.calling_convention != .SysV) + @compileError("Non SysV function passed"); + var source_buffer = [_]u8{0} ** AsmBufferLimit; var source_needle: usize = 0; @@ -79,11 +86,15 @@ pub fn generateOpZovSysvFromPrototype(prototype: anytype) !*const fn () callconv // todo: In-stack returns by pointing %rdi directly to final destination. + // todo: Handle cases with more than 32 eightbytes on stack. + // For that we would need to increment %rbp by 256 every once in a while, + // either by storing its copy in volatile register or subtracting back after copy it done. + // todo: Test whether aligning by shifting is better. const Prelude = \\ movq %%rsp, %%rbp # Move stack pointer in non-volatile %rbp to restore later \\ andq $-16, %%rsp # Align stack so that %rsp + 8 in callee is 16 aligned. - \\ addq $0x{x}, %%rsp + \\ subq $0x{x}, %%rsp \\ ; @@ -160,3 +171,84 @@ pub fn generateOpZovSysvFromPrototype(prototype: anytype) !*const fn () callconv }.op; } } + +fn addInt1(a: u64) callconv(.SysV) void { + @setAlignStack(16); + if (a != 1) + @panic("addInt1"); +} + +fn addInt2(a: u64, b: u8) callconv(.SysV) void { + @setAlignStack(16); + if ((a + b) != 3) + @panic("addInt2"); +} + +fn addInt3(a: u64, b: u32, c: u16) callconv(.SysV) void { + @setAlignStack(16); + if ((a + b + c) != 6) + @panic("addInt3"); +} + +fn addInt4(a: u64, b: u32, c: u16, d: u8) callconv(.SysV) void { + @setAlignStack(16); + if ((a + b + c + d) != 10) + @panic("addInt4"); +} + +fn addInt5(a: u64, b: u32, c: u16, d: u8, e: u64) callconv(.SysV) void { + @setAlignStack(16); + if ((a + b + c + d + e) != 11) + @panic("addInt5"); +} + +fn addInt6(a: u64, b: u32, c: u16, d: u8, e: u64, f: i32) callconv(.SysV) void { + @setAlignStack(16); + if ((a + b + c + d + e + @as(u64, @intCast(f))) != 15) + @panic("addInt6"); +} + +const opZov1Int = generateOpZovSysvFromPrototype(addInt1) catch unreachable; +const opZov2Ints = generateOpZovSysvFromPrototype(addInt2) catch unreachable; +const opZov3Ints = generateOpZovSysvFromPrototype(addInt3) catch unreachable; +const opZov4Ints = generateOpZovSysvFromPrototype(addInt4) catch unreachable; +const opZov5Ints = generateOpZovSysvFromPrototype(addInt5) catch unreachable; +const opZov6Ints = generateOpZovSysvFromPrototype(addInt6) catch unreachable; + +test "integer register passing" { + const code = [_]tolmac.Word{ + @as(tolmac.Word, @intFromPtr(&tolmac.opPushWord)), + 1, + @as(tolmac.Word, @intFromPtr(opZov1Int)), + @as(tolmac.Word, @intFromPtr(&addInt1)), + @as(tolmac.Word, @intFromPtr(&tolmac.opPushWord)), + 2, + @as(tolmac.Word, @intFromPtr(opZov2Ints)), + @as(tolmac.Word, @intFromPtr(&addInt2)), + @as(tolmac.Word, @intFromPtr(&tolmac.opPushWord)), + 3, + @as(tolmac.Word, @intFromPtr(opZov3Ints)), + @as(tolmac.Word, @intFromPtr(&addInt3)), + @as(tolmac.Word, @intFromPtr(&tolmac.opPushWord)), + 4, + @as(tolmac.Word, @intFromPtr(opZov4Ints)), + @as(tolmac.Word, @intFromPtr(&addInt4)), + @as(tolmac.Word, @intFromPtr(&tolmac.opPushWord)), + 1, + @as(tolmac.Word, @intFromPtr(opZov5Ints)), + @as(tolmac.Word, @intFromPtr(&addInt5)), + @as(tolmac.Word, @intFromPtr(&tolmac.opPushWord)), + 4, + @as(tolmac.Word, @intFromPtr(opZov6Ints)), + @as(tolmac.Word, @intFromPtr(&addInt6)), + @as(tolmac.Word, @intFromPtr(&tolmac.opSinkWord)), + @as(tolmac.Word, @intFromPtr(&tolmac.opSinkWord)), + @as(tolmac.Word, @intFromPtr(&tolmac.opSinkWord)), + @as(tolmac.Word, @intFromPtr(&tolmac.opSinkWord)), + @as(tolmac.Word, @intFromPtr(&tolmac.opSinkWord)), + @as(tolmac.Word, @intFromPtr(&tolmac.opSinkWord)), + @as(tolmac.Word, @intFromPtr(&tolmac.opReturn)), + }; + + tolmac.execute(&code, 0); +} diff --git a/src/main.zig b/src/main.zig index 2d0c1a1..2443fee 100644 --- a/src/main.zig +++ b/src/main.zig @@ -15,7 +15,6 @@ const opPrintInt3Zov = tolmac.generateOpZovSysvFromPrototype(printInt3) catch un const opPrintInt2Zov = tolmac.generateOpZovSysvFromPrototype(printInt2) catch unreachable; pub fn main() !void { - // todo: Mixing return addresses in stack poses a challenge, hm. const add = [_]tolmac.Word{ @as(tolmac.Word, @intFromPtr(&tolmac.opSumWordsWithOverflow)), @as(tolmac.Word, @intFromPtr(&tolmac.opReturn)), @@ -45,3 +44,7 @@ pub fn main() !void { tolmac.execute(&entry, 0); } + +test { + _ = @import("tolmac.zig"); +}