| ... | ... | @@ -214,6 +214,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 214 | 214 | src: usize, |
| 215 | 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 | 222 | /// Whenever there is a runtime branch, we push a Branch onto this stack, |
| 218 | 223 | /// and pop it off when the runtime branch joins. This provides an "overlay" |
| 219 | 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 | 381 | .src = src, |
| 377 | 382 | .stack_align = undefined, |
| 378 | 383 | }; |
| 384 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 379 | 385 | |
| 380 | 386 | var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) { |
| 381 | 387 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| ... | ... | @@ -401,29 +407,78 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 401 | 407 | } |
| 402 | 408 | |
| 403 | 409 | fn gen(self: *Self) !void { |
| 404 | | try self.code.ensureCapacity(self.code.items.len + 11); |
| 405 | | |
| 406 | | // TODO omit this for naked functions |
| 407 | | // push rbp |
| 408 | | // mov rbp, rsp |
| 409 | | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 }); |
| 410 | | |
| 411 | | // sub rsp, x |
| 412 | | const stack_end = self.branch_stack.items[0].max_end_stack; |
| 413 | | if (stack_end > math.maxInt(i32)) { |
| 414 | | return self.fail(self.src, "too much stack used in call parameters", .{}); |
| 415 | | } else if (stack_end > math.maxInt(i8)) { |
| 416 | | // 48 83 ec xx sub rsp,0x10 |
| 417 | | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec }); |
| 418 | | const x = @intCast(u32, stack_end); |
| 419 | | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x); |
| 420 | | } else if (stack_end != 0) { |
| 421 | | // 48 81 ec xx xx xx xx sub rsp,0x80 |
| 422 | | const x = @intCast(u8, stack_end); |
| 423 | | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x }); |
| 424 | | } |
| 410 | switch (arch) { |
| 411 | .x86_64 => { |
| 412 | try self.code.ensureCapacity(self.code.items.len + 11); |
| 413 | |
| 414 | const cc = self.fn_type.fnCallingConvention(); |
| 415 | if (cc != .Naked) { |
| 416 | // We want to subtract the aligned stack frame size from rsp here, but we don't |
| 417 | // yet know how big it will be, so we leave room for a 4-byte stack size. |
| 418 | // TODO During semantic analysis, check if there are no function calls. If there |
| 419 | // are none, here we can omit the part where we subtract and then add rsp. |
| 420 | self.code.appendSliceAssumeCapacity(&[_]u8{ |
| 421 | // push rbp |
| 422 | 0x55, |
| 423 | // mov rbp, rsp |
| 424 | 0x48, |
| 425 | 0x89, |
| 426 | 0xe5, |
| 427 | // sub rsp, imm32 (with reloc) |
| 428 | 0x48, |
| 429 | 0x81, |
| 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 | 484 | fn genBody(self: *Self, body: ir.Body) InnerError!void { |
| ... | ... | @@ -987,10 +1042,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 987 | 1042 | try self.code.append(0xc3); // ret |
| 988 | 1043 | }, |
| 989 | 1044 | .x86_64 => { |
| 990 | | try self.code.appendSlice(&[_]u8{ |
| 991 | | 0x5d, // pop rbp |
| 992 | | 0xc3, // ret |
| 993 | | }); |
| 1045 | // TODO when implementing defer, this will need to jump to the appropriate defer expression. |
| 1046 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction |
| 1047 | // which is available if the jump is 127 bytes or less forward. |
| 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 | 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 | 1187 | switch (reloc) { |
| 1131 | 1188 | .rel32 => |pos| { |
| 1132 | 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 | 1195 | const s32_amt = math.cast(i32, amt) catch |
| 1134 | 1196 | return self.fail(src, "unable to perform relocation: jump too far", .{}); |
| 1135 | 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 | 1358 | const reg_id: u8 = @truncate(u3, reg.id()); |
| 1297 | 1359 | if (stack_offset <= 128) { |
| 1298 | 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 | 1362 | const negative_offset = @intCast(i8, -@intCast(i32, stack_offset)); |
| 1301 | 1363 | const twos_comp = @bitCast(u8, negative_offset); |
| 1302 | 1364 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp }); |
| 1303 | 1365 | } else if (stack_offset <= 2147483648) { |
| 1304 | 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 | 1368 | const negative_offset = @intCast(i32, -@intCast(i33, stack_offset)); |
| 1307 | 1369 | const twos_comp = @bitCast(u32, negative_offset); |
| 1308 | 1370 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM }); |