authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-01 19:04:08+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-07 22:42:56+02:00
log3a4c69c01824fb6f72e90433a5683a8df09ad4c1
tree575fac6bb92c31963a239a0ead74f533fc026a31
parent38573fed0ba796f642e7b4eebdf3f9eafc572f25

x86_64: implement Windows x64 calling convention


2 files changed, 79 insertions(+), 59 deletions(-)

lib/std/os/windows/kernel32.zig+7-1
...@@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile(...@@ -348,7 +348,13 @@ pub extern "kernel32" fn WriteFile(
348 in_out_lpOverlapped: ?*OVERLAPPED,348 in_out_lpOverlapped: ?*OVERLAPPED,
349) callconv(WINAPI) BOOL;349) callconv(WINAPI) BOOL;
350350
351pub extern "kernel32" fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: *OVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) callconv(WINAPI) BOOL;351pub extern "kernel32" fn WriteFileEx(
352 hFile: HANDLE,
353 lpBuffer: [*]const u8,
354 nNumberOfBytesToWrite: DWORD,
355 lpOverlapped: *OVERLAPPED,
356 lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
357) callconv(WINAPI) BOOL;
352358
353pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE;359pub extern "kernel32" fn LoadLibraryW(lpLibFileName: [*:0]const u16) callconv(WINAPI) ?HMODULE;
354360
src/arch/x86_64/CodeGen.zig+72-58
...@@ -4204,7 +4204,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {...@@ -4204,7 +4204,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
4204 },4204 },
4205 .stack_offset => {4205 .stack_offset => {
4206 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);4206 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
4207 log.warn("REG = {}", .{reg});
4208 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);4207 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
4209 defer self.register_manager.unlockReg(reg_lock);4208 defer self.register_manager.unlockReg(reg_lock);
42104209
...@@ -7129,11 +7128,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7129,11 +7128,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7129 result.stack_align = 1;7128 result.stack_align = 1;
7130 return result;7129 return result;
7131 },7130 },
7132 .Unspecified, .C => {7131 .C => {
7133 // Return values7132 // Return values
7134 if (ret_ty.zigTypeTag() == .NoReturn) {7133 if (ret_ty.zigTypeTag() == .NoReturn) {
7135 result.return_value = .{ .unreach = {} };7134 result.return_value = .{ .unreach = {} };
7136 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {7135 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7136 // TODO: is this even possible for C calling convention?
7137 result.return_value = .{ .none = {} };7137 result.return_value = .{ .none = {} };
7138 } else {7138 } else {
7139 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));7139 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
...@@ -7144,81 +7144,95 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -7144,81 +7144,95 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
7144 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);7144 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
7145 result.return_value = .{ .register = aliased_reg };7145 result.return_value = .{ .register = aliased_reg };
7146 } else {7146 } else {
7147 // We simply make the return MCValue a stack offset. However, the actual value7147 // TODO: return argument cell should go first
7148 // for the offset will be populated later. We will also push the stack offset
7149 // value into .rdi register when we resolve the offset.
7150 result.return_value = .{ .stack_offset = 0 };7148 result.return_value = .{ .stack_offset = 0 };
7151 }7149 }
7152 }7150 }
71537151
7154 // Input params7152 // Input params
7155 // First, split into args that can be passed via registers.7153 var next_stack_offset: u32 = switch (result.return_value) {
7156 // This will make it easier to then push the rest of args in reverse7154 .stack_offset => |off| @intCast(u32, off),
7157 // order on the stack.7155 else => 0,
7158 var next_int_reg: usize = 0;
7159 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
7160 defer by_reg.deinit();
7161
7162 // If we want debug output, we store all args on stack for better liveness of args
7163 // in debugging contexts such as previewing the args in the debugger anywhere in
7164 // the procedure. Passing the args via registers can lead to reusing the register
7165 // for local ops thus clobbering the input arg forever.
7166 // This of course excludes C ABI calls.
7167 const omit_args_in_registers = blk: {
7168 if (cc == .C) break :blk false;
7169 switch (self.bin_file.options.optimize_mode) {
7170 .Debug => break :blk true,
7171 else => break :blk false,
7172 }
7173 };7156 };
7174 if (!omit_args_in_registers) {7157
7175 for (param_types) |ty, i| {7158 for (param_types) |ty, i| {
7176 if (!ty.hasRuntimeBits()) continue;7159 assert(ty.hasRuntimeBits());
7177 const param_size = @intCast(u32, ty.abiSize(self.target.*));7160
7178 // For simplicity of codegen, slices and other types are always pushed onto the stack.7161 if (self.target.os.tag != .windows) {
7179 // TODO: look into optimizing this by passing things as registers sometimes,7162 return self.fail("TODO SysV calling convention", .{});
7180 // such as ptr and len of slices as separate registers.7163 }
7181 // TODO: also we need to honor the C ABI for relevant types rather than passing on7164
7182 // the stack here.7165 switch (abi.classifyWindows(ty, self.target.*)) {
7183 const pass_in_reg = switch (ty.zigTypeTag()) {7166 .integer => blk: {
7184 .Bool => true,7167 if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough
7185 .Int, .Enum => param_size <= 8,7168 result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] };
7186 .Pointer => ty.ptrSize() != .Slice,7169 continue;
7187 .Optional => ty.isPtrLikeOptional(),7170 },
7188 else => false,7171 .sse => return self.fail("TODO float/vector via SSE on Windows", .{}),
7189 };7172 .memory => {}, // fallthrough
7190 if (pass_in_reg) {7173 else => unreachable,
7191 if (next_int_reg >= abi.getCAbiIntParamRegs(self.target.*).len) break;7174 }
7192 try by_reg.putNoClobber(i, next_int_reg);7175
7193 next_int_reg += 1;7176 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7194 }7177 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7178 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7179 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7180 next_stack_offset = offset;
7181 }
7182 // Align the stack to 16bytes before allocating shadow stack space.
7183 const aligned_next_stack_offset = mem.alignForwardGeneric(u32, next_stack_offset, 16);
7184 const padding = aligned_next_stack_offset - next_stack_offset;
7185 if (padding > 0) {
7186 for (result.args) |*arg| {
7187 if (arg.isRegister()) continue;
7188 arg.stack_offset += @intCast(i32, padding);
7189 }
7190 }
7191
7192 // TODO fix this so that the 16byte alignment padding is at the current value of $rsp, and push
7193 // the args onto the stack so that there is no padding between the first argument and
7194 // the standard preamble.
7195 // alignment padding | ret value (if > 8) | args ... | shadow stack space | $rbp |
7196 result.stack_byte_count = aligned_next_stack_offset + 4 * @sizeOf(u64);
7197 result.stack_align = 16;
7198 },
7199 .Unspecified => {
7200 // Return values
7201 if (ret_ty.zigTypeTag() == .NoReturn) {
7202 result.return_value = .{ .unreach = {} };
7203 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7204 result.return_value = .{ .none = {} };
7205 } else {
7206 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
7207 if (ret_ty_size == 0) {
7208 assert(ret_ty.isError());
7209 result.return_value = .{ .immediate = 0 };
7210 } else if (ret_ty_size <= 8) {
7211 result.return_value = .{ .register = .rdi };
7212 } else {
7213 // We simply make the return MCValue a stack offset. However, the actual value
7214 // for the offset will be populated later. We will also push the stack offset
7215 // value into .rdi register when we resolve the offset.
7216 result.return_value = .{ .stack_offset = 0 };
7195 }7217 }
7196 }7218 }
71977219
7220 // Input params
7198 var next_stack_offset: u32 = switch (result.return_value) {7221 var next_stack_offset: u32 = switch (result.return_value) {
7199 .stack_offset => |off| @intCast(u32, off),7222 .stack_offset => |off| @intCast(u32, off),
7200 else => 0,7223 else => 0,
7201 };7224 };
7202 var count: usize = param_types.len;7225
7203 while (count > 0) : (count -= 1) {7226 for (param_types) |ty, i| {
7204 const i = count - 1;
7205 const ty = param_types[i];
7206 if (!ty.hasRuntimeBits()) {7227 if (!ty.hasRuntimeBits()) {
7207 assert(cc != .C);
7208 result.args[i] = .{ .none = {} };7228 result.args[i] = .{ .none = {} };
7209 continue;7229 continue;
7210 }7230 }
7211 const param_size = @intCast(u32, ty.abiSize(self.target.*));7231 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7212 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));7232 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7213 if (by_reg.get(i)) |int_reg| {7233 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7214 const aliased_reg = registerAlias(abi.getCAbiIntParamRegs(self.target.*)[int_reg], param_size);7234 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7215 result.args[i] = .{ .register = aliased_reg };7235 next_stack_offset = offset;
7216 next_int_reg += 1;
7217 } else {
7218 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7219 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7220 next_stack_offset = offset;
7221 }
7222 }7236 }
72237237
7224 result.stack_align = 16;7238 result.stack_align = 16;