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(
348348 in_out_lpOverlapped: ?*OVERLAPPED,
349349) 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
353359pub 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 {
42044204 },
42054205 .stack_offset => {
42064206 const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
4207 log.warn("REG = {}", .{reg});
42084207 const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
42094208 defer self.register_manager.unlockReg(reg_lock);
42104209
......@@ -7129,11 +7128,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
71297128 result.stack_align = 1;
71307129 return result;
71317130 },
7132 .Unspecified, .C => {
7131 .C => {
71337132 // Return values
71347133 if (ret_ty.zigTypeTag() == .NoReturn) {
71357134 result.return_value = .{ .unreach = {} };
71367135 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) {
7136 // TODO: is this even possible for C calling convention?
71377137 result.return_value = .{ .none = {} };
71387138 } else {
71397139 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
......@@ -7144,81 +7144,95 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
71447144 const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size);
71457145 result.return_value = .{ .register = aliased_reg };
71467146 } else {
7147 // We simply make the return MCValue a stack offset. However, the actual value
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.
7147 // TODO: return argument cell should go first
71507148 result.return_value = .{ .stack_offset = 0 };
71517149 }
71527150 }
71537151
71547152 // Input params
7155 // First, split into args that can be passed via registers.
7156 // This will make it easier to then push the rest of args in reverse
7157 // order on the stack.
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 }
7153 var next_stack_offset: u32 = switch (result.return_value) {
7154 .stack_offset => |off| @intCast(u32, off),
7155 else => 0,
71737156 };
7174 if (!omit_args_in_registers) {
7175 for (param_types) |ty, i| {
7176 if (!ty.hasRuntimeBits()) continue;
7177 const param_size = @intCast(u32, ty.abiSize(self.target.*));
7178 // For simplicity of codegen, slices and other types are always pushed onto the stack.
7179 // TODO: look into optimizing this by passing things as registers sometimes,
7180 // such as ptr and len of slices as separate registers.
7181 // TODO: also we need to honor the C ABI for relevant types rather than passing on
7182 // the stack here.
7183 const pass_in_reg = switch (ty.zigTypeTag()) {
7184 .Bool => true,
7185 .Int, .Enum => param_size <= 8,
7186 .Pointer => ty.ptrSize() != .Slice,
7187 .Optional => ty.isPtrLikeOptional(),
7188 else => false,
7189 };
7190 if (pass_in_reg) {
7191 if (next_int_reg >= abi.getCAbiIntParamRegs(self.target.*).len) break;
7192 try by_reg.putNoClobber(i, next_int_reg);
7193 next_int_reg += 1;
7194 }
7157
7158 for (param_types) |ty, i| {
7159 assert(ty.hasRuntimeBits());
7160
7161 if (self.target.os.tag != .windows) {
7162 return self.fail("TODO SysV calling convention", .{});
7163 }
7164
7165 switch (abi.classifyWindows(ty, self.target.*)) {
7166 .integer => blk: {
7167 if (i >= abi.getCAbiIntParamRegs(self.target.*).len) break :blk; // fallthrough
7168 result.args[i] = .{ .register = abi.getCAbiIntParamRegs(self.target.*)[i] };
7169 continue;
7170 },
7171 .sse => return self.fail("TODO float/vector via SSE on Windows", .{}),
7172 .memory => {}, // fallthrough
7173 else => unreachable,
7174 }
7175
7176 const param_size = @intCast(u32, ty.abiSize(self.target.*));
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 };
71957217 }
71967218 }
71977219
7220 // Input params
71987221 var next_stack_offset: u32 = switch (result.return_value) {
71997222 .stack_offset => |off| @intCast(u32, off),
72007223 else => 0,
72017224 };
7202 var count: usize = param_types.len;
7203 while (count > 0) : (count -= 1) {
7204 const i = count - 1;
7205 const ty = param_types[i];
7225
7226 for (param_types) |ty, i| {
72067227 if (!ty.hasRuntimeBits()) {
7207 assert(cc != .C);
72087228 result.args[i] = .{ .none = {} };
72097229 continue;
72107230 }
72117231 const param_size = @intCast(u32, ty.abiSize(self.target.*));
72127232 const param_align = @intCast(u32, ty.abiAlignment(self.target.*));
7213 if (by_reg.get(i)) |int_reg| {
7214 const aliased_reg = registerAlias(abi.getCAbiIntParamRegs(self.target.*)[int_reg], param_size);
7215 result.args[i] = .{ .register = aliased_reg };
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 }
7233 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
7234 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
7235 next_stack_offset = offset;
72227236 }
72237237
72247238 result.stack_align = 16;