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 {
387387 // sub sp, sp, #reloc
388388 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
390402 _ = try self.addInst(.{
391403 .tag = .dbg_prologue_end,
392404 .cond = undefined,
......@@ -1014,6 +1026,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
10141026
10151027 break :result MCValue{ .register = dest_reg };
10161028 },
1029 .Vector => return self.fail("TODO bitwise not for vectors", .{}),
10171030 .Int => {
10181031 const int_info = operand_ty.intInfo(self.target.*);
10191032 if (int_info.bits <= 32) {
......@@ -1666,6 +1679,8 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
16661679}
16671680
16681681fn 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
16691684 switch (ptr) {
16701685 .none => unreachable,
16711686 .undef => unreachable,
......@@ -1702,7 +1717,30 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
17021717 try self.genSetReg(value_ty, tmp_reg, value);
17031718 try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
17041719 } 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);
17061744 }
17071745 },
17081746 }
......@@ -2408,9 +2446,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
24082446 const mcv = switch (result) {
24092447 // Copy registers to the stack
24102448 .register => |reg| blk: {
2411 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {
2412 return self.fail("type '{}' too big to fit into stack frame", .{ty});
2413 };
2449 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
24142450 const abi_align = ty.abiAlignment(self.target.*);
24152451 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
24162452 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
......@@ -2491,95 +2527,113 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
24912527 // saving compare flags may require a new caller-saved register
24922528 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
24942547 // Make space for the arguments passed via the stack
24952548 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
24972580 // Due to incremental compilation, how function calls are generated depends
24982581 // on linking.
2499 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
2500 for (info.args) |mc_arg, arg_i| {
2501 const arg = args[arg_i];
2502 const arg_ty = self.air.typeOf(arg);
2503 const arg_mcv = try self.resolveInst(args[arg_i]);
2504
2505 switch (mc_arg) {
2506 .none => continue,
2507 .undef => unreachable,
2508 .immediate => unreachable,
2509 .unreach => unreachable,
2510 .dead => unreachable,
2511 .embedded_in_code => unreachable,
2512 .memory => unreachable,
2513 .compare_flags_signed => unreachable,
2514 .compare_flags_unsigned => unreachable,
2515 .ptr_stack_offset => unreachable,
2516 .ptr_embedded_in_code => unreachable,
2517 .register => |reg| {
2518 try self.register_manager.getReg(reg, null);
2519 try self.genSetReg(arg_ty, reg, arg_mcv);
2520 },
2521 .stack_offset => unreachable,
2522 .stack_argument_offset => |offset| try self.genSetStackArgument(
2523 arg_ty,
2524 info.stack_byte_count - offset,
2525 arg_mcv,
2526 ),
2582 switch (self.bin_file.tag) {
2583 .elf, .coff => {
2584 if (self.air.value(callee)) |func_value| {
2585 if (func_value.castTag(.function)) |func_payload| {
2586 const func = func_payload.data;
2587 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2588 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2589 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
2590 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
2591 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
2592 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
2593 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
2594 else
2595 unreachable;
2596
2597 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
2598 } else if (func_value.castTag(.extern_fn)) |_| {
2599 return self.fail("TODO implement calling extern functions", .{});
2600 } else {
2601 return self.fail("TODO implement calling bitcasted functions", .{});
2602 }
2603 } else {
2604 assert(ty.zigTypeTag() == .Pointer);
2605 const mcv = try self.resolveInst(callee);
2606
2607 try self.genSetReg(Type.initTag(.usize), .lr, mcv);
25272608 }
2528 }
25292609
2530 if (self.air.value(callee)) |func_value| {
2531 if (func_value.castTag(.function)) |func_payload| {
2532 const func = func_payload.data;
2533 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2534 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2535 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
2536 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
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", .{});
2610 // TODO: add Instruction.supportedOn
2611 // function for ARM
2612 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
2613 _ = try self.addInst(.{
2614 .tag = .blx,
2615 .data = .{ .reg = .lr },
2616 });
25462617 } 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 // });
25482631 }
2549 } else {
2550 assert(ty.zigTypeTag() == .Pointer);
2551 const mcv = try self.resolveInst(callee);
2552
2553 try self.genSetReg(Type.initTag(.usize), .lr, mcv);
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;
2632 },
2633 .macho => unreachable, // unsupported architecture for MachO
2634 .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
2635 else => unreachable,
2636 }
25832637
25842638 const result: MCValue = result: {
25852639 switch (info.return_value) {
......@@ -2610,7 +2664,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
26102664
26112665fn ret(self: *Self, mcv: MCValue) !void {
26122666 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
26152688 // Just add space for an instruction, patch this later
26162689 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
35243597 });
35253598 },
35263599 .immediate => |x| {
3527 if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{});
3528
3529 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {
3600 if (Instruction.Operand.fromU32(x)) |op| {
35303601 _ = try self.addInst(.{
35313602 .tag = .mov,
35323603 .data = .{ .rr_op = .{
......@@ -3535,7 +3606,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
35353606 .op = op,
35363607 } },
35373608 });
3538 } else if (Instruction.Operand.fromU32(~@intCast(u32, x))) |op| {
3609 } else if (Instruction.Operand.fromU32(~x)) |op| {
35393610 _ = try self.addInst(.{
35403611 .tag = .mvn,
35413612 .data = .{ .rr_op = .{
......@@ -4262,6 +4333,25 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
42624333 var ncrn: usize = 0; // Next Core Register Number
42634334 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
42654355 for (param_types) |ty, i| {
42664356 if (ty.abiAlignment(self.target.*) == 8)
42674357 ncrn = std.mem.alignForwardGeneric(usize, ncrn, 2);
......@@ -4290,6 +4380,23 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
42904380 result.stack_align = 8;
42914381 },
42924382 .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
42934400 var stack_offset: u32 = 0;
42944401
42954402 for (param_types) |ty, i| {
......@@ -4308,22 +4415,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
43084415 else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
43094416 }
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 }
43274418 return result;
43284419}
43294420
test/behavior/basic.zig-1
......@@ -113,7 +113,6 @@ fn first4KeysOfHomeRow() []const u8 {
113113
114114test "return string from function" {
115115 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
116 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
117116
118117 try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
119118}
test/behavior/struct.zig-1
......@@ -381,7 +381,6 @@ test "align 1 field before self referential align 8 field as slice return type"
381381 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
382382 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
383383 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
384 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
385384
386385 const result = alloc(Expr);
387386 try expect(result.len == 0);