authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-02-28 22:41:36+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-03-01 09:14:52+01:00
logca97caab8a02813eb76388e72e2a9dab1f5fa047
tree94196cf3a478a397957d43540767f721b4a73960
parentec54ceee6d34a61734ac6759ffdbd75248a2f3d7

stage2 ARM: implement return types with abi size > 4


3 files changed, 196 insertions(+), 107 deletions(-)

src/arch/arm/CodeGen.zig+196-105
...@@ -387,6 +387,18 @@ fn gen(self: *Self) !void {...@@ -387,6 +387,18 @@ fn gen(self: *Self) !void {
387 // sub sp, sp, #reloc387 // sub sp, sp, #reloc
388 const sub_reloc = try self.addNop();388 const sub_reloc = try self.addNop();
389389
390 if (self.ret_mcv == .stack_offset) {
391 // The address of where to store the return value is in
392 // r0. As this register might get overwritten along the
393 // way, save the address to the stack.
394 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, 4);
395 self.next_stack_offset = stack_offset + 4;
396 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
397
398 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 });
399 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
400 }
401
390 _ = try self.addInst(.{402 _ = try self.addInst(.{
391 .tag = .dbg_prologue_end,403 .tag = .dbg_prologue_end,
392 .cond = undefined,404 .cond = undefined,
...@@ -1014,6 +1026,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -1014,6 +1026,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
10141026
1015 break :result MCValue{ .register = dest_reg };1027 break :result MCValue{ .register = dest_reg };
1016 },1028 },
1029 .Vector => return self.fail("TODO bitwise not for vectors", .{}),
1017 .Int => {1030 .Int => {
1018 const int_info = operand_ty.intInfo(self.target.*);1031 const int_info = operand_ty.intInfo(self.target.*);
1019 if (int_info.bits <= 32) {1032 if (int_info.bits <= 32) {
...@@ -1666,6 +1679,8 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -1666,6 +1679,8 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1666}1679}
16671680
1668fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {1681fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
1682 const elem_size = @intCast(u32, value_ty.abiSize(self.target.*));
1683
1669 switch (ptr) {1684 switch (ptr) {
1670 .none => unreachable,1685 .none => unreachable,
1671 .undef => unreachable,1686 .undef => unreachable,
...@@ -1702,7 +1717,30 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1702,7 +1717,30 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1702 try self.genSetReg(value_ty, tmp_reg, value);1717 try self.genSetReg(value_ty, tmp_reg, value);
1703 try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);1718 try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
1704 } else {1719 } else {
1705 return self.fail("TODO implement memcpy", .{});1720 const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null });
1721 self.register_manager.freezeRegs(&regs);
1722 defer self.register_manager.unfreezeRegs(&regs);
1723
1724 const src_reg = regs[0];
1725 const dst_reg = addr_reg;
1726 const len_reg = regs[1];
1727 const count_reg = regs[2];
1728 const tmp_reg = regs[3];
1729
1730 switch (value) {
1731 .stack_offset => |off| {
1732 // sub src_reg, fp, #off
1733 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off });
1734 },
1735 .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }),
1736 else => return self.fail("TODO store {} to register", .{value}),
1737 }
1738
1739 // mov len, #elem_size
1740 try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size });
1741
1742 // memcpy(src, dst, len)
1743 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg);
1706 }1744 }
1707 },1745 },
1708 }1746 }
...@@ -2408,9 +2446,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -2408,9 +2446,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2408 const mcv = switch (result) {2446 const mcv = switch (result) {
2409 // Copy registers to the stack2447 // Copy registers to the stack
2410 .register => |reg| blk: {2448 .register => |reg| blk: {
2411 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {2449 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
2412 return self.fail("type '{}' too big to fit into stack frame", .{ty});
2413 };
2414 const abi_align = ty.abiAlignment(self.target.*);2450 const abi_align = ty.abiAlignment(self.target.*);
2415 const stack_offset = try self.allocMem(inst, abi_size, abi_align);2451 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
2416 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });2452 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
...@@ -2491,95 +2527,113 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2491,95 +2527,113 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2491 // saving compare flags may require a new caller-saved register2527 // saving compare flags may require a new caller-saved register
2492 try self.spillCompareFlagsIfOccupied();2528 try self.spillCompareFlagsIfOccupied();
24932529
2530 if (info.return_value == .stack_offset) {
2531 const ret_ty = fn_ty.fnReturnType();
2532 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
2533 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
2534 const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align);
2535
2536 var ptr_ty_payload: Type.Payload.ElemType = .{
2537 .base = .{ .tag = .single_mut_pointer },
2538 .data = ret_ty,
2539 };
2540 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2541 try self.register_manager.getReg(.r0, inst);
2542 try self.genSetReg(ptr_ty, .r0, .{ .ptr_stack_offset = stack_offset });
2543
2544 info.return_value = .{ .stack_offset = stack_offset };
2545 }
2546
2494 // Make space for the arguments passed via the stack2547 // Make space for the arguments passed via the stack
2495 self.max_end_stack += info.stack_byte_count;2548 self.max_end_stack += info.stack_byte_count;
24962549
2550 for (info.args) |mc_arg, arg_i| {
2551 const arg = args[arg_i];
2552 const arg_ty = self.air.typeOf(arg);
2553 const arg_mcv = try self.resolveInst(args[arg_i]);
2554
2555 switch (mc_arg) {
2556 .none => continue,
2557 .undef => unreachable,
2558 .immediate => unreachable,
2559 .unreach => unreachable,
2560 .dead => unreachable,
2561 .embedded_in_code => unreachable,
2562 .memory => unreachable,
2563 .compare_flags_signed => unreachable,
2564 .compare_flags_unsigned => unreachable,
2565 .ptr_stack_offset => unreachable,
2566 .ptr_embedded_in_code => unreachable,
2567 .register => |reg| {
2568 try self.register_manager.getReg(reg, null);
2569 try self.genSetReg(arg_ty, reg, arg_mcv);
2570 },
2571 .stack_offset => unreachable,
2572 .stack_argument_offset => |offset| try self.genSetStackArgument(
2573 arg_ty,
2574 info.stack_byte_count - offset,
2575 arg_mcv,
2576 ),
2577 }
2578 }
2579
2497 // Due to incremental compilation, how function calls are generated depends2580 // Due to incremental compilation, how function calls are generated depends
2498 // on linking.2581 // on linking.
2499 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {2582 switch (self.bin_file.tag) {
2500 for (info.args) |mc_arg, arg_i| {2583 .elf, .coff => {
2501 const arg = args[arg_i];2584 if (self.air.value(callee)) |func_value| {
2502 const arg_ty = self.air.typeOf(arg);2585 if (func_value.castTag(.function)) |func_payload| {
2503 const arg_mcv = try self.resolveInst(args[arg_i]);2586 const func = func_payload.data;
25042587 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2505 switch (mc_arg) {2588 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2506 .none => continue,2589 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
2507 .undef => unreachable,2590 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
2508 .immediate => unreachable,2591 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
2509 .unreach => unreachable,2592 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
2510 .dead => unreachable,2593 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
2511 .embedded_in_code => unreachable,2594 else
2512 .memory => unreachable,2595 unreachable;
2513 .compare_flags_signed => unreachable,2596
2514 .compare_flags_unsigned => unreachable,2597 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
2515 .ptr_stack_offset => unreachable,2598 } else if (func_value.castTag(.extern_fn)) |_| {
2516 .ptr_embedded_in_code => unreachable,2599 return self.fail("TODO implement calling extern functions", .{});
2517 .register => |reg| {2600 } else {
2518 try self.register_manager.getReg(reg, null);2601 return self.fail("TODO implement calling bitcasted functions", .{});
2519 try self.genSetReg(arg_ty, reg, arg_mcv);2602 }
2520 },2603 } else {
2521 .stack_offset => unreachable,2604 assert(ty.zigTypeTag() == .Pointer);
2522 .stack_argument_offset => |offset| try self.genSetStackArgument(2605 const mcv = try self.resolveInst(callee);
2523 arg_ty,2606
2524 info.stack_byte_count - offset,2607 try self.genSetReg(Type.initTag(.usize), .lr, mcv);
2525 arg_mcv,
2526 ),
2527 }2608 }
2528 }
25292609
2530 if (self.air.value(callee)) |func_value| {2610 // TODO: add Instruction.supportedOn
2531 if (func_value.castTag(.function)) |func_payload| {2611 // function for ARM
2532 const func = func_payload.data;2612 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
2533 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2613 _ = try self.addInst(.{
2534 const ptr_bytes: u64 = @divExact(ptr_bits, 8);2614 .tag = .blx,
2535 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {2615 .data = .{ .reg = .lr },
2536 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];2616 });
2537 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
2538 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
2539 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
2540 else
2541 unreachable;
2542
2543 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
2544 } else if (func_value.castTag(.extern_fn)) |_| {
2545 return self.fail("TODO implement calling extern functions", .{});
2546 } else {2617 } else {
2547 return self.fail("TODO implement calling bitcasted functions", .{});2618 return self.fail("TODO fix blx emulation for ARM <v5", .{});
2619 // _ = try self.addInst(.{
2620 // .tag = .mov,
2621 // .data = .{ .rr_op = .{
2622 // .rd = .lr,
2623 // .rn = .r0,
2624 // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
2625 // } },
2626 // });
2627 // _ = try self.addInst(.{
2628 // .tag = .bx,
2629 // .data = .{ .reg = .lr },
2630 // });
2548 }2631 }
2549 } else {2632 },
2550 assert(ty.zigTypeTag() == .Pointer);2633 .macho => unreachable, // unsupported architecture for MachO
2551 const mcv = try self.resolveInst(callee);2634 .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
25522635 else => unreachable,
2553 try self.genSetReg(Type.initTag(.usize), .lr, mcv);2636 }
2554 }
2555
2556 // TODO: add Instruction.supportedOn
2557 // function for ARM
2558 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
2559 _ = try self.addInst(.{
2560 .tag = .blx,
2561 .data = .{ .reg = .lr },
2562 });
2563 } else {
2564 return self.fail("TODO fix blx emulation for ARM <v5", .{});
2565 // _ = try self.addInst(.{
2566 // .tag = .mov,
2567 // .data = .{ .rr_op = .{
2568 // .rd = .lr,
2569 // .rn = .r0,
2570 // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
2571 // } },
2572 // });
2573 // _ = try self.addInst(.{
2574 // .tag = .bx,
2575 // .data = .{ .reg = .lr },
2576 // });
2577 }
2578 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2579 unreachable; // unsupported architecture for MachO
2580 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
2581 return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch});
2582 } else unreachable;
25832637
2584 const result: MCValue = result: {2638 const result: MCValue = result: {
2585 switch (info.return_value) {2639 switch (info.return_value) {
...@@ -2610,7 +2664,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2610,7 +2664,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
26102664
2611fn ret(self: *Self, mcv: MCValue) !void {2665fn ret(self: *Self, mcv: MCValue) !void {
2612 const ret_ty = self.fn_type.fnReturnType();2666 const ret_ty = self.fn_type.fnReturnType();
2613 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);2667 switch (self.ret_mcv) {
2668 .none => {},
2669 .register => |reg| {
2670 // Return result by value
2671 try self.genSetReg(ret_ty, reg, mcv);
2672 },
2673 .stack_offset => {
2674 // Return result by reference
2675 //
2676 // self.ret_mcv is an address to where this function
2677 // should store its result into
2678 var ptr_ty_payload: Type.Payload.ElemType = .{
2679 .base = .{ .tag = .single_mut_pointer },
2680 .data = ret_ty,
2681 };
2682 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2683 try self.store(self.ret_mcv, mcv, ptr_ty, ret_ty);
2684 },
2685 else => unreachable, // invalid return result
2686 }
26142687
2615 // Just add space for an instruction, patch this later2688 // Just add space for an instruction, patch this later
2616 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());2689 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop());
...@@ -3524,9 +3597,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -3524,9 +3597,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
3524 });3597 });
3525 },3598 },
3526 .immediate => |x| {3599 .immediate => |x| {
3527 if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{});3600 if (Instruction.Operand.fromU32(x)) |op| {
3528
3529 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {
3530 _ = try self.addInst(.{3601 _ = try self.addInst(.{
3531 .tag = .mov,3602 .tag = .mov,
3532 .data = .{ .rr_op = .{3603 .data = .{ .rr_op = .{
...@@ -3535,7 +3606,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -3535,7 +3606,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
3535 .op = op,3606 .op = op,
3536 } },3607 } },
3537 });3608 });
3538 } else if (Instruction.Operand.fromU32(~@intCast(u32, x))) |op| {3609 } else if (Instruction.Operand.fromU32(~x)) |op| {
3539 _ = try self.addInst(.{3610 _ = try self.addInst(.{
3540 .tag = .mvn,3611 .tag = .mvn,
3541 .data = .{ .rr_op = .{3612 .data = .{ .rr_op = .{
...@@ -4262,6 +4333,25 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4262,6 +4333,25 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4262 var ncrn: usize = 0; // Next Core Register Number4333 var ncrn: usize = 0; // Next Core Register Number
4263 var nsaa: u32 = 0; // Next stacked argument address4334 var nsaa: u32 = 0; // Next stacked argument address
42644335
4336 if (ret_ty.zigTypeTag() == .NoReturn) {
4337 result.return_value = .{ .unreach = {} };
4338 } else if (!ret_ty.hasRuntimeBits()) {
4339 result.return_value = .{ .none = {} };
4340 } else {
4341 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
4342 // TODO handle cases where multiple registers are used
4343 if (ret_ty_size <= 4) {
4344 result.return_value = .{ .register = c_abi_int_return_regs[0] };
4345 } else {
4346 // The result is returned by reference, not by
4347 // value. This means that r0 will contain the
4348 // address of where this function should write the
4349 // result into.
4350 result.return_value = .{ .stack_offset = 0 };
4351 ncrn = 1;
4352 }
4353 }
4354
4265 for (param_types) |ty, i| {4355 for (param_types) |ty, i| {
4266 if (ty.abiAlignment(self.target.*) == 8)4356 if (ty.abiAlignment(self.target.*) == 8)
4267 ncrn = std.mem.alignForwardGeneric(usize, ncrn, 2);4357 ncrn = std.mem.alignForwardGeneric(usize, ncrn, 2);
...@@ -4290,6 +4380,23 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4290,6 +4380,23 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4290 result.stack_align = 8;4380 result.stack_align = 8;
4291 },4381 },
4292 .Unspecified => {4382 .Unspecified => {
4383 if (ret_ty.zigTypeTag() == .NoReturn) {
4384 result.return_value = .{ .unreach = {} };
4385 } else if (!ret_ty.hasRuntimeBits()) {
4386 result.return_value = .{ .none = {} };
4387 } else {
4388 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
4389 if (ret_ty_size <= 4) {
4390 result.return_value = .{ .register = .r0 };
4391 } else {
4392 // The result is returned by reference, not by
4393 // value. This means that r0 will contain the
4394 // address of where this function should write the
4395 // result into.
4396 result.return_value = .{ .stack_offset = 0 };
4397 }
4398 }
4399
4293 var stack_offset: u32 = 0;4400 var stack_offset: u32 = 0;
42944401
4295 for (param_types) |ty, i| {4402 for (param_types) |ty, i| {
...@@ -4308,22 +4415,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4308,22 +4415,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4308 else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),4415 else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
4309 }4416 }
43104417
4311 if (ret_ty.zigTypeTag() == .NoReturn) {
4312 result.return_value = .{ .unreach = {} };
4313 } else if (!ret_ty.hasRuntimeBits()) {
4314 result.return_value = .{ .none = {} };
4315 } else switch (cc) {
4316 .Naked => unreachable,
4317 .Unspecified, .C => {
4318 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
4319 if (ret_ty_size <= 4) {
4320 result.return_value = .{ .register = c_abi_int_return_regs[0] };
4321 } else {
4322 return self.fail("TODO support more return types for ARM backend", .{});
4323 }
4324 },
4325 else => return self.fail("TODO implement function return values for {}", .{cc}),
4326 }
4327 return result;4418 return result;
4328}4419}
43294420
test/behavior/basic.zig-1
...@@ -113,7 +113,6 @@ fn first4KeysOfHomeRow() []const u8 {...@@ -113,7 +113,6 @@ fn first4KeysOfHomeRow() []const u8 {
113113
114test "return string from function" {114test "return string from function" {
115 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;115 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
116 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
117116
118 try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));117 try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
119}118}
test/behavior/struct.zig-1
...@@ -381,7 +381,6 @@ test "align 1 field before self referential align 8 field as slice return type"...@@ -381,7 +381,6 @@ test "align 1 field before self referential align 8 field as slice return type"
381 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;381 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
382 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO382 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
383 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO383 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
384 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
385384
386 const result = alloc(Expr);385 const result = alloc(Expr);
387 try expect(result.len == 0);386 try expect(result.len == 0);