| ... | ... | @@ -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 | 3124 | else => {}, |
| 3116 | 3125 | } |
| 3117 | 3126 | break :result info.return_value; |
| ... | ... | @@ -3133,7 +3142,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 3133 | 3142 | |
| 3134 | 3143 | fn ret(self: *Self, mcv: MCValue) !void { |
| 3135 | 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 | 3156 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. |
| 3138 | 3157 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction |
| 3139 | 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 | 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 | 4302 | fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type, val: MCValue) InnerError!void { |
| 4135 | 4303 | const abi_size = ty.abiSize(self.target.*); |
| 4136 | 4304 | |
| ... | ... | @@ -5061,26 +5229,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5061 | 5229 | |
| 5062 | 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 | 5232 | switch (cc) { |
| 5085 | 5233 | .Naked => { |
| 5086 | 5234 | assert(result.args.len == 0); |
| ... | ... | @@ -5090,9 +5238,31 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5090 | 5238 | return result; |
| 5091 | 5239 | }, |
| 5092 | 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 | 5260 | // First, split into args that can be passed via registers. |
| 5094 | 5261 | // This will make it easier to then push the rest of args in reverse |
| 5095 | 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 | 5266 | var next_int_reg: usize = 0; |
| 5097 | 5267 | var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator); |
| 5098 | 5268 | defer by_reg.deinit(); |
| ... | ... | @@ -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 | 5310 | var count: usize = param_types.len; |
| 5138 | 5311 | while (count > 0) : (count -= 1) { |
| 5139 | 5312 | const i = count - 1; |
| ... | ... | @@ -5163,7 +5336,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5163 | 5336 | // alignment padding | args ... | ret addr | $rbp | |
| 5164 | 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 | 5342 | return result; |