| ... | ... | @@ -340,7 +340,7 @@ pub fn generate( |
| 340 | 340 | .prev_di_line = module_fn.lbrace_line, |
| 341 | 341 | .prev_di_column = module_fn.lbrace_column, |
| 342 | 342 | .stack_size = mem.alignForwardGeneric(u32, function.max_end_stack, function.stack_align), |
| 343 | | .prologue_stack_space = call_info.stack_byte_count + function.saved_regs_stack_space, |
| 343 | .saved_regs_stack_space = function.saved_regs_stack_space, |
| 344 | 344 | }; |
| 345 | 345 | defer emit.deinit(); |
| 346 | 346 | |
| ... | ... | @@ -2317,6 +2317,9 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV |
| 2317 | 2317 | const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); |
| 2318 | 2318 | switch (error_union_mcv) { |
| 2319 | 2319 | .register => return self.fail("TODO errUnionErr for registers", .{}), |
| 2320 | .stack_argument_offset => |off| { |
| 2321 | return MCValue{ .stack_argument_offset = off + err_offset }; |
| 2322 | }, |
| 2320 | 2323 | .stack_offset => |off| { |
| 2321 | 2324 | return MCValue{ .stack_offset = off - err_offset }; |
| 2322 | 2325 | }, |
| ... | ... | @@ -2351,6 +2354,9 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) |
| 2351 | 2354 | const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); |
| 2352 | 2355 | switch (error_union_mcv) { |
| 2353 | 2356 | .register => return self.fail("TODO errUnionPayload for registers", .{}), |
| 2357 | .stack_argument_offset => |off| { |
| 2358 | return MCValue{ .stack_argument_offset = off + payload_offset }; |
| 2359 | }, |
| 2354 | 2360 | .stack_offset => |off| { |
| 2355 | 2361 | return MCValue{ .stack_offset = off - payload_offset }; |
| 2356 | 2362 | }, |
| ... | ... | @@ -3016,7 +3022,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3016 | 3022 | switch (mcv) { |
| 3017 | 3023 | .dead, .unreach => unreachable, |
| 3018 | 3024 | .stack_argument_offset => |off| { |
| 3019 | | break :result MCValue{ .stack_argument_offset = off - struct_field_offset }; |
| 3025 | break :result MCValue{ .stack_argument_offset = off + struct_field_offset }; |
| 3020 | 3026 | }, |
| 3021 | 3027 | .stack_offset => |off| { |
| 3022 | 3028 | break :result MCValue{ .stack_offset = off - struct_field_offset }; |
| ... | ... | @@ -3150,6 +3156,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3150 | 3156 | // saving compare flags may require a new caller-saved register |
| 3151 | 3157 | try self.spillCompareFlagsIfOccupied(); |
| 3152 | 3158 | |
| 3159 | // Make space for the arguments passed via the stack |
| 3160 | self.max_end_stack += info.stack_byte_count; |
| 3161 | |
| 3153 | 3162 | for (info.args) |mc_arg, arg_i| { |
| 3154 | 3163 | const arg = args[arg_i]; |
| 3155 | 3164 | const arg_ty = self.air.typeOf(arg); |
| ... | ... | @@ -3164,7 +3173,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3164 | 3173 | .stack_offset => unreachable, |
| 3165 | 3174 | .stack_argument_offset => |offset| try self.genSetStackArgument( |
| 3166 | 3175 | arg_ty, |
| 3167 | | info.stack_byte_count - offset, |
| 3176 | offset, |
| 3168 | 3177 | arg_mcv, |
| 3169 | 3178 | ), |
| 3170 | 3179 | else => unreachable, |
| ... | ... | @@ -3642,40 +3651,14 @@ fn isNonNull(self: *Self, operand: MCValue) !MCValue { |
| 3642 | 3651 | |
| 3643 | 3652 | fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 3644 | 3653 | const error_type = ty.errorUnionSet(); |
| 3645 | | const payload_type = ty.errorUnionPayload(); |
| 3654 | const error_int_type = Type.initTag(.u16); |
| 3646 | 3655 | |
| 3647 | 3656 | if (error_type.errorSetIsEmpty()) { |
| 3648 | 3657 | return MCValue{ .immediate = 0 }; // always false |
| 3649 | 3658 | } |
| 3650 | 3659 | |
| 3651 | | const err_off = errUnionErrorOffset(payload_type, self.target.*); |
| 3652 | | switch (operand) { |
| 3653 | | .stack_offset => |off| { |
| 3654 | | const offset = off - @intCast(u32, err_off); |
| 3655 | | const tmp_reg = try self.copyToTmpRegister(Type.anyerror, .{ .stack_offset = offset }); |
| 3656 | | _ = try self.addInst(.{ |
| 3657 | | .tag = .cmp_immediate, |
| 3658 | | .data = .{ .r_imm12_sh = .{ |
| 3659 | | .rn = tmp_reg, |
| 3660 | | .imm12 = 0, |
| 3661 | | } }, |
| 3662 | | }); |
| 3663 | | }, |
| 3664 | | .register => |reg| { |
| 3665 | | if (err_off > 0 or payload_type.hasRuntimeBitsIgnoreComptime()) { |
| 3666 | | return self.fail("TODO implement isErr for register operand with payload bits", .{}); |
| 3667 | | } |
| 3668 | | _ = try self.addInst(.{ |
| 3669 | | .tag = .cmp_immediate, |
| 3670 | | .data = .{ .r_imm12_sh = .{ |
| 3671 | | .rn = reg, |
| 3672 | | .imm12 = 0, |
| 3673 | | } }, |
| 3674 | | }); |
| 3675 | | }, |
| 3676 | | else => return self.fail("TODO implement isErr for {}", .{operand}), |
| 3677 | | } |
| 3678 | | |
| 3660 | const error_mcv = try self.errUnionErr(operand, ty); |
| 3661 | _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null); |
| 3679 | 3662 | return MCValue{ .condition_flags = .hi }; |
| 3680 | 3663 | } |
| 3681 | 3664 | |
| ... | ... | @@ -4174,6 +4157,15 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4174 | 4157 | // sub src_reg, fp, #off |
| 4175 | 4158 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); |
| 4176 | 4159 | }, |
| 4160 | .stack_argument_offset => |off| { |
| 4161 | _ = try self.addInst(.{ |
| 4162 | .tag = .ldr_ptr_stack_argument, |
| 4163 | .data = .{ .load_store_stack = .{ |
| 4164 | .rt = src_reg, |
| 4165 | .offset = off, |
| 4166 | } }, |
| 4167 | }); |
| 4168 | }, |
| 4177 | 4169 | .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }), |
| 4178 | 4170 | .got_load, |
| 4179 | 4171 | .direct_load, |
| ... | ... | @@ -4433,7 +4425,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4433 | 4425 | } |
| 4434 | 4426 | }, |
| 4435 | 4427 | .register_with_overflow => { |
| 4436 | | return self.fail("TODO implement genSetStack {}", .{mcv}); |
| 4428 | return self.fail("TODO implement genSetStackArgument {}", .{mcv}); |
| 4437 | 4429 | }, |
| 4438 | 4430 | .got_load, |
| 4439 | 4431 | .direct_load, |
| ... | ... | @@ -4469,6 +4461,15 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4469 | 4461 | // sub src_reg, fp, #off |
| 4470 | 4462 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); |
| 4471 | 4463 | }, |
| 4464 | .stack_argument_offset => |off| { |
| 4465 | _ = try self.addInst(.{ |
| 4466 | .tag = .ldr_ptr_stack_argument, |
| 4467 | .data = .{ .load_store_stack = .{ |
| 4468 | .rt = src_reg, |
| 4469 | .offset = off, |
| 4470 | } }, |
| 4471 | }); |
| 4472 | }, |
| 4472 | 4473 | .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), |
| 4473 | 4474 | .got_load, |
| 4474 | 4475 | .direct_load, |
| ... | ... | @@ -4490,7 +4491,6 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 4490 | 4491 | }, |
| 4491 | 4492 | }); |
| 4492 | 4493 | }, |
| 4493 | | .stack_argument_offset => return self.fail("TODO load {}", .{mcv}), |
| 4494 | 4494 | else => unreachable, |
| 4495 | 4495 | } |
| 4496 | 4496 | |
| ... | ... | @@ -4989,11 +4989,27 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 4989 | 4989 | result.stack_align = 1; |
| 4990 | 4990 | return result; |
| 4991 | 4991 | }, |
| 4992 | | .Unspecified, .C => { |
| 4992 | .C => { |
| 4993 | 4993 | // ARM64 Procedure Call Standard |
| 4994 | 4994 | var ncrn: usize = 0; // Next Core Register Number |
| 4995 | 4995 | var nsaa: u32 = 0; // Next stacked argument address |
| 4996 | 4996 | |
| 4997 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 4998 | result.return_value = .{ .unreach = {} }; |
| 4999 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 5000 | result.return_value = .{ .none = {} }; |
| 5001 | } else { |
| 5002 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 5003 | if (ret_ty_size == 0) { |
| 5004 | assert(ret_ty.isError()); |
| 5005 | result.return_value = .{ .immediate = 0 }; |
| 5006 | } else if (ret_ty_size <= 8) { |
| 5007 | result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; |
| 5008 | } else { |
| 5009 | return self.fail("TODO support more return types for ARM backend", .{}); |
| 5010 | } |
| 5011 | } |
| 5012 | |
| 4997 | 5013 | for (param_types) |ty, i| { |
| 4998 | 5014 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 4999 | 5015 | if (param_size == 0) { |
| ... | ... | @@ -5027,36 +5043,52 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5027 | 5043 | } |
| 5028 | 5044 | } |
| 5029 | 5045 | |
| 5030 | | nsaa += param_size; |
| 5031 | 5046 | result.args[i] = .{ .stack_argument_offset = nsaa }; |
| 5047 | nsaa += param_size; |
| 5032 | 5048 | } |
| 5033 | 5049 | } |
| 5034 | 5050 | |
| 5035 | 5051 | result.stack_byte_count = nsaa; |
| 5036 | 5052 | result.stack_align = 16; |
| 5037 | 5053 | }, |
| 5038 | | else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}), |
| 5039 | | } |
| 5040 | | |
| 5041 | | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 5042 | | result.return_value = .{ .unreach = {} }; |
| 5043 | | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 5044 | | result.return_value = .{ .none = {} }; |
| 5045 | | } else switch (cc) { |
| 5046 | | .Naked => unreachable, |
| 5047 | | .Unspecified, .C => { |
| 5048 | | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 5049 | | if (ret_ty_size == 0) { |
| 5050 | | assert(ret_ty.isError()); |
| 5051 | | result.return_value = .{ .immediate = 0 }; |
| 5052 | | } else if (ret_ty_size <= 8) { |
| 5053 | | result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; |
| 5054 | .Unspecified => { |
| 5055 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 5056 | result.return_value = .{ .unreach = {} }; |
| 5057 | } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { |
| 5058 | result.return_value = .{ .none = {} }; |
| 5054 | 5059 | } else { |
| 5055 | | return self.fail("TODO support more return types for ARM backend", .{}); |
| 5060 | const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| 5061 | if (ret_ty_size == 0) { |
| 5062 | assert(ret_ty.isError()); |
| 5063 | result.return_value = .{ .immediate = 0 }; |
| 5064 | } else if (ret_ty_size <= 8) { |
| 5065 | result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; |
| 5066 | } else { |
| 5067 | return self.fail("TODO support more return types for ARM backend", .{}); |
| 5068 | } |
| 5056 | 5069 | } |
| 5070 | |
| 5071 | var stack_offset: u32 = 0; |
| 5072 | |
| 5073 | for (param_types) |ty, i| { |
| 5074 | if (ty.abiSize(self.target.*) > 0) { |
| 5075 | const param_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 5076 | const param_alignment = ty.abiAlignment(self.target.*); |
| 5077 | |
| 5078 | stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, param_alignment); |
| 5079 | result.args[i] = .{ .stack_argument_offset = stack_offset }; |
| 5080 | stack_offset += param_size; |
| 5081 | } else { |
| 5082 | result.args[i] = .{ .none = {} }; |
| 5083 | } |
| 5084 | } |
| 5085 | |
| 5086 | result.stack_byte_count = stack_offset; |
| 5087 | result.stack_align = 16; |
| 5057 | 5088 | }, |
| 5058 | | else => return self.fail("TODO implement function return values for {}", .{cc}), |
| 5089 | else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}), |
| 5059 | 5090 | } |
| 5091 | |
| 5060 | 5092 | return result; |
| 5061 | 5093 | } |
| 5062 | 5094 | |