authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-19 21:17:30+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-22 21:56:34+01:00
log478f7700533097b92820dafd3003af6320fd3c85
treefa8e80757acf27205a5902cb20f0016a5787e5db
parente582c0a043e89e0d507ca86253a836766794db0e

x64: first, really horrible attempt at returning values on stack


1 files changed, 196 insertions(+), 23 deletions(-)

src/arch/x86_64/CodeGen.zig+196-23
...@@ -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 {
31333142
3134fn ret(self: *Self, mcv: MCValue) !void {3143fn 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 instruction3157 // 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}
41334152
4153fn 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
4134fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type, val: MCValue) InnerError!void {4302fn 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.*);
41364304
...@@ -5061,26 +5229,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -5061,26 +5229,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
50615229
5062 const ret_ty = fn_ty.fnReturnType();5230 const ret_ty = fn_ty.fnReturnType();
50635231
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 reverse5261 // 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 }
51355305
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 }
51685341
5169 return result;5342 return result;