authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-29 00:08:43-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-29 02:29:37-07:00
log1bbfa36b76271e907cac88e83cec8dee1e3d69f7
tree3cfa960bb8f95a5bb97afaac13e7c0e4e4dd33f1
parent64a1a280ef2b5858aa9d5ec659badf3e5236b5f9

stage2: improved codegen

* multiple returns jump to one canonical function exitlude. This is in preparation for the defer feature. * simple elision of trivial jump relocs. * omit prelude/exitlude for naked calling convention functions. * fix not switching on arch for prelude/exitlude * fix swapped registers when setting stack mem from a register

1 files changed, 90 insertions(+), 28 deletions(-)

src-self-hosted/codegen.zig+90-28
...@@ -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,
216216
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);
379385
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 }
402408
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) {
405411 .x86_64 => {
406 // TODO omit this for naked functions412 try self.code.ensureCapacity(self.code.items.len + 11);
407 // push rbp413
408 // mov rbp, rsp414 const cc = self.fn_type.fnCallingConvention();
409 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });415 if (cc != .Naked) {
410416 // We want to subtract the aligned stack frame size from rsp here, but we don't
411 // sub rsp, x417 // 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,0x10422 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,0x80427 // 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 }
425469
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 }
428483
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); // ret1042 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 rbp1046 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
992 0xc3, // ret1047 // 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) catch1195 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],rdx1360 // 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],rdx1366 // 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 });