| ... | ... | @@ -418,6 +418,23 @@ fn gen(self: *Self) !void { |
| 418 | 418 | // sub sp, sp, #reloc |
| 419 | 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 | 438 | _ = try self.addInst(.{ |
| 422 | 439 | .tag = .dbg_prologue_end, |
| 423 | 440 | .data = .{ .nop = {} }, |
| ... | ... | @@ -2446,21 +2463,28 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2446 | 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 | 2483 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2450 | 2484 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2451 | 2485 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2452 | 2486 | const mcv = try self.resolveInst(ty_op.operand); |
| 2453 | | switch (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 | | } |
| 2487 | break :result slicePtr(mcv); |
| 2464 | 2488 | }; |
| 2465 | 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 | 2498 | switch (mcv) { |
| 2475 | 2499 | .dead, .unreach, .none => unreachable, |
| 2476 | 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 | 2504 | .stack_offset => |off| { |
| 2478 | 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 | 2551 | |
| 2525 | 2552 | if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2526 | 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 | 2557 | const slice_mcv = try self.resolveInst(bin_op.lhs); |
| 2528 | 2558 | |
| 2529 | 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 | 2561 | const index_mcv = try self.resolveInst(bin_op.rhs); |
| 2532 | 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 | 2564 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 2539 | 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 | 2570 | null; |
| 2545 | 2571 | defer if (index_lock) |reg| self.register_manager.unlockReg(reg); |
| 2546 | 2572 | |
| 2547 | | const base_mcv: MCValue = switch (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); |
| 2573 | const base_mcv = slicePtr(slice_mcv); |
| 2553 | 2574 | |
| 2554 | 2575 | switch (elem_size) { |
| 2555 | 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 | 2584 | const dest = try self.allocRegOrMem(inst, true); |
| 2557 | 2585 | const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null); |
| 2558 | 2586 | try self.load(dest, addr, slice_ptr_field_type); |
| ... | ... | @@ -2567,7 +2595,16 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2567 | 2595 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2568 | 2596 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2569 | 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 | 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 | 3193 | // saving compare flags may require a new caller-saved register |
| 3157 | 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 | 3218 | // Make space for the arguments passed via the stack |
| 3160 | 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 | 3378 | }, |
| 3320 | 3379 | .stack_offset => { |
| 3321 | 3380 | // Return result by reference |
| 3322 | | // TODO |
| 3323 | | return self.fail("TODO implement airRet for {}", .{self.ret_mcv}); |
| 3381 | // |
| 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 | 3391 | else => unreachable, |
| 3326 | 3392 | } |
| ... | ... | @@ -3346,10 +3412,34 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3346 | 3412 | }, |
| 3347 | 3413 | .stack_offset => { |
| 3348 | 3414 | // Return result by reference |
| 3349 | | // TODO |
| 3350 | | return self.fail("TODO implement airRetLoad for {}", .{self.ret_mcv}); |
| 3415 | // |
| 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 | 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 | 5152 | assert(ret_ty.isError()); |
| 5063 | 5153 | result.return_value = .{ .immediate = 0 }; |
| 5064 | 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 | 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 | |