diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index c4220d23f4..4c76907c28 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -864,8 +864,27 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { } fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { - const stack_offset = try self.allocMemPtr(inst); - return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); + const result: MCValue = switch (self.ret_mcv) { + .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) }, + .stack_offset => blk: { + // self.ret_mcv is an address to where this function + // should store its result into + const ret_ty = self.fn_type.fnReturnType(); + var ptr_ty_payload: Type.Payload.ElemType = .{ + .base = .{ .tag = .single_mut_pointer }, + .data = ret_ty, + }; + const ptr_ty = Type.initPayload(&ptr_ty_payload.base); + + // addr_reg will contain the address of where to store the + // result into + const addr_reg = try self.copyToTmpRegister(ptr_ty, self.ret_mcv); + break :blk .{ .register = addr_reg }; + }, + else => unreachable, // invalid return result + }; + + return self.finishAir(inst, result, .{ .none, .none, .none }); } fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { @@ -1577,9 +1596,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .ptr_embedded_in_code => |off| { try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off }); }, - .embedded_in_code => { - return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); - }, .register => |reg| { self.register_manager.freezeRegs(&.{reg}); defer self.register_manager.unfreezeRegs(&.{reg}); @@ -1626,6 +1642,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo } }, .memory, + .embedded_in_code, .stack_offset, .stack_argument_offset, => { @@ -1684,9 +1701,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .ptr_embedded_in_code => |off| { try self.setRegOrMem(value_ty, .{ .embedded_in_code = off }, value); }, - .embedded_in_code => { - return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); - }, .register => |addr_reg| { self.register_manager.freezeRegs(&.{addr_reg}); defer self.register_manager.unfreezeRegs(&.{addr_reg}); @@ -1719,7 +1733,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type switch (value) { .stack_offset => |off| { // sub src_reg, fp, #off - try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off }); + try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); }, .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), else => return self.fail("TODO store {} to register", .{value}), @@ -1735,6 +1749,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type } }, .memory, + .embedded_in_code, .stack_offset, .stack_argument_offset, => { @@ -2656,13 +2671,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { return bt.finishAir(result); } -fn ret(self: *Self, mcv: MCValue) !void { +fn airRet(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); const ret_ty = self.fn_type.fnReturnType(); + switch (self.ret_mcv) { .none => {}, .register => |reg| { // Return result by value - try self.genSetReg(ret_ty, reg, mcv); + try self.genSetReg(ret_ty, reg, operand); }, .stack_offset => { // Return result by reference @@ -2674,28 +2692,66 @@ fn ret(self: *Self, mcv: MCValue) !void { .data = ret_ty, }; const ptr_ty = Type.initPayload(&ptr_ty_payload.base); - try self.store(self.ret_mcv, mcv, ptr_ty, ret_ty); + try self.store(self.ret_mcv, operand, ptr_ty, ret_ty); }, else => unreachable, // invalid return result } // Just add space for an instruction, patch this later try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); -} -fn airRet(self: *Self, inst: Air.Inst.Index) !void { - const un_op = self.air.instructions.items(.data)[inst].un_op; - const operand = try self.resolveInst(un_op); - try self.ret(operand); return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const ptr = try self.resolveInst(un_op); - _ = ptr; - return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch}); - //return self.finishAir(inst, .dead, .{ un_op, .none, .none }); + const ptr_ty = self.air.typeOf(un_op); + const ret_ty = self.fn_type.fnReturnType(); + + switch (self.ret_mcv) { + .none => {}, + .register => { + // Return result by value + try self.load(self.ret_mcv, ptr, ptr_ty); + }, + .stack_offset => { + // Return result by reference + // + // self.ret_mcv is an address to where this function + // should store its result into + // + // If the operand is a ret_ptr instruction, we are done + // here. Else we need to load the result from the location + // pointed to by the operand and store it to the result + // location. + const op_inst = Air.refToIndex(un_op).?; + if (self.air.instructions.items(.tag)[op_inst] != .ret_ptr) { + const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); + const abi_align = ret_ty.abiAlignment(self.target.*); + + // This is essentially allocMem without the + // instruction tracking + if (abi_align > self.stack_align) + self.stack_align = abi_align; + // TODO find a free slot instead of always appending + const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align); + self.next_stack_offset = offset + abi_size; + if (self.next_stack_offset > self.max_end_stack) + self.max_end_stack = self.next_stack_offset; + + const tmp_mcv = MCValue{ .stack_offset = offset }; + try self.load(tmp_mcv, ptr, ptr_ty); + try self.store(self.ret_mcv, tmp_mcv, ptr_ty, ret_ty); + } + }, + else => unreachable, // invalid return result + } + + // Just add space for an instruction, patch this later + try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); + + return self.finishAir(inst, .dead, .{ un_op, .none, .none }); } fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 6b63c8b89b..1ed7ea9faf 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -285,7 +285,6 @@ fn fB() []const u8 { test "call function pointer in struct" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage1) return error.SkipZigTest; try expect(mem.eql(u8, f3(true), "a")); diff --git a/test/behavior/bugs/1735.zig b/test/behavior/bugs/1735.zig index d2ab25624f..b94b4257b6 100644 --- a/test/behavior/bugs/1735.zig +++ b/test/behavior/bugs/1735.zig @@ -44,7 +44,6 @@ const a = struct { test "initialization" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; var t = a.init(); try std.testing.expect(t.foo.len == 0); } diff --git a/test/behavior/bugs/4328.zig b/test/behavior/bugs/4328.zig index cd7386939c..c24017683e 100644 --- a/test/behavior/bugs/4328.zig +++ b/test/behavior/bugs/4328.zig @@ -57,7 +57,6 @@ test "Peer resolution of extern function calls in @TypeOf" { test "Extern function calls, dereferences and field access in @TypeOf" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; const Test = struct { diff --git a/test/behavior/byval_arg_var.zig b/test/behavior/byval_arg_var.zig index 3a1be9cc0c..b6b972d2d3 100644 --- a/test/behavior/byval_arg_var.zig +++ b/test/behavior/byval_arg_var.zig @@ -5,7 +5,6 @@ var result: []const u8 = "wrong"; test "pass string literal byvalue to a generic var param" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; start(); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index e92e20f941..6f300773ae 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1266,7 +1266,6 @@ test "cast between *[N]void and []void" { test "peer resolve arrays of different size to const slice" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO try expect(mem.eql(u8, boolToStr(true), "true")); try expect(mem.eql(u8, boolToStr(false), "false")); diff --git a/test/behavior/defer.zig b/test/behavior/defer.zig index cad06a7ed7..c8239395eb 100644 --- a/test/behavior/defer.zig +++ b/test/behavior/defer.zig @@ -32,7 +32,6 @@ test "defer and labeled break" { } test "errdefer does not apply to fn inside fn" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (testNestedFnErrDefer()) |_| @panic("expected error") else |e| try expect(e == error.Bad); diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index 016ab8575b..412d87befd 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -17,7 +17,6 @@ fn checkSize(comptime T: type) usize { } test "simple generic fn" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try expect(max(i32, 3, -1) == 3); diff --git a/test/behavior/incomplete_struct_param_tld.zig b/test/behavior/incomplete_struct_param_tld.zig index 3f69c1cbbd..385c64dbdb 100644 --- a/test/behavior/incomplete_struct_param_tld.zig +++ b/test/behavior/incomplete_struct_param_tld.zig @@ -22,7 +22,6 @@ fn foo(a: A) i32 { } test "incomplete struct param top level declaration" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const a = A{ .b = B{ diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index ae480d7075..35e6e3a3a3 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -138,7 +138,6 @@ fn returnEmptyStructInstance() StructWithNoFields { test "fn call of struct field" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const Foo = struct { ptr: fn () i32, @@ -196,7 +195,6 @@ const MemberFnRand = struct { test "return struct byval from function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const bar = makeBar2(1234, 5678); try expect(bar.y == 5678); @@ -325,7 +323,6 @@ test "return empty struct from fn" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO _ = testReturnEmptyStructFromFn(); } diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 66c1b342e1..12d7d54f39 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -108,7 +108,6 @@ fn returnsFive() i32 { } test "switch on type" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO try expect(trueIfBoolFalseOtherwise(bool)); diff --git a/test/behavior/usingnamespace.zig b/test/behavior/usingnamespace.zig index 987fc95e24..ed84c36071 100644 --- a/test/behavior/usingnamespace.zig +++ b/test/behavior/usingnamespace.zig @@ -37,7 +37,6 @@ test "usingnamespace does not redeclare an imported variable" { usingnamespace @import("usingnamespace/foo.zig"); test "usingnamespace omits mixing in private functions" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try expect(@This().privateFunction()); @@ -53,7 +52,6 @@ test { usingnamespace @import("usingnamespace/a.zig"); test "two files usingnamespace import each other" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try expect(@This().ok()); diff --git a/test/behavior/while.zig b/test/behavior/while.zig index d49507c7df..dced1ee208 100644 --- a/test/behavior/while.zig +++ b/test/behavior/while.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); const expect = std.testing.expect; test "while loop" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; var i: i32 = 0; @@ -23,7 +22,6 @@ fn whileLoop2() i32 { } test "static eval while" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; try expect(static_eval_while_number == 1);