mirror of
https://github.com/ziglang/zig.git
synced 2025-12-15 02:33:07 +00:00
x86_64: implement Windows x64 calling convention
This commit is contained in:
parent
38573fed0b
commit
3a4c69c018
@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile(
|
|||||||
in_out_lpOverlapped: ?*OVERLAPPED,
|
in_out_lpOverlapped: ?*OVERLAPPED,
|
||||||
) callconv(WINAPI) BOOL;
|
) callconv(WINAPI) BOOL;
|
||||||
|
|
||||||
pub extern "kernel32" fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: *OVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) callconv(WINAPI) BOOL;
|
pub extern "kernel32" fn WriteFileEx(
|
||||||
|
hFile: HANDLE,
|
||||||
|
lpBuffer: [*]const u8,
|
||||||
|
nNumberOfBytesToWrite: DWORD,
|
||||||
|
lpOverlapped: *OVERLAPPED,
|
||||||
|
lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
|
||||||
|
) callconv(WINAPI) BOOL;
|
||||||
|
|
||||||
pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE;
|
pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE;
|
||||||
|
|
||||||
|
|||||||
@ -4204,7 +4204,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
|
|||||||
},
|
},
|
||||||
.stack_offset => {
|
.stack_offset => {
|
||||||
const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
|
const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
|
||||||
log.warn("REG = {}", .{reg});
|
|
||||||
const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
|
const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
|
||||||
defer self.register_manager.unlockReg(reg_lock);
|
defer self.register_manager.unlockReg(reg_lock);
|
||||||
|
|
||||||
@ -7129,7 +7128,75 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
|
|||||||
result.stack_align = 1;
|
result.stack_align = 1;
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
.Unspecified, .C => {
|
.C => {
|
||||||
|
// Return values
|
||||||
|
if (ret_ty.zigTypeTag() == .NoReturn) {
|
||||||
|
result.return_value = .{ .unreach = {} };
|
||||||
|
} else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
|
||||||
|
// TODO: is this even possible for C calling convention?
|
||||||
|
result.return_value = .{ .none = {} };
|
||||||
|
} else {
|
||||||
|
const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
|
||||||
|
if (ret_ty_size == 0) {
|
||||||
|
assert(ret_ty.isError());
|
||||||
|
result.return_value = .{ .immediate = 0 };
|
||||||
|
} else if (ret_ty_size <= 8) {
|
||||||
|
const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
|
||||||
|
result.return_value = .{ .register = aliased_reg };
|
||||||
|
} else {
|
||||||
|
// TODO: return argument cell should go first
|
||||||
|
result.return_value = .{ .stack_offset = 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input params
|
||||||
|
var next_stack_offset: u32 = switch (result.return_value) {
|
||||||
|
.stack_offset => |off| @intCast(u32, off),
|
||||||
|
else => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (param_types) |ty, i| {
|
||||||
|
assert(ty.hasRuntimeBits());
|
||||||
|
|
||||||
|
if (self.target.os.tag != .windows) {
|
||||||
|
return self.fail("TODO SysV calling convention", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (abi.classifyWindows(ty, self.target.*)) {
|
||||||
|
.integer => blk: {
|
||||||
|
if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough
|
||||||
|
result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] };
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
.sse => return self.fail("TODO float/vector via SSE on Windows", .{}),
|
||||||
|
.memory => {}, // fallthrough
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
|
||||||
|
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
||||||
|
const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
|
||||||
|
const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
|
||||||
|
result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
|
||||||
|
next_stack_offset = offset;
|
||||||
|
}
|
||||||
|
// Align the stack to 16bytes before allocating shadow stack space.
|
||||||
|
const aligned_next_stack_offset = mem.alignForwardGeneric(u32, next_stack_offset, 16);
|
||||||
|
const padding = aligned_next_stack_offset - next_stack_offset;
|
||||||
|
if (padding > 0) {
|
||||||
|
for (result.args) |*arg| {
|
||||||
|
if (arg.isRegister()) continue;
|
||||||
|
arg.stack_offset += @intCast(i32, padding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO fix this so that the 16byte alignment padding is at the current value of $rsp, and push
|
||||||
|
// the args onto the stack so that there is no padding between the first argument and
|
||||||
|
// the standard preamble.
|
||||||
|
// alignment padding | ret value (if > 8) | args ... | shadow stack space | $rbp |
|
||||||
|
result.stack_byte_count = aligned_next_stack_offset + 4 * @sizeOf(u64);
|
||||||
|
result.stack_align = 16;
|
||||||
|
},
|
||||||
|
.Unspecified => {
|
||||||
// Return values
|
// Return values
|
||||||
if (ret_ty.zigTypeTag() == .NoReturn) {
|
if (ret_ty.zigTypeTag() == .NoReturn) {
|
||||||
result.return_value = .{ .unreach = {} };
|
result.return_value = .{ .unreach = {} };
|
||||||
@ -7141,8 +7208,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
|
|||||||
assert(ret_ty.isError());
|
assert(ret_ty.isError());
|
||||||
result.return_value = .{ .immediate = 0 };
|
result.return_value = .{ .immediate = 0 };
|
||||||
} else if (ret_ty_size <= 8) {
|
} else if (ret_ty_size <= 8) {
|
||||||
const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
|
result.return_value = .{ .register = .rdi };
|
||||||
result.return_value = .{ .register = aliased_reg };
|
|
||||||
} else {
|
} else {
|
||||||
// We simply make the return MCValue a stack offset. However, the actual value
|
// We simply make the return MCValue a stack offset. However, the actual value
|
||||||
// for the offset will be populated later. We will also push the stack offset
|
// for the offset will be populated later. We will also push the stack offset
|
||||||
@ -7152,74 +7218,22 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Input params
|
// Input params
|
||||||
// First, split into args that can be passed via registers.
|
|
||||||
// This will make it easier to then push the rest of args in reverse
|
|
||||||
// order on the stack.
|
|
||||||
var next_int_reg: usize = 0;
|
|
||||||
var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
|
|
||||||
defer by_reg.deinit();
|
|
||||||
|
|
||||||
// If we want debug output, we store all args on stack for better liveness of args
|
|
||||||
// in debugging contexts such as previewing the args in the debugger anywhere in
|
|
||||||
// the procedure. Passing the args via registers can lead to reusing the register
|
|
||||||
// for local ops thus clobbering the input arg forever.
|
|
||||||
// This of course excludes C ABI calls.
|
|
||||||
const omit_args_in_registers = blk: {
|
|
||||||
if (cc == .C) break :blk false;
|
|
||||||
switch (self.bin_file.options.optimize_mode) {
|
|
||||||
.Debug => break :blk true,
|
|
||||||
else => break :blk false,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (!omit_args_in_registers) {
|
|
||||||
for (param_types) |ty, i| {
|
|
||||||
if (!ty.hasRuntimeBits()) continue;
|
|
||||||
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
|
||||||
// For simplicity of codegen, slices and other types are always pushed onto the stack.
|
|
||||||
// TODO: look into optimizing this by passing things as registers sometimes,
|
|
||||||
// such as ptr and len of slices as separate registers.
|
|
||||||
// TODO: also we need to honor the C ABI for relevant types rather than passing on
|
|
||||||
// the stack here.
|
|
||||||
const pass_in_reg = switch (ty.zigTypeTag()) {
|
|
||||||
.Bool => true,
|
|
||||||
.Int, .Enum => param_size <= 8,
|
|
||||||
.Pointer => ty.ptrSize() != .Slice,
|
|
||||||
.Optional => ty.isPtrLikeOptional(),
|
|
||||||
else => false,
|
|
||||||
};
|
|
||||||
if (pass_in_reg) {
|
|
||||||
if (next_int_reg >= abi.getCAbiIntParamRegs(self.target.*).len) break;
|
|
||||||
try by_reg.putNoClobber(i, next_int_reg);
|
|
||||||
next_int_reg += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var next_stack_offset: u32 = switch (result.return_value) {
|
var next_stack_offset: u32 = switch (result.return_value) {
|
||||||
.stack_offset => |off| @intCast(u32, off),
|
.stack_offset => |off| @intCast(u32, off),
|
||||||
else => 0,
|
else => 0,
|
||||||
};
|
};
|
||||||
var count: usize = param_types.len;
|
|
||||||
while (count > 0) : (count -= 1) {
|
for (param_types) |ty, i| {
|
||||||
const i = count - 1;
|
|
||||||
const ty = param_types[i];
|
|
||||||
if (!ty.hasRuntimeBits()) {
|
if (!ty.hasRuntimeBits()) {
|
||||||
assert(cc != .C);
|
|
||||||
result.args[i] = .{ .none = {} };
|
result.args[i] = .{ .none = {} };
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
const param_size = @intCast(u32, ty.abiSize(self.target.*));
|
||||||
const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
|
const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
|
||||||
if (by_reg.get(i)) |int_reg| {
|
|
||||||
const aliased_reg = registerAlias(abi.getCAbiIntParamRegs(self.target.*)[int_reg], param_size);
|
|
||||||
result.args[i] = .{ .register = aliased_reg };
|
|
||||||
next_int_reg += 1;
|
|
||||||
} else {
|
|
||||||
const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
|
const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
|
||||||
result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
|
result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
|
||||||
next_stack_offset = offset;
|
next_stack_offset = offset;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
result.stack_align = 16;
|
result.stack_align = 16;
|
||||||
// TODO fix this so that the 16byte alignment padding is at the current value of $rsp, and push
|
// TODO fix this so that the 16byte alignment padding is at the current value of $rsp, and push
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user