| ... | @@ -418,6 +418,23 @@ fn gen(self: *Self) !void { | ... | @@ -418,6 +418,23 @@ fn gen(self: *Self) !void { |
| 418 | // sub sp, sp, #reloc | 418 | // sub sp, sp, #reloc |
| 419 | const backpatch_reloc = try self.addNop(); | 419 | const backpatch_reloc = try self.addNop(); |
| 420 | | 420 | |
| | 421 | if (self.ret_mcv == .stack_offset) { |
| | 422 | // The address of where to store the return value is in x0 |
| | 423 | // (or w0 when pointer size is 32 bits). As this register |
| | 424 | // might get overwritten along the way, save the address |
| | 425 | // to the stack. |
| | 426 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| | 427 | const ptr_bytes = @divExact(ptr_bits, 8); |
| | 428 | const ret_ptr_reg = registerAlias(.x0, ptr_bytes); |
| | 429 | |
| | 430 | const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, ptr_bytes) + ptr_bytes; |
| | 431 | self.next_stack_offset = stack_offset; |
| | 432 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); |
| | 433 | |
| | 434 | try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg }); |
| | 435 | self.ret_mcv = MCValue{ .stack_offset = stack_offset }; |
| | 436 | } |
| | 437 | |
| 421 | _ = try self.addInst(.{ | 438 | _ = try self.addInst(.{ |
| 422 | .tag = .dbg_prologue_end, | 439 | .tag = .dbg_prologue_end, |
| 423 | .data = .{ .nop = {} }, | 440 | .data = .{ .nop = {} }, |
| ... | @@ -2446,21 +2463,28 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2446,21 +2463,28 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2446 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 2463 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2447 | } | 2464 | } |
| 2448 | | 2465 | |
| | 2466 | fn slicePtr(mcv: MCValue) MCValue { |
| | 2467 | switch (mcv) { |
| | 2468 | .dead, .unreach, .none => unreachable, |
| | 2469 | .register => unreachable, // a slice doesn't fit in one register |
| | 2470 | .stack_argument_offset => |off| { |
| | 2471 | return MCValue{ .stack_argument_offset = off }; |
| | 2472 | }, |
| | 2473 | .stack_offset => |off| { |
| | 2474 | return MCValue{ .stack_offset = off }; |
| | 2475 | }, |
| | 2476 | .memory => |addr| { |
| | 2477 | return MCValue{ .memory = addr }; |
| | 2478 | }, |
| | 2479 | else => unreachable, // invalid MCValue for a slice |
| | 2480 | } |
| | 2481 | } |
| | 2482 | |
| 2449 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { | 2483 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2450 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2484 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2451 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { | 2485 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2452 | const mcv = try self.resolveInst(ty_op.operand); | 2486 | const mcv = try self.resolveInst(ty_op.operand); |
| 2453 | switch (mcv) { | 2487 | break :result slicePtr(mcv); |
| 2454 | .dead, .unreach, .none => unreachable, | | |
| 2455 | .register => unreachable, // a slice doesn't fit in one register | | |
| 2456 | .stack_offset => |off| { | | |
| 2457 | break :result MCValue{ .stack_offset = off }; | | |
| 2458 | }, | | |
| 2459 | .memory => |addr| { | | |
| 2460 | break :result MCValue{ .memory = addr }; | | |
| 2461 | }, | | |
| 2462 | else => return self.fail("TODO implement slice_len for {}", .{mcv}), | | |
| 2463 | } | | |
| 2464 | }; | 2488 | }; |
| 2465 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 2489 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2466 | } | 2490 | } |
| ... | @@ -2474,6 +2498,9 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2474,6 +2498,9 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 2474 | switch (mcv) { | 2498 | switch (mcv) { |
| 2475 | .dead, .unreach, .none => unreachable, | 2499 | .dead, .unreach, .none => unreachable, |
| 2476 | .register => unreachable, // a slice doesn't fit in one register | 2500 | .register => unreachable, // a slice doesn't fit in one register |
| | 2501 | .stack_argument_offset => |off| { |
| | 2502 | break :result MCValue{ .stack_argument_offset = off + ptr_bytes }; |
| | 2503 | }, |
| 2477 | .stack_offset => |off| { | 2504 | .stack_offset => |off| { |
| 2478 | break :result MCValue{ .stack_offset = off - ptr_bytes }; | 2505 | break :result MCValue{ .stack_offset = off - ptr_bytes }; |
| 2479 | }, | 2506 | }, |
| ... | @@ -2524,6 +2551,9 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2524,6 +2551,9 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2524 | | 2551 | |
| 2525 | if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); | 2552 | if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2526 | const result: MCValue = result: { | 2553 | const result: MCValue = result: { |
| | 2554 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| | 2555 | const elem_ty = slice_ty.childType(); |
| | 2556 | const elem_size = elem_ty.abiSize(self.target.*); |
| 2527 | const slice_mcv = try self.resolveInst(bin_op.lhs); | 2557 | const slice_mcv = try self.resolveInst(bin_op.lhs); |
| 2528 | | 2558 | |
| 2529 | // TODO optimize for the case where the index is a constant, | 2559 | // TODO optimize for the case where the index is a constant, |
| ... | @@ -2531,10 +2561,6 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2531,10 +2561,6 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2531 | const index_mcv = try self.resolveInst(bin_op.rhs); | 2561 | const index_mcv = try self.resolveInst(bin_op.rhs); |
| 2532 | const index_is_register = index_mcv == .register; | 2562 | const index_is_register = index_mcv == .register; |
| 2533 | | 2563 | |
| 2534 | const slice_ty = self.air.typeOf(bin_op.lhs); | | |
| 2535 | const elem_ty = slice_ty.childType(); | | |
| 2536 | const elem_size = elem_ty.abiSize(self.target.*); | | |
| 2537 | | | |
| 2538 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 2564 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 2539 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); | 2565 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); |
| 2540 | | 2566 | |
| ... | @@ -2544,15 +2570,17 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2544,15 +2570,17 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2544 | null; | 2570 | null; |
| 2545 | defer if (index_lock) |reg| self.register_manager.unlockReg(reg); | 2571 | defer if (index_lock) |reg| self.register_manager.unlockReg(reg); |
| 2546 | | 2572 | |
| 2547 | const base_mcv: MCValue = switch (slice_mcv) { | 2573 | const base_mcv = slicePtr(slice_mcv); |
| 2548 | .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) }, | | |
| 2549 | else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), | | |
| 2550 | }; | | |
| 2551 | const base_lock = self.register_manager.lockRegAssumeUnused(base_mcv.register); | | |
| 2552 | defer self.register_manager.unlockReg(base_lock); | | |
| 2553 | | 2574 | |
| 2554 | switch (elem_size) { | 2575 | switch (elem_size) { |
| 2555 | else => { | 2576 | else => { |
| | 2577 | const base_reg = switch (base_mcv) { |
| | 2578 | .register => |r| r, |
| | 2579 | else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv), |
| | 2580 | }; |
| | 2581 | const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg); |
| | 2582 | defer self.register_manager.unlockReg(base_reg_lock); |
| | 2583 | |
| 2556 | const dest = try self.allocRegOrMem(inst, true); | 2584 | const dest = try self.allocRegOrMem(inst, true); |
| 2557 | const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); | 2585 | const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); |
| 2558 | try self.load(dest, addr, slice_ptr_field_type); | 2586 | try self.load(dest, addr, slice_ptr_field_type); |
| ... | @@ -2567,7 +2595,16 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2567,7 +2595,16 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2567 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { | 2595 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2568 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 2596 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2569 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 2597 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2570 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch}); | 2598 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| | 2599 | const slice_mcv = try self.resolveInst(extra.lhs); |
| | 2600 | const index_mcv = try self.resolveInst(extra.rhs); |
| | 2601 | const base_mcv = slicePtr(slice_mcv); |
| | 2602 | |
| | 2603 | const slice_ty = self.air.typeOf(extra.lhs); |
| | 2604 | |
| | 2605 | const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null); |
| | 2606 | break :result addr; |
| | 2607 | }; |
| 2571 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | 2608 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 2572 | } | 2609 | } |
| 2573 | | 2610 | |
| ... | @@ -3156,6 +3193,28 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. | ... | @@ -3156,6 +3193,28 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. |
| 3156 | // saving compare flags may require a new caller-saved register | 3193 | // saving compare flags may require a new caller-saved register |
| 3157 | try self.spillCompareFlagsIfOccupied(); | 3194 | try self.spillCompareFlagsIfOccupied(); |
| 3158 | | 3195 | |
| | 3196 | if (info.return_value == .stack_offset) { |
| | 3197 | log.debug("airCall: return by reference", .{}); |
| | 3198 | const ret_ty = fn_ty.fnReturnType(); |
| | 3199 | const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| | 3200 | const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); |
| | 3201 | const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align); |
| | 3202 | |
| | 3203 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| | 3204 | const ptr_bytes = @divExact(ptr_bits, 8); |
| | 3205 | const ret_ptr_reg = registerAlias(.x0, ptr_bytes); |
| | 3206 | |
| | 3207 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| | 3208 | .base = .{ .tag = .single_mut_pointer }, |
| | 3209 | .data = ret_ty, |
| | 3210 | }; |
| | 3211 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| | 3212 | try self.register_manager.getReg(ret_ptr_reg, null); |
| | 3213 | try self.genSetReg(ptr_ty, ret_ptr_reg, .{ .ptr_stack_offset = stack_offset }); |
| | 3214 | |
| | 3215 | info.return_value = .{ .stack_offset = stack_offset }; |
| | 3216 | } |
| | 3217 | |
| 3159 | // Make space for the arguments passed via the stack | 3218 | // Make space for the arguments passed via the stack |
| 3160 | self.max_end_stack += info.stack_byte_count; | 3219 | self.max_end_stack += info.stack_byte_count; |
| 3161 | | 3220 | |
| ... | @@ -3319,8 +3378,15 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3319,8 +3378,15 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| 3319 | }, | 3378 | }, |
| 3320 | .stack_offset => { | 3379 | .stack_offset => { |
| 3321 | // Return result by reference | 3380 | // Return result by reference |
| 3322 | // TODO | 3381 | // |
| 3323 | return self.fail("TODO implement airRet for {}", .{self.ret_mcv}); | 3382 | // self.ret_mcv is an address to where this function |
| | 3383 | // should store its result into |
| | 3384 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| | 3385 | .base = .{ .tag = .single_mut_pointer }, |
| | 3386 | .data = ret_ty, |
| | 3387 | }; |
| | 3388 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| | 3389 | try self.store(self.ret_mcv, operand, ptr_ty, ret_ty); |
| 3324 | }, | 3390 | }, |
| 3325 | else => unreachable, | 3391 | else => unreachable, |
| 3326 | } | 3392 | } |
| ... | @@ -3346,10 +3412,34 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3346,10 +3412,34 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3346 | }, | 3412 | }, |
| 3347 | .stack_offset => { | 3413 | .stack_offset => { |
| 3348 | // Return result by reference | 3414 | // Return result by reference |
| 3349 | // TODO | 3415 | // |
| 3350 | return self.fail("TODO implement airRetLoad for {}", .{self.ret_mcv}); | 3416 | // self.ret_mcv is an address to where this function |
| | 3417 | // should store its result into |
| | 3418 | // |
| | 3419 | // If the operand is a ret_ptr instruction, we are done |
| | 3420 | // here. Else we need to load the result from the location |
| | 3421 | // pointed to by the operand and store it to the result |
| | 3422 | // location. |
| | 3423 | const op_inst = Air.refToIndex(un_op).?; |
| | 3424 | if (self.air.instructions.items(.tag)[op_inst] != .ret_ptr) { |
| | 3425 | const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); |
| | 3426 | const abi_align = ret_ty.abiAlignment(self.target.*); |
| | 3427 | |
| | 3428 | // This is essentially allocMem without the |
| | 3429 | // instruction tracking |
| | 3430 | if (abi_align > self.stack_align) |
| | 3431 | self.stack_align = abi_align; |
| | 3432 | // TODO find a free slot instead of always appending |
| | 3433 | const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; |
| | 3434 | self.next_stack_offset = offset; |
| | 3435 | self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); |
| | 3436 | |
| | 3437 | const tmp_mcv = MCValue{ .stack_offset = offset }; |
| | 3438 | try self.load(tmp_mcv, ptr, ptr_ty); |
| | 3439 | try self.store(self.ret_mcv, tmp_mcv, ptr_ty, ret_ty); |
| | 3440 | } |
| 3351 | }, | 3441 | }, |
| 3352 | else => unreachable, | 3442 | else => unreachable, // invalid return result |
| 3353 | } | 3443 | } |
| 3354 | | 3444 | |
| 3355 | try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); | 3445 | try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); |
| ... | @@ -5062,9 +5152,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { | ... | @@ -5062,9 +5152,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 5062 | assert(ret_ty.isError()); | 5152 | assert(ret_ty.isError()); |
| 5063 | result.return_value = .{ .immediate = 0 }; | 5153 | result.return_value = .{ .immediate = 0 }; |
| 5064 | } else if (ret_ty_size <= 8) { | 5154 | } else if (ret_ty_size <= 8) { |
| 5065 | result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; | 5155 | result.return_value = .{ .register = registerAlias(.x0, ret_ty_size) }; |
| 5066 | } else { | 5156 | } else { |
| 5067 | return self.fail("TODO support more return types for ARM backend", .{}); | 5157 | // The result is returned by reference, not by |
| | 5158 | // value. This means that x0 (or w0 when pointer |
| | 5159 | // size is 32 bits) will contain the address of |
| | 5160 | // where this function should write the result |
| | 5161 | // into. |
| | 5162 | result.return_value = .{ .stack_offset = 0 }; |
| 5068 | } | 5163 | } |
| 5069 | } | 5164 | } |
| 5070 | | 5165 | |