authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-08-04 09:33:04+02:00
committergravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2022-08-05 20:30:51+02:00
log02738228f24b807c45f9ae7b3b1f3657668aed27
treef6a6a42d3317b15cedebd438bb927bb6b33445e0
parent65b3c27f2457f3d957d83edf13e20e41e84f6dd4
signature Commit is signed but in an unrecognized format.

stage2 AArch64: support returning values by reference

also adds some more support for slices passed as stack arguments

1 files changed, 124 insertions(+), 29 deletions(-)

src/arch/aarch64/CodeGen.zig+124-29
...@@ -418,6 +418,23 @@ fn gen(self: *Self) !void {...@@ -418,6 +418,23 @@ fn gen(self: *Self) !void {
418 // sub sp, sp, #reloc418 // sub sp, sp, #reloc
419 const backpatch_reloc = try self.addNop();419 const backpatch_reloc = try self.addNop();
420420
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}
24482465
2466fn 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
2449fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {2483fn 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 register2500 .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 {
25242551
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);
25282558
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;
25332563
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);
25402566
...@@ -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);
25462572
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);
25532574
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 {
2567fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {2595fn 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}
25732610
...@@ -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 register3193 // saving compare flags may require a new caller-saved register
3157 try self.spillCompareFlagsIfOccupied();3194 try self.spillCompareFlagsIfOccupied();
31583195
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 stack3218 // 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;
31613220
...@@ -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 reference3380 // Return result by reference
3322 // TODO3381 //
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 reference3414 // Return result by reference
3349 // TODO3415 //
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 }
33543444
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 }
50705165