| ... | @@ -387,6 +387,18 @@ fn gen(self: *Self) !void { | ... | @@ -387,6 +387,18 @@ fn gen(self: *Self) !void { |
| 387 | // sub sp, sp, #reloc | 387 | // sub sp, sp, #reloc |
| 388 | const sub_reloc = try self.addNop(); | 388 | const sub_reloc = try self.addNop(); |
| 389 | | 389 | |
| | 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 { |
| 1014 | | 1026 | |
| 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 | } |
| 1667 | | 1680 | |
| 1668 | fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { | 1681 | fn 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 stack | 2447 | // 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 register | 2527 | // saving compare flags may require a new caller-saved register |
| 2492 | try self.spillCompareFlagsIfOccupied(); | 2528 | try self.spillCompareFlagsIfOccupied(); |
| 2493 | | 2529 | |
| | 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 stack | 2547 | // 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; |
| 2496 | | 2549 | |
| | 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 depends | 2580 | // 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; |
| 2504 | | 2587 | 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 | } | | |
| 2529 | | 2609 | |
| 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}), |
| 2552 | | 2635 | 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; | | |
| 2583 | | 2637 | |
| 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 { |
| 2610 | | 2664 | |
| 2611 | fn ret(self: *Self, mcv: MCValue) !void { | 2665 | fn 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 | } |
| 2614 | | 2687 | |
| 2615 | // Just add space for an instruction, patch this later | 2688 | // 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 Number | 4333 | var ncrn: usize = 0; // Next Core Register Number |
| 4263 | var nsaa: u32 = 0; // Next stacked argument address | 4334 | var nsaa: u32 = 0; // Next stacked argument address |
| 4264 | | 4335 | |
| | 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; |
| 4294 | | 4401 | |
| 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 | } |
| 4310 | | 4417 | |
| 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 | } |
| 4329 | | 4420 | |