| ... | @@ -3112,6 +3112,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3112,6 +3112,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 3112 | ); | 3112 | ); |
| 3113 | } | 3113 | } |
| 3114 | }, | 3114 | }, |
| | 3115 | .stack_offset => { |
| | 3116 | // TODO what a waste... |
| | 3117 | const ret_ty = fn_ty.fnReturnType(); |
| | 3118 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| | 3119 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); |
| | 3120 | const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align)); |
| | 3121 | try self.genInlineMemcpyTODO(stack_offset, .rbp, ret_ty, info.return_value); |
| | 3122 | break :result MCValue{ .stack_offset = stack_offset }; |
| | 3123 | }, |
| 3115 | else => {}, | 3124 | else => {}, |
| 3116 | } | 3125 | } |
| 3117 | break :result info.return_value; | 3126 | break :result info.return_value; |
| ... | @@ -3133,7 +3142,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3133,7 +3142,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 3133 | | 3142 | |
| 3134 | fn ret(self: *Self, mcv: MCValue) !void { | 3143 | fn ret(self: *Self, mcv: MCValue) !void { |
| 3135 | const ret_ty = self.fn_type.fnReturnType(); | 3144 | const ret_ty = self.fn_type.fnReturnType(); |
| 3136 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); | 3145 | const ret_mcv = blk: { |
| | 3146 | switch (self.ret_mcv) { |
| | 3147 | .stack_offset => |off| { |
| | 3148 | // Adjust the stack offset |
| | 3149 | const offset = @intCast(i32, self.max_end_stack) - off + 16; |
| | 3150 | break :blk MCValue{ .stack_offset = -offset }; |
| | 3151 | }, |
| | 3152 | else => break :blk self.ret_mcv, |
| | 3153 | } |
| | 3154 | }; |
| | 3155 | try self.setRegOrMem(ret_ty, ret_mcv, mcv); |
| 3137 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. | 3156 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. |
| 3138 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction | 3157 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction |
| 3139 | // which is available if the jump is 127 bytes or less forward. | 3158 | // which is available if the jump is 127 bytes or less forward. |
| ... | @@ -4131,6 +4150,155 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro | ... | @@ -4131,6 +4150,155 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro |
| 4131 | } | 4150 | } |
| 4132 | } | 4151 | } |
| 4133 | | 4152 | |
| | 4153 | fn genInlineMemcpyTODO(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type, val: MCValue) InnerError!void { |
| | 4154 | const abi_size = ty.abiSize(self.target.*); |
| | 4155 | |
| | 4156 | try self.register_manager.getReg(.rax, null); |
| | 4157 | try self.register_manager.getReg(.rcx, null); |
| | 4158 | |
| | 4159 | self.register_manager.freezeRegs(&.{ .rax, .rcx, .rbp }); |
| | 4160 | defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx, .rbp }); |
| | 4161 | |
| | 4162 | const addr_reg: Register = blk: { |
| | 4163 | switch (val) { |
| | 4164 | .memory, |
| | 4165 | .direct_load, |
| | 4166 | .got_load, |
| | 4167 | => { |
| | 4168 | break :blk try self.loadMemPtrIntoRegister(Type.usize, val); |
| | 4169 | }, |
| | 4170 | .stack_offset => |off| { |
| | 4171 | const addr_reg = (try self.register_manager.allocReg(null)).to64(); |
| | 4172 | _ = try self.addInst(.{ |
| | 4173 | .tag = .lea, |
| | 4174 | .ops = (Mir.Ops{ |
| | 4175 | .reg1 = addr_reg, |
| | 4176 | .reg2 = .rsp, |
| | 4177 | }).encode(), |
| | 4178 | .data = .{ .imm = @bitCast(u32, -off) }, |
| | 4179 | }); |
| | 4180 | break :blk addr_reg; |
| | 4181 | }, |
| | 4182 | .register => |reg| { |
| | 4183 | const addr_reg = try self.register_manager.allocReg(null); |
| | 4184 | _ = try self.addInst(.{ |
| | 4185 | .tag = .mov, |
| | 4186 | .ops = (Mir.Ops{ |
| | 4187 | .reg1 = registerAlias(addr_reg, @divExact(reg.size(), 8)), |
| | 4188 | .reg2 = reg, |
| | 4189 | }).encode(), |
| | 4190 | .data = undefined, |
| | 4191 | }); |
| | 4192 | break :blk addr_reg.to64(); |
| | 4193 | }, |
| | 4194 | else => { |
| | 4195 | return self.fail("TODO implement memcpy for setting stack from {}", .{val}); |
| | 4196 | }, |
| | 4197 | } |
| | 4198 | }; |
| | 4199 | |
| | 4200 | self.register_manager.freezeRegs(&.{addr_reg}); |
| | 4201 | defer self.register_manager.unfreezeRegs(&.{addr_reg}); |
| | 4202 | |
| | 4203 | const regs = try self.register_manager.allocRegs(2, .{ null, null }); |
| | 4204 | const count_reg = regs[0].to64(); |
| | 4205 | const tmp_reg = regs[1].to8(); |
| | 4206 | |
| | 4207 | try self.genSetReg(Type.u32, count_reg, .{ .immediate = @intCast(u32, abi_size) }); |
| | 4208 | |
| | 4209 | // mov rcx, 0 |
| | 4210 | _ = try self.addInst(.{ |
| | 4211 | .tag = .mov, |
| | 4212 | .ops = (Mir.Ops{ |
| | 4213 | .reg1 = .rcx, |
| | 4214 | }).encode(), |
| | 4215 | .data = .{ .imm = 0 }, |
| | 4216 | }); |
| | 4217 | |
| | 4218 | // mov rax, 0 |
| | 4219 | _ = try self.addInst(.{ |
| | 4220 | .tag = .mov, |
| | 4221 | .ops = (Mir.Ops{ |
| | 4222 | .reg1 = .rax, |
| | 4223 | }).encode(), |
| | 4224 | .data = .{ .imm = 0 }, |
| | 4225 | }); |
| | 4226 | |
| | 4227 | // loop: |
| | 4228 | // cmp count, 0 |
| | 4229 | const loop_start = try self.addInst(.{ |
| | 4230 | .tag = .cmp, |
| | 4231 | .ops = (Mir.Ops{ |
| | 4232 | .reg1 = count_reg, |
| | 4233 | }).encode(), |
| | 4234 | .data = .{ .imm = 0 }, |
| | 4235 | }); |
| | 4236 | |
| | 4237 | // je end |
| | 4238 | const loop_reloc = try self.addInst(.{ |
| | 4239 | .tag = .cond_jmp_eq_ne, |
| | 4240 | .ops = (Mir.Ops{ .flags = 0b01 }).encode(), |
| | 4241 | .data = .{ .inst = undefined }, |
| | 4242 | }); |
| | 4243 | |
| | 4244 | // mov tmp, [addr + rcx] |
| | 4245 | _ = try self.addInst(.{ |
| | 4246 | .tag = .mov_scale_src, |
| | 4247 | .ops = (Mir.Ops{ |
| | 4248 | .reg1 = tmp_reg.to8(), |
| | 4249 | .reg2 = addr_reg, |
| | 4250 | }).encode(), |
| | 4251 | .data = .{ .imm = 0 }, |
| | 4252 | }); |
| | 4253 | |
| | 4254 | // mov [stack_offset + rax], tmp |
| | 4255 | _ = try self.addInst(.{ |
| | 4256 | .tag = .mov_scale_dst, |
| | 4257 | .ops = (Mir.Ops{ |
| | 4258 | .reg1 = stack_reg, |
| | 4259 | .reg2 = tmp_reg.to8(), |
| | 4260 | }).encode(), |
| | 4261 | .data = .{ .imm = @bitCast(u32, -stack_offset) }, |
| | 4262 | }); |
| | 4263 | |
| | 4264 | // add rcx, 1 |
| | 4265 | _ = try self.addInst(.{ |
| | 4266 | .tag = .add, |
| | 4267 | .ops = (Mir.Ops{ |
| | 4268 | .reg1 = .rcx, |
| | 4269 | }).encode(), |
| | 4270 | .data = .{ .imm = 1 }, |
| | 4271 | }); |
| | 4272 | |
| | 4273 | // add rax, 1 |
| | 4274 | _ = try self.addInst(.{ |
| | 4275 | .tag = .add, |
| | 4276 | .ops = (Mir.Ops{ |
| | 4277 | .reg1 = .rax, |
| | 4278 | }).encode(), |
| | 4279 | .data = .{ .imm = 1 }, |
| | 4280 | }); |
| | 4281 | |
| | 4282 | // sub count, 1 |
| | 4283 | _ = try self.addInst(.{ |
| | 4284 | .tag = .sub, |
| | 4285 | .ops = (Mir.Ops{ |
| | 4286 | .reg1 = count_reg, |
| | 4287 | }).encode(), |
| | 4288 | .data = .{ .imm = 1 }, |
| | 4289 | }); |
| | 4290 | |
| | 4291 | // jmp loop |
| | 4292 | _ = try self.addInst(.{ |
| | 4293 | .tag = .jmp, |
| | 4294 | .ops = (Mir.Ops{ .flags = 0b00 }).encode(), |
| | 4295 | .data = .{ .inst = loop_start }, |
| | 4296 | }); |
| | 4297 | |
| | 4298 | // end: |
| | 4299 | try self.performReloc(loop_reloc); |
| | 4300 | } |
| | 4301 | |
| 4134 | fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type, val: MCValue) InnerError!void { | 4302 | fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type, val: MCValue) InnerError!void { |
| 4135 | const abi_size = ty.abiSize(self.target.*); | 4303 | const abi_size = ty.abiSize(self.target.*); |
| 4136 | | 4304 | |
| ... | @@ -5061,26 +5229,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -5061,26 +5229,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5061 | | 5229 | |
| 5062 | const ret_ty = fn_ty.fnReturnType(); | 5230 | const ret_ty = fn_ty.fnReturnType(); |
| 5063 | | 5231 | |
| 5064 | // Return values | | |
| 5065 | if (ret_ty.zigTypeTag() == .NoReturn) { | | |
| 5066 | result.return_value = .{ .unreach = {} }; | | |
| 5067 | } else if (!ret_ty.hasRuntimeBits()) { | | |
| 5068 | result.return_value = .{ .none = {} }; | | |
| 5069 | } else switch (cc) { | | |
| 5070 | .Naked => unreachable, | | |
| 5071 | .Unspecified, .C => { | | |
| 5072 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); | | |
| 5073 | if (ret_ty_size <= 8) { | | |
| 5074 | const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size); | | |
| 5075 | result.return_value = .{ .register = aliased_reg }; | | |
| 5076 | } else { | | |
| 5077 | return self.fail("TODO support more return types for x86_64 backend", .{}); | | |
| 5078 | } | | |
| 5079 | }, | | |
| 5080 | else => return self.fail("TODO implement function return values for {}", .{cc}), | | |
| 5081 | } | | |
| 5082 | | | |
| 5083 | // Input params | | |
| 5084 | switch (cc) { | 5232 | switch (cc) { |
| 5085 | .Naked => { | 5233 | .Naked => { |
| 5086 | assert(result.args.len == 0); | 5234 | assert(result.args.len == 0); |
| ... | @@ -5090,9 +5238,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -5090,9 +5238,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5090 | return result; | 5238 | return result; |
| 5091 | }, | 5239 | }, |
| 5092 | .Unspecified, .C => { | 5240 | .Unspecified, .C => { |
| | 5241 | // Return values |
| | 5242 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| | 5243 | result.return_value = .{ .unreach = {} }; |
| | 5244 | } else if (!ret_ty.hasRuntimeBits()) { |
| | 5245 | result.return_value = .{ .none = {} }; |
| | 5246 | } else { |
| | 5247 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| | 5248 | if (ret_ty_size <= 8) { |
| | 5249 | const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size); |
| | 5250 | result.return_value = .{ .register = aliased_reg }; |
| | 5251 | } else { |
| | 5252 | // TODO save the stack location in `.rdi` for C ABI compatible extern calls. |
| | 5253 | const ret_ty_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); |
| | 5254 | const offset = mem.alignForwardGeneric(u32, ret_ty_size, ret_ty_align); |
| | 5255 | result.return_value = .{ .stack_offset = @intCast(i32, offset) }; |
| | 5256 | } |
| | 5257 | } |
| | 5258 | |
| | 5259 | // Input params |
| 5093 | // First, split into args that can be passed via registers. | 5260 | // First, split into args that can be passed via registers. |
| 5094 | // This will make it easier to then push the rest of args in reverse | 5261 | // This will make it easier to then push the rest of args in reverse |
| 5095 | // order on the stack. | 5262 | // order on the stack. |
| | 5263 | // TODO if we want to be C ABI compatible (well, SysV compatible), passing return value |
| | 5264 | // on the stack requires consuming `.rdi` set with the stack location where to save |
| | 5265 | // the return value. |
| 5096 | var next_int_reg: usize = 0; | 5266 | var next_int_reg: usize = 0; |
| 5097 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); | 5267 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); |
| 5098 | defer by_reg.deinit(); | 5268 | defer by_reg.deinit(); |
| ... | @@ -5133,7 +5303,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -5133,7 +5303,10 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5133 | } | 5303 | } |
| 5134 | } | 5304 | } |
| 5135 | | 5305 | |
| 5136 | var next_stack_offset: u32 = 0; | 5306 | var next_stack_offset: u32 = switch (result.return_value) { |
| | 5307 | .stack_offset => |off| @intCast(u32, off), |
| | 5308 | else => 0, |
| | 5309 | }; |
| 5137 | var count: usize = param_types.len; | 5310 | var count: usize = param_types.len; |
| 5138 | while (count > 0) : (count -= 1) { | 5311 | while (count > 0) : (count -= 1) { |
| 5139 | const i = count - 1; | 5312 | const i = count - 1; |
| ... | @@ -5163,7 +5336,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -5163,7 +5336,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5163 | // alignment padding | args ... | ret addr | $rbp | | 5336 | // alignment padding | args ... | ret addr | $rbp | |
| 5164 | result.stack_byte_count = mem.alignForwardGeneric(u32, next_stack_offset, result.stack_align); | 5337 | result.stack_byte_count = mem.alignForwardGeneric(u32, next_stack_offset, result.stack_align); |
| 5165 | }, | 5338 | }, |
| 5166 | else => return self.fail("TODO implement function parameters for {} on x86_64", .{cc}), | 5339 | else => return self.fail("TODO implement function parameters and return values for {} on x86_64", .{cc}), |
| 5167 | } | 5340 | } |
| 5168 | | 5341 | |
| 5169 | return result; | 5342 | return result; |