| ... | ... | @@ -4204,7 +4204,6 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 4204 | 4204 | }, |
| 4205 | 4205 | .stack_offset => { |
| 4206 | 4206 | const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv); |
| 4207 | | log.warn("REG = {}", .{reg}); |
| 4208 | 4207 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); |
| 4209 | 4208 | defer self.register_manager.unlockReg(reg_lock); |
| 4210 | 4209 | |
| ... | ... | @@ -7129,11 +7128,12 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 7129 | 7128 | result.stack_align = 1; |
| 7130 | 7129 | return result; |
| 7131 | 7130 | }, |
| 7132 | | .Unspecified, .C => { |
| 7131 | .C => { |
| 7133 | 7132 | // Return values |
| 7134 | 7133 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 7135 | 7134 | result.return_value = .{ .unreach = {} }; |
| 7136 | 7135 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 7136 | // TODO: is this even possible for C calling convention? |
| 7137 | 7137 | result.return_value = .{ .none = {} }; |
| 7138 | 7138 | } else { |
| 7139 | 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 | 7144 | const aliased_reg = registerAlias(abi.getCAbiIntReturnRegs(self.target.*)[0], ret_ty_size); |
| 7145 | 7145 | result.return_value = .{ .register = aliased_reg }; |
| 7146 | 7146 | } 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 |
| 7150 | 7148 | result.return_value = .{ .stack_offset = 0 }; |
| 7151 | 7149 | } |
| 7152 | 7150 | } |
| 7153 | 7151 | |
| 7154 | 7152 | // 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, |
| 7173 | 7156 | }; |
| 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 }; |
| 7195 | 7217 | } |
| 7196 | 7218 | } |
| 7197 | 7219 | |
| 7220 | // Input params |
| 7198 | 7221 | var next_stack_offset: u32 = switch (result.return_value) { |
| 7199 | 7222 | .stack_offset => |off| @intCast(u32, off), |
| 7200 | 7223 | else => 0, |
| 7201 | 7224 | }; |
| 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| { |
| 7206 | 7227 | if (!ty.hasRuntimeBits()) { |
| 7207 | | assert(cc != .C); |
| 7208 | 7228 | result.args[i] = .{ .none = {} }; |
| 7209 | 7229 | continue; |
| 7210 | 7230 | } |
| 7211 | 7231 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 7212 | 7232 | 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; |
| 7222 | 7236 | } |
| 7223 | 7237 | |
| 7224 | 7238 | result.stack_align = 16; |