authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-27 18:59:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-27 18:59:13-07:00
logb8e22d20022f533de701b5efa9e28a196de664f3
tree2882fa1565e69151cae9a22d5b74cacd8aa0296e
parent3e0a46281c91f7706add98df2a009697170822cb

stage2: implement integer return values


3 files changed, 113 insertions(+), 42 deletions(-)

src-self-hosted/codegen.zig+82-42
...@@ -206,6 +206,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -206,6 +206,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
206 code: *std.ArrayList(u8),206 code: *std.ArrayList(u8),
207 err_msg: ?*ErrorMsg,207 err_msg: ?*ErrorMsg,
208 args: []MCValue,208 args: []MCValue,
209 ret_mcv: MCValue,
209 arg_index: usize,210 arg_index: usize,
210 src: usize,211 src: usize,
211212
...@@ -333,11 +334,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -333,11 +334,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
333 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;334 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
334335
335 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;336 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
336 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
337 defer bin_file.allocator.free(param_types);
338 fn_type.fnParamTypes(param_types);
339 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);
340 defer bin_file.allocator.free(mc_args);
341337
342 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);338 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
343 defer {339 defer {
...@@ -355,17 +351,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -355,17 +351,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
355 .mod_fn = module_fn,351 .mod_fn = module_fn,
356 .code = code,352 .code = code,
357 .err_msg = null,353 .err_msg = null,
358 .args = mc_args,354 .args = undefined, // populated after `resolveCallingConventionValues`
355 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
359 .arg_index = 0,356 .arg_index = 0,
360 .branch_stack = &branch_stack,357 .branch_stack = &branch_stack,
361 .src = src,358 .src = src,
362 };359 };
363360
364 const cc = fn_type.fnCallingConvention();361 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
365 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
366 error.CodegenFail => return Result{ .fail = function.err_msg.? },362 error.CodegenFail => return Result{ .fail = function.err_msg.? },
367 else => |e| return e,363 else => |e| return e,
368 };364 };
365 defer call_info.deinit(&function);
366
367 function.args = call_info.args;
368 function.ret_mcv = call_info.return_value;
369 branch.max_end_stack = call_info.stack_byte_count;
369370
370 function.gen() catch |err| switch (err) {371 function.gen() catch |err| switch (err) {
371 error.CodegenFail => return Result{ .fail = function.err_msg.? },372 error.CodegenFail => return Result{ .fail = function.err_msg.? },
...@@ -705,18 +706,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -705,18 +706,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
705 }706 }
706707
707 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {708 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
708 const fn_ty = inst.func.ty;709 var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);
709 const cc = fn_ty.fnCallingConvention();710 defer info.deinit(self);
710 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
711 defer self.gpa.free(param_types);
712 fn_ty.fnParamTypes(param_types);
713 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
714 defer self.gpa.free(mc_args);
715 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
716711
717 switch (arch) {712 switch (arch) {
718 .x86_64 => {713 .x86_64 => {
719 for (mc_args) |mc_arg, arg_i| {714 for (info.args) |mc_arg, arg_i| {
720 const arg = inst.args[arg_i];715 const arg = inst.args[arg_i];
721 const arg_mcv = try self.resolveInst(inst.args[arg_i]);716 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
722 switch (mc_arg) {717 switch (mc_arg) {
...@@ -761,18 +756,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -761,18 +756,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
761 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),756 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
762 }757 }
763758
764 const return_type = fn_ty.fnReturnType();759 return info.return_value;
765 switch (return_type.zigTypeTag()) {
766 .Void => return MCValue{ .none = {} },
767 .NoReturn => return MCValue{ .unreach = {} },
768 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
769 }
770 }760 }
771761
772 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {762 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
773 if (mcv != .none) {763 try self.setRegOrStack(src, self.ret_mcv, mcv);
774 return self.fail(src, "TODO implement return with non-void operand", .{});
775 }
776 switch (arch) {764 switch (arch) {
777 .i386 => {765 .i386 => {
778 try self.code.append(0xc3); // ret766 try self.code.append(0xc3); // ret
...@@ -1024,12 +1012,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1024,12 +1012,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1024 }1012 }
1025 }1013 }
10261014
1015 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1016 fn setRegOrStack(self: *Self, src: usize, loc: MCValue, val: MCValue) !void {
1017 switch (loc) {
1018 .none => return,
1019 .register => |reg| return self.genSetReg(src, reg, val),
1020 .stack_offset => {
1021 return self.fail(src, "TODO implement setRegOrStack for stack offset", .{});
1022 },
1023 else => unreachable,
1024 }
1025 }
1026
1027 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {1027 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
1028 switch (arch) {1028 switch (arch) {
1029 .x86_64 => switch (mcv) {1029 .x86_64 => switch (mcv) {
1030 .dead => unreachable,1030 .dead => unreachable,
1031 .none => unreachable,1031 .unreach, .none => return, // Nothing to do.
1032 .unreach => unreachable,
1033 .compare_flags_unsigned => |op| {1032 .compare_flags_unsigned => |op| {
1034 try self.code.ensureCapacity(self.code.items.len + 3);1033 try self.code.ensureCapacity(self.code.items.len + 3);
1035 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });1034 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
...@@ -1131,6 +1130,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1131,6 +1130,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1131 mem.writeIntLittle(i32, imm_ptr, offset);1130 mem.writeIntLittle(i32, imm_ptr, offset);
1132 },1131 },
1133 .register => |src_reg| {1132 .register => |src_reg| {
1133 // If the registers are the same, nothing to do.
1134 if (src_reg == reg)
1135 return;
1136
1134 if (reg.size() != 64) {1137 if (reg.size() != 64) {
1135 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});1138 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1136 }1139 }
...@@ -1211,7 +1214,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1211,7 +1214,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1211 return self.fail(src, "TODO implement genSetReg for stack variables", .{});1214 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
1212 },1215 },
1213 },1216 },
1214 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),1217 else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),
1215 }1218 }
1216 }1219 }
12171220
...@@ -1320,19 +1323,40 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1320,19 +1323,40 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1320 }1323 }
1321 }1324 }
13221325
1323 fn resolveParameters(1326 const CallMCValues = struct {
1324 self: *Self,1327 args: []MCValue,
1325 src: usize,1328 return_value: MCValue,
1326 cc: std.builtin.CallingConvention,1329 stack_byte_count: u32,
1327 param_types: []const Type,1330
1328 results: []MCValue,1331 fn deinit(self: *CallMCValues, func: *Self) void {
1329 ) !u32 {1332 func.gpa.free(self.args);
1333 self.* = undefined;
1334 }
1335 };
1336
1337 /// Caller must call `CallMCValues.deinit`.
1338 fn resolveCallingConventionValues(self: *Self, src: usize, fn_ty: Type) !CallMCValues {
1339 const cc = fn_ty.fnCallingConvention();
1340 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
1341 defer self.gpa.free(param_types);
1342 fn_ty.fnParamTypes(param_types);
1343 var result: CallMCValues = .{
1344 .args = try self.gpa.alloc(MCValue, param_types.len),
1345 .return_value = undefined,
1346 .stack_byte_count = undefined,
1347 };
1348 errdefer self.gpa.free(result.args);
1349
1350 const ret_ty = fn_ty.fnReturnType();
1351
1330 switch (arch) {1352 switch (arch) {
1331 .x86_64 => {1353 .x86_64 => {
1332 switch (cc) {1354 switch (cc) {
1333 .Naked => {1355 .Naked => {
1334 assert(results.len == 0);1356 assert(result.args.len == 0);
1335 return 0;1357 result.return_value = .{ .unreach = {} };
1358 result.stack_byte_count = 0;
1359 return result;
1336 },1360 },
1337 .Unspecified, .C => {1361 .Unspecified, .C => {
1338 var next_int_reg: usize = 0;1362 var next_int_reg: usize = 0;
...@@ -1342,23 +1366,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1342,23 +1366,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1342 switch (ty.zigTypeTag()) {1366 switch (ty.zigTypeTag()) {
1343 .Bool, .Int => {1367 .Bool, .Int => {
1344 if (next_int_reg >= c_abi_int_param_regs.len) {1368 if (next_int_reg >= c_abi_int_param_regs.len) {
1345 results[i] = .{ .stack_offset = next_stack_offset };1369 result.args[i] = .{ .stack_offset = next_stack_offset };
1346 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));1370 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1347 } else {1371 } else {
1348 results[i] = .{ .register = c_abi_int_param_regs[next_int_reg] };1372 result.args[i] = .{ .register = c_abi_int_param_regs[next_int_reg] };
1349 next_int_reg += 1;1373 next_int_reg += 1;
1350 }1374 }
1351 },1375 },
1352 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),1376 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1353 }1377 }
1354 }1378 }
1355 return next_stack_offset;1379 result.stack_byte_count = next_stack_offset;
1356 },1380 },
1357 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),1381 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1358 }1382 }
1359 },1383 },
1360 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),1384 else => return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
1385 }
1386
1387 if (ret_ty.zigTypeTag() == .NoReturn) {
1388 result.return_value = .{ .unreach = {} };
1389 } else if (!ret_ty.hasCodeGenBits()) {
1390 result.return_value = .{ .none = {} };
1391 } else switch (arch) {
1392 .x86_64 => switch (cc) {
1393 .Naked => unreachable,
1394 .Unspecified, .C => {
1395 result.return_value = .{ .register = c_abi_int_return_regs[0] };
1396 },
1397 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
1398 },
1399 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),
1361 }1400 }
1401 return result;
1362 }1402 }
13631403
1364 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {1404 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
src-self-hosted/codegen/x86_64.zig+1
...@@ -88,3 +88,4 @@ pub const Register = enum(u8) {...@@ -88,3 +88,4 @@ pub const Register = enum(u8) {
88/// These registers belong to the called function.88/// These registers belong to the called function.
89pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };89pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
90pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };90pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
91pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
test/stage2/compare_output.zig+30
...@@ -333,5 +333,35 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -333,5 +333,35 @@ pub fn addCases(ctx: *TestContext) !void {
333 ,333 ,
334 "",334 "",
335 );335 );
336
337 // Now we test integer return values.
338 case.addCompareOutput(
339 \\export fn _start() noreturn {
340 \\ assert(add(3, 4) == 7);
341 \\ assert(add(20, 10) == 30);
342 \\
343 \\ exit();
344 \\}
345 \\
346 \\fn add(a: u32, b: u32) u32 {
347 \\ return a + b;
348 \\}
349 \\
350 \\pub fn assert(ok: bool) void {
351 \\ if (!ok) unreachable; // assertion failure
352 \\}
353 \\
354 \\fn exit() noreturn {
355 \\ asm volatile ("syscall"
356 \\ :
357 \\ : [number] "{rax}" (231),
358 \\ [arg1] "{rdi}" (0)
359 \\ : "rcx", "r11", "memory"
360 \\ );
361 \\ unreachable;
362 \\}
363 ,
364 "",
365 );
336 }366 }
337}367}