| ... | ... | @@ -206,6 +206,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 206 | 206 | code: *std.ArrayList(u8), |
| 207 | 207 | err_msg: ?*ErrorMsg, |
| 208 | 208 | args: []MCValue, |
| 209 | ret_mcv: MCValue, |
| 209 | 210 | arg_index: usize, |
| 210 | 211 | src: usize, |
| 211 | 212 | |
| ... | ... | @@ -333,11 +334,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 333 | 334 | const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; |
| 334 | 335 | |
| 335 | 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 | 338 | var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); |
| 343 | 339 | defer { |
| ... | ... | @@ -355,17 +351,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 355 | 351 | .mod_fn = module_fn, |
| 356 | 352 | .code = code, |
| 357 | 353 | .err_msg = null, |
| 358 | | .args = mc_args, |
| 354 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 355 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| 359 | 356 | .arg_index = 0, |
| 360 | 357 | .branch_stack = &branch_stack, |
| 361 | 358 | .src = src, |
| 362 | 359 | }; |
| 363 | 360 | |
| 364 | | const cc = fn_type.fnCallingConvention(); |
| 365 | | branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) { |
| 361 | var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) { |
| 366 | 362 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 367 | 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 | 371 | function.gen() catch |err| switch (err) { |
| 371 | 372 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| ... | ... | @@ -705,18 +706,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 705 | 706 | } |
| 706 | 707 | |
| 707 | 708 | fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue { |
| 708 | | const fn_ty = inst.func.ty; |
| 709 | | const cc = fn_ty.fnCallingConvention(); |
| 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); |
| 709 | var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty); |
| 710 | defer info.deinit(self); |
| 716 | 711 | |
| 717 | 712 | switch (arch) { |
| 718 | 713 | .x86_64 => { |
| 719 | | for (mc_args) |mc_arg, arg_i| { |
| 714 | for (info.args) |mc_arg, arg_i| { |
| 720 | 715 | const arg = inst.args[arg_i]; |
| 721 | 716 | const arg_mcv = try self.resolveInst(inst.args[arg_i]); |
| 722 | 717 | switch (mc_arg) { |
| ... | ... | @@ -761,18 +756,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 761 | 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(); |
| 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 | | } |
| 759 | return info.return_value; |
| 770 | 760 | } |
| 771 | 761 | |
| 772 | 762 | fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue { |
| 773 | | if (mcv != .none) { |
| 774 | | return self.fail(src, "TODO implement return with non-void operand", .{}); |
| 775 | | } |
| 763 | try self.setRegOrStack(src, self.ret_mcv, mcv); |
| 776 | 764 | switch (arch) { |
| 777 | 765 | .i386 => { |
| 778 | 766 | try self.code.append(0xc3); // ret |
| ... | ... | @@ -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 | 1027 | fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void { |
| 1028 | 1028 | switch (arch) { |
| 1029 | 1029 | .x86_64 => switch (mcv) { |
| 1030 | 1030 | .dead => unreachable, |
| 1031 | | .none => unreachable, |
| 1032 | | .unreach => unreachable, |
| 1031 | .unreach, .none => return, // Nothing to do. |
| 1033 | 1032 | .compare_flags_unsigned => |op| { |
| 1034 | 1033 | try self.code.ensureCapacity(self.code.items.len + 3); |
| 1035 | 1034 | self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 }); |
| ... | ... | @@ -1131,6 +1130,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1131 | 1130 | mem.writeIntLittle(i32, imm_ptr, offset); |
| 1132 | 1131 | }, |
| 1133 | 1132 | .register => |src_reg| { |
| 1133 | // If the registers are the same, nothing to do. |
| 1134 | if (src_reg == reg) |
| 1135 | return; |
| 1136 | |
| 1134 | 1137 | if (reg.size() != 64) { |
| 1135 | 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 | 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 | 1323 | } |
| 1321 | 1324 | } |
| 1322 | 1325 | |
| 1323 | | fn resolveParameters( |
| 1324 | | self: *Self, |
| 1325 | | src: usize, |
| 1326 | | cc: std.builtin.CallingConvention, |
| 1327 | | param_types: []const Type, |
| 1328 | | results: []MCValue, |
| 1329 | | ) !u32 { |
| 1326 | const CallMCValues = struct { |
| 1327 | args: []MCValue, |
| 1328 | return_value: MCValue, |
| 1329 | stack_byte_count: u32, |
| 1330 | |
| 1331 | fn deinit(self: *CallMCValues, func: *Self) void { |
| 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 | 1352 | switch (arch) { |
| 1331 | 1353 | .x86_64 => { |
| 1332 | 1354 | switch (cc) { |
| 1333 | 1355 | .Naked => { |
| 1334 | | assert(results.len == 0); |
| 1335 | | return 0; |
| 1356 | assert(result.args.len == 0); |
| 1357 | result.return_value = .{ .unreach = {} }; |
| 1358 | result.stack_byte_count = 0; |
| 1359 | return result; |
| 1336 | 1360 | }, |
| 1337 | 1361 | .Unspecified, .C => { |
| 1338 | 1362 | var next_int_reg: usize = 0; |
| ... | ... | @@ -1342,23 +1366,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1342 | 1366 | switch (ty.zigTypeTag()) { |
| 1343 | 1367 | .Bool, .Int => { |
| 1344 | 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 | 1370 | next_stack_offset += @intCast(u32, ty.abiSize(self.target.*)); |
| 1347 | 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 | 1373 | next_int_reg += 1; |
| 1350 | 1374 | } |
| 1351 | 1375 | }, |
| 1352 | 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 | 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 | 1404 | fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } { |