| ... | ... | @@ -387,6 +387,18 @@ fn gen(self: *Self) !void { |
| 387 | 387 | // sub sp, sp, #reloc |
| 388 | 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 | 402 | _ = try self.addInst(.{ |
| 391 | 403 | .tag = .dbg_prologue_end, |
| 392 | 404 | .cond = undefined, |
| ... | ... | @@ -1014,6 +1026,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1014 | 1026 | |
| 1015 | 1027 | break :result MCValue{ .register = dest_reg }; |
| 1016 | 1028 | }, |
| 1029 | .Vector => return self.fail("TODO bitwise not for vectors", .{}), |
| 1017 | 1030 | .Int => { |
| 1018 | 1031 | const int_info = operand_ty.intInfo(self.target.*); |
| 1019 | 1032 | if (int_info.bits <= 32) { |
| ... | ... | @@ -1666,6 +1679,8 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 1666 | 1679 | } |
| 1667 | 1680 | |
| 1668 | 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 | 1684 | switch (ptr) { |
| 1670 | 1685 | .none => unreachable, |
| 1671 | 1686 | .undef => unreachable, |
| ... | ... | @@ -1702,7 +1717,30 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 1702 | 1717 | try self.genSetReg(value_ty, tmp_reg, value); |
| 1703 | 1718 | try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); |
| 1704 | 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 | 2446 | const mcv = switch (result) { |
| 2409 | 2447 | // Copy registers to the stack |
| 2410 | 2448 | .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.*)); |
| 2414 | 2450 | const abi_align = ty.abiAlignment(self.target.*); |
| 2415 | 2451 | const stack_offset = try self.allocMem(inst, abi_size, abi_align); |
| 2416 | 2452 | try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| ... | ... | @@ -2491,95 +2527,113 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2491 | 2527 | // saving compare flags may require a new caller-saved register |
| 2492 | 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 | 2547 | // Make space for the arguments passed via the stack |
| 2495 | 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 | 2580 | // Due to incremental compilation, how function calls are generated depends |
| 2498 | 2581 | // 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); |
| 2527 | 2608 | } |
| 2528 | | } |
| 2529 | 2609 | |
| 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 | }); |
| 2546 | 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 { |
| 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 | } |
| 2583 | 2637 | |
| 2584 | 2638 | const result: MCValue = result: { |
| 2585 | 2639 | switch (info.return_value) { |
| ... | ... | @@ -2610,7 +2664,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2610 | 2664 | |
| 2611 | 2665 | fn ret(self: *Self, mcv: MCValue) !void { |
| 2612 | 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 | 2688 | // Just add space for an instruction, patch this later |
| 2616 | 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 | 3597 | }); |
| 3525 | 3598 | }, |
| 3526 | 3599 | .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| { |
| 3530 | 3601 | _ = try self.addInst(.{ |
| 3531 | 3602 | .tag = .mov, |
| 3532 | 3603 | .data = .{ .rr_op = .{ |
| ... | ... | @@ -3535,7 +3606,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3535 | 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 | 3610 | _ = try self.addInst(.{ |
| 3540 | 3611 | .tag = .mvn, |
| 3541 | 3612 | .data = .{ .rr_op = .{ |
| ... | ... | @@ -4262,6 +4333,25 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4262 | 4333 | var ncrn: usize = 0; // Next Core Register Number |
| 4263 | 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 | 4355 | for (param_types) |ty, i| { |
| 4266 | 4356 | if (ty.abiAlignment(self.target.*) == 8) |
| 4267 | 4357 | ncrn = std.mem.alignForwardGeneric(usize, ncrn, 2); |
| ... | ... | @@ -4290,6 +4380,23 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4290 | 4380 | result.stack_align = 8; |
| 4291 | 4381 | }, |
| 4292 | 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 | 4400 | var stack_offset: u32 = 0; |
| 4294 | 4401 | |
| 4295 | 4402 | for (param_types) |ty, i| { |
| ... | ... | @@ -4308,22 +4415,6 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4308 | 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 | 4418 | return result; |
| 4328 | 4419 | } |
| 4329 | 4420 | |