| ... | @@ -214,6 +214,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -214,6 +214,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 214 | src: usize, | 214 | src: usize, |
| 215 | stack_align: u32, | 215 | stack_align: u32, |
| 216 | | 216 | |
| | 217 | /// The value is an offset into the `Function` `code` from the beginning. |
| | 218 | /// To perform the reloc, write 32-bit signed little-endian integer |
| | 219 | /// which is a relative jump, based on the address following the reloc. |
| | 220 | exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, |
| | 221 | |
| 217 | /// Whenever there is a runtime branch, we push a Branch onto this stack, | 222 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| 218 | /// and pop it off when the runtime branch joins. This provides an "overlay" | 223 | /// and pop it off when the runtime branch joins. This provides an "overlay" |
| 219 | /// of the table of mappings from instructions to `MCValue` from within the branch. | 224 | /// of the table of mappings from instructions to `MCValue` from within the branch. |
| ... | @@ -376,6 +381,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -376,6 +381,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 376 | .src = src, | 381 | .src = src, |
| 377 | .stack_align = undefined, | 382 | .stack_align = undefined, |
| 378 | }; | 383 | }; |
| | 384 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 379 | | 385 | |
| 380 | var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) { | 386 | var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) { |
| 381 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | 387 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| ... | @@ -401,29 +407,78 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -401,29 +407,78 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 401 | } | 407 | } |
| 402 | | 408 | |
| 403 | fn gen(self: *Self) !void { | 409 | fn gen(self: *Self) !void { |
| 404 | try self.code.ensureCapacity(self.code.items.len + 11); | 410 | switch (arch) { |
| 405 | | 411 | .x86_64 => { |
| 406 | // TODO omit this for naked functions | 412 | try self.code.ensureCapacity(self.code.items.len + 11); |
| 407 | // push rbp | 413 | |
| 408 | // mov rbp, rsp | 414 | const cc = self.fn_type.fnCallingConvention(); |
| 409 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 }); | 415 | if (cc != .Naked) { |
| 410 | | 416 | // We want to subtract the aligned stack frame size from rsp here, but we don't |
| 411 | // sub rsp, x | 417 | // yet know how big it will be, so we leave room for a 4-byte stack size. |
| 412 | const stack_end = self.branch_stack.items[0].max_end_stack; | 418 | // TODO During semantic analysis, check if there are no function calls. If there |
| 413 | if (stack_end > math.maxInt(i32)) { | 419 | // are none, here we can omit the part where we subtract and then add rsp. |
| 414 | return self.fail(self.src, "too much stack used in call parameters", .{}); | 420 | self.code.appendSliceAssumeCapacity(&[_]u8{ |
| 415 | } else if (stack_end > math.maxInt(i8)) { | 421 | // push rbp |
| 416 | // 48 83 ec xx sub rsp,0x10 | 422 | 0x55, |
| 417 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec }); | 423 | // mov rbp, rsp |
| 418 | const x = @intCast(u32, stack_end); | 424 | 0x48, |
| 419 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x); | 425 | 0x89, |
| 420 | } else if (stack_end != 0) { | 426 | 0xe5, |
| 421 | // 48 81 ec xx xx xx xx sub rsp,0x80 | 427 | // sub rsp, imm32 (with reloc) |
| 422 | const x = @intCast(u8, stack_end); | 428 | 0x48, |
| 423 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x }); | 429 | 0x81, |
| 424 | } | 430 | 0xec, |
| | 431 | }); |
| | 432 | const reloc_index = self.code.items.len; |
| | 433 | self.code.items.len += 4; |
| | 434 | |
| | 435 | try self.genBody(self.mod_fn.analysis.success); |
| | 436 | |
| | 437 | const stack_end = self.branch_stack.items[0].max_end_stack; |
| | 438 | if (stack_end > math.maxInt(i32)) |
| | 439 | return self.fail(self.src, "too much stack used in call parameters", .{}); |
| | 440 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); |
| | 441 | mem.writeIntLittle(u32, self.code.items[reloc_index..][0..4], @intCast(u32, aligned_stack_end)); |
| | 442 | |
| | 443 | if (self.code.items.len >= math.maxInt(i32)) { |
| | 444 | return self.fail(self.src, "unable to perform relocation: jump too far", .{}); |
| | 445 | } |
| | 446 | for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| | 447 | const amt = self.code.items.len - (jmp_reloc + 4); |
| | 448 | // If it wouldn't jump at all, elide it. |
| | 449 | if (amt == 0) { |
| | 450 | self.code.items.len -= 5; |
| | 451 | continue; |
| | 452 | } |
| | 453 | const s32_amt = @intCast(i32, amt); |
| | 454 | mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt); |
| | 455 | } |
| | 456 | |
| | 457 | try self.code.ensureCapacity(self.code.items.len + 9); |
| | 458 | // add rsp, x |
| | 459 | if (aligned_stack_end > math.maxInt(i8)) { |
| | 460 | // example: 48 81 c4 ff ff ff 7f add rsp,0x7fffffff |
| | 461 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xc4 }); |
| | 462 | const x = @intCast(u32, aligned_stack_end); |
| | 463 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x); |
| | 464 | } else if (aligned_stack_end != 0) { |
| | 465 | // example: 48 83 c4 7f add rsp,0x7f |
| | 466 | const x = @intCast(u8, aligned_stack_end); |
| | 467 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xc4, x }); |
| | 468 | } |
| 425 | | 469 | |
| 426 | try self.genBody(self.mod_fn.analysis.success); | 470 | self.code.appendSliceAssumeCapacity(&[_]u8{ |
| | 471 | 0x5d, // pop rbp |
| | 472 | 0xc3, // ret |
| | 473 | }); |
| | 474 | } else { |
| | 475 | try self.genBody(self.mod_fn.analysis.success); |
| | 476 | } |
| | 477 | }, |
| | 478 | else => { |
| | 479 | try self.genBody(self.mod_fn.analysis.success); |
| | 480 | }, |
| | 481 | } |
| 427 | } | 482 | } |
| 428 | | 483 | |
| 429 | fn genBody(self: *Self, body: ir.Body) InnerError!void { | 484 | fn genBody(self: *Self, body: ir.Body) InnerError!void { |
| ... | @@ -987,10 +1042,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -987,10 +1042,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 987 | try self.code.append(0xc3); // ret | 1042 | try self.code.append(0xc3); // ret |
| 988 | }, | 1043 | }, |
| 989 | .x86_64 => { | 1044 | .x86_64 => { |
| 990 | try self.code.appendSlice(&[_]u8{ | 1045 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. |
| 991 | 0x5d, // pop rbp | 1046 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction |
| 992 | 0xc3, // ret | 1047 | // which is available if the jump is 127 bytes or less forward. |
| 993 | }); | 1048 | try self.code.resize(self.code.items.len + 5); |
| | 1049 | self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32 |
| | 1050 | try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); |
| 994 | }, | 1051 | }, |
| 995 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), | 1052 | else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}), |
| 996 | } | 1053 | } |
| ... | @@ -1130,6 +1187,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1130,6 +1187,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1130 | switch (reloc) { | 1187 | switch (reloc) { |
| 1131 | .rel32 => |pos| { | 1188 | .rel32 => |pos| { |
| 1132 | const amt = self.code.items.len - (pos + 4); | 1189 | const amt = self.code.items.len - (pos + 4); |
| | 1190 | // If it wouldn't jump at all, elide it. |
| | 1191 | if (amt == 0) { |
| | 1192 | self.code.items.len -= 5; |
| | 1193 | return; |
| | 1194 | } |
| 1133 | const s32_amt = math.cast(i32, amt) catch | 1195 | const s32_amt = math.cast(i32, amt) catch |
| 1134 | return self.fail(src, "unable to perform relocation: jump too far", .{}); | 1196 | return self.fail(src, "unable to perform relocation: jump too far", .{}); |
| 1135 | mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt); | 1197 | mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt); |
| ... | @@ -1296,13 +1358,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1296,13 +1358,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1296 | const reg_id: u8 = @truncate(u3, reg.id()); | 1358 | const reg_id: u8 = @truncate(u3, reg.id()); |
| 1297 | if (stack_offset <= 128) { | 1359 | if (stack_offset <= 128) { |
| 1298 | // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx | 1360 | // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx |
| 1299 | const RM = @as(u8, 0b01_101_000) | reg_id; | 1361 | const RM = @as(u8, 0b01_000_101) | (reg_id << 3); |
| 1300 | const negative_offset = @intCast(i8, -@intCast(i32, stack_offset)); | 1362 | const negative_offset = @intCast(i8, -@intCast(i32, stack_offset)); |
| 1301 | const twos_comp = @bitCast(u8, negative_offset); | 1363 | const twos_comp = @bitCast(u8, negative_offset); |
| 1302 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp }); | 1364 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp }); |
| 1303 | } else if (stack_offset <= 2147483648) { | 1365 | } else if (stack_offset <= 2147483648) { |
| 1304 | // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx | 1366 | // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx |
| 1305 | const RM = @as(u8, 0b10_101_000) | reg_id; | 1367 | const RM = @as(u8, 0b10_000_101) | (reg_id << 3); |
| 1306 | const negative_offset = @intCast(i32, -@intCast(i33, stack_offset)); | 1368 | const negative_offset = @intCast(i32, -@intCast(i33, stack_offset)); |
| 1307 | const twos_comp = @bitCast(u32, negative_offset); | 1369 | const twos_comp = @bitCast(u32, negative_offset); |
| 1308 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM }); | 1370 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM }); |