| ... | @@ -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, |
| 211 | | 212 | |
| ... | @@ -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; |
| 334 | | 335 | |
| 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); | | |
| 341 | | 337 | |
| 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 | }; |
| 363 | | 360 | |
| 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; |
| 369 | | 370 | |
| 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 | } |
| 706 | | 707 | |
| 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); | | |
| 716 | | 711 | |
| 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 | } |
| 763 | | 758 | |
| 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 | } |
| 771 | | 761 | |
| 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); // ret | 766 | 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 | } |
| 1026 | | 1014 | |
| | 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 | } |
| 1217 | | 1220 | |
| ... | @@ -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 | } |
| 1322 | | 1325 | |
| 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 | } |
| 1363 | | 1403 | |
| 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 } { |