From 2262640e8bf4cbcc4e6a46405d7a5a20562b8e47 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 11 Feb 2022 19:06:46 +0100 Subject: [PATCH] stage2 ARM: lower const slices Follow-up to e1a535360fb9ed08fc48018571b9702ab12a5876 for ARM This also fixes some stack offset calculation bugs --- src/arch/arm/CodeGen.zig | 207 ++++++++++++++++++++++----------------- test/behavior/basic.zig | 3 - test/behavior/slice.zig | 17 ---- 3 files changed, 119 insertions(+), 108 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 2c60027d97..e211f9d7bc 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1170,10 +1170,10 @@ fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off }; + break :result MCValue{ .stack_argument_offset = off + 4 }; }, .stack_offset => |off| { - break :result MCValue{ .stack_offset = off }; + break :result MCValue{ .stack_offset = off + 4 }; }, .memory => |addr| { break :result MCValue{ .memory = addr }; @@ -1192,10 +1192,10 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { - break :result MCValue{ .stack_argument_offset = off + 4 }; + break :result MCValue{ .stack_argument_offset = off }; }, .stack_offset => |off| { - break :result MCValue{ .stack_offset = off + 4 }; + break :result MCValue{ .stack_offset = off }; }, .memory => |addr| { break :result MCValue{ .memory = addr + 4 }; @@ -1260,7 +1260,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); const base_mcv: MCValue = switch (slice_mcv) { - .stack_offset => .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, slice_mcv) }, + .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off + 4 }) }, else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), }; self.register_manager.freezeRegs(&.{base_mcv.register}); @@ -1471,36 +1471,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); - } else if (elem_size == 8) { - // TODO generalize this: maybe add a - // genArmMemcpy function which manually copies - // data if the size is below a certain - // threshold and calls "memcpy" if the size is - // larger - - const usize_ty = Type.initTag(.usize); - const tmp_regs = try self.register_manager.allocRegs(2, .{ null, null }); - self.register_manager.freezeRegs(&tmp_regs); - defer self.register_manager.unfreezeRegs(&tmp_regs); - - _ = try self.addInst(.{ - .tag = .ldr, - .data = .{ .rr_offset = .{ - .rt = tmp_regs[0], - .rn = reg, - .offset = .{ .offset = Instruction.Offset.none }, - } }, - }); - _ = try self.addInst(.{ - .tag = .ldr, - .data = .{ .rr_offset = .{ - .rt = tmp_regs[1], - .rn = reg, - .offset = .{ .offset = Instruction.Offset.imm(4) }, - } }, - }); - try self.genSetStack(usize_ty, off, MCValue{ .register = tmp_regs[0] }); - try self.genSetStack(usize_ty, off + 4, MCValue{ .register = tmp_regs[1] }); } else { // TODO optimize the register allocation const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); @@ -1677,7 +1647,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde .ptr_stack_offset => |off| { break :result MCValue{ .ptr_stack_offset = off + struct_size - struct_field_offset - struct_field_size }; }, - .stack_argument_offset => { + else => { const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = struct_field_offset, }); @@ -1699,7 +1669,6 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde ); break :result MCValue{ .register = dst_reg }; }, - else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}), } }; } @@ -3200,9 +3169,9 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { } fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, - .ptr_embedded_in_code => unreachable, .unreach, .none => return, // Nothing to do. .undef => { if (!self.wantSafety()) @@ -3215,23 +3184,16 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement memset", .{}), } }, - .ptr_stack_offset => { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - }, .compare_flags_unsigned, .compare_flags_signed, .immediate, + .ptr_stack_offset, + .ptr_embedded_in_code, => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); }, - .embedded_in_code => |code_offset| { - _ = code_offset; - return self.fail("TODO implement set stack variable from embedded_in_code", .{}); - }, .register => |reg| { - const abi_size = @intCast(u32, ty.abiSize(self.target.*)); const adj_off = stack_offset + abi_size; switch (abi_size) { @@ -3279,24 +3241,23 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } }, .memory, + .embedded_in_code, .stack_argument_offset, + .stack_offset, => { - if (ty.abiSize(self.target.*) <= 4) { - const reg = try self.copyToTmpRegister(ty, mcv); - return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); - } else { - return self.fail("TODO implement memcpy", .{}); + switch (mcv) { + .stack_offset => |off| { + if (stack_offset == off) + return; // Copy stack variable to itself; nothing to do. + }, + else => {}, } - }, - .stack_offset => |off| { - if (stack_offset == off) - return; // Copy stack variable to itself; nothing to do. - if (ty.abiSize(self.target.*) <= 4) { + if (abi_size <= 4) { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); } else { - // TODO optimize the register allocation + // TODO call extern memcpy const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); const src_reg = regs[0]; const dst_reg = regs[1]; @@ -3304,22 +3265,31 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro const count_reg = regs[3]; const tmp_reg = regs[4]; - // sub src_reg, fp, #off - const adj_src_offset = off + @intCast(u32, ty.abiSize(self.target.*)); - const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_src_offset)) |x| x else { - return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); - }; - _ = try self.addInst(.{ - .tag = .sub, - .data = .{ .rr_op = .{ - .rd = src_reg, - .rn = .fp, - .op = src_offset_op, - } }, - }); + switch (mcv) { + .stack_offset => |off| { + // sub src_reg, fp, #off + const adj_src_offset = off + @intCast(u32, abi_size); + const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_src_offset)) |x| x else { + return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); + }; + _ = try self.addInst(.{ + .tag = .sub, + .data = .{ .rr_op = .{ + .rd = src_reg, + .rn = .fp, + .op = src_offset_op, + } }, + }); + }, + .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), + .embedded_in_code, + .stack_argument_offset, + => return self.fail("TODO genSetStack with src={}", .{mcv}), + else => unreachable, + } // sub dst_reg, fp, #stack_offset - const adj_dst_offset = stack_offset + @intCast(u32, ty.abiSize(self.target.*)); + const adj_dst_offset = stack_offset + abi_size; const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_dst_offset)) |x| x else { return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); }; @@ -3332,9 +3302,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } }, }); - // mov len, #elem_size - const elem_size = @intCast(u32, ty.abiSize(self.target.*)); - const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(elem_size)) |x| x else { + // mov len, #abi_size + const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else { return self.fail("TODO load: set reg to elem_size with all possible sizes", .{}); }; _ = try self.addInst(.{ @@ -3619,6 +3588,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (mcv) { .dead => unreachable, .none, .unreach => return, @@ -3634,7 +3604,6 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I } }, .register => |reg| { - const abi_size = @intCast(u32, ty.abiSize(self.target.*)); const adj_off = stack_offset - abi_size; switch (abi_size) { @@ -3675,28 +3644,86 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), } }, - .immediate, - .compare_flags_signed, - .compare_flags_unsigned, .stack_offset, .memory, .stack_argument_offset, .embedded_in_code, => { - if (ty.abiSize(self.target.*) <= 4) { + if (abi_size <= 4) { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); } else { - return self.fail("TODO implement memcpy", .{}); + // TODO call extern memcpy + const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); + const src_reg = regs[0]; + const dst_reg = regs[1]; + const len_reg = regs[2]; + const count_reg = regs[3]; + const tmp_reg = regs[4]; + + switch (mcv) { + .stack_offset => |off| { + // sub src_reg, fp, #off + const adj_src_offset = off + abi_size; + const src_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_src_offset)) |x| x else { + return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); + }; + _ = try self.addInst(.{ + .tag = .sub, + .data = .{ .rr_op = .{ + .rd = src_reg, + .rn = .fp, + .op = src_offset_op, + } }, + }); + }, + .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), + .stack_argument_offset, + .embedded_in_code, + => return self.fail("TODO genSetStackArgument src={}", .{mcv}), + else => unreachable, + } + + // add dst_reg, sp, #stack_offset + const adj_dst_offset = stack_offset - abi_size; + const dst_offset_op: Instruction.Operand = if (Instruction.Operand.fromU32(adj_dst_offset)) |x| x else { + return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); + }; + _ = try self.addInst(.{ + .tag = .add, + .data = .{ .rr_op = .{ + .rd = dst_reg, + .rn = .sp, + .op = dst_offset_op, + } }, + }); + + // mov len, #abi_size + const len_op: Instruction.Operand = if (Instruction.Operand.fromU32(abi_size)) |x| x else { + return self.fail("TODO load: set reg to elem_size with all possible sizes", .{}); + }; + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ .rr_op = .{ + .rd = len_reg, + .rn = .r0, + .op = len_op, + } }, + }); + + // memcpy(src, dst, len) + try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); } }, - .ptr_stack_offset => { + .compare_flags_unsigned, + .compare_flags_signed, + .immediate, + .ptr_stack_offset, + .ptr_embedded_in_code, + => { const reg = try self.copyToTmpRegister(ty, mcv); return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); }, - .ptr_embedded_in_code => { - return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); - }, } } @@ -4114,9 +4141,13 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { var stack_offset: u32 = 0; for (param_types) |ty, i| { - stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, ty.abiAlignment(self.target.*)); - result.args[i] = .{ .stack_argument_offset = stack_offset }; - stack_offset += @intCast(u32, ty.abiSize(self.target.*)); + if (ty.abiSize(self.target.*) > 0) { + stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, ty.abiAlignment(self.target.*)); + result.args[i] = .{ .stack_argument_offset = stack_offset }; + stack_offset += @intCast(u32, ty.abiSize(self.target.*)); + } else { + result.args[i] = .{ .none = {} }; + } } result.stack_byte_count = stack_offset; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 5d7bb3b9a7..18a24f9b3a 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -216,7 +216,6 @@ test "compile time global reinterpret" { } test "cast undefined" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; const array: [100]u8 = undefined; @@ -678,7 +677,6 @@ test "string concatenation" { } test "thread local variable" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO @@ -704,7 +702,6 @@ fn maybe(x: bool) anyerror!?u32 { } test "pointer to thread local array" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 327b8f4f76..badaf7ef03 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -48,7 +48,6 @@ test "slicing" { test "const slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { const a = "1234567890"; @@ -61,7 +60,6 @@ test "const slice" { test "comptime slice of undefined pointer of length 0" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const slice1 = @as([*]i32, undefined)[0..0]; try expect(slice1.len == 0); @@ -83,7 +81,6 @@ fn assertLenIsZero(msg: []const u8) !void { test "access len index of sentinel-terminated slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -99,7 +96,6 @@ test "access len index of sentinel-terminated slice" { test "comptime slice of slice preserves comptime var" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var buff: [10]u8 = undefined; @@ -110,7 +106,6 @@ test "comptime slice of slice preserves comptime var" { test "slice of type" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var types_array = [_]type{ i32, f64, type }; @@ -151,7 +146,6 @@ fn memFree(comptime T: type, memory: []T) void { test "slice of hardcoded address to pointer" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -168,7 +162,6 @@ test "slice of hardcoded address to pointer" { test "comptime slice of pointer preserves comptime var" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var buff: [10]u8 = undefined; @@ -180,7 +173,6 @@ test "comptime slice of pointer preserves comptime var" { test "comptime pointer cast array and then slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; @@ -239,7 +231,6 @@ test "slice string literal has correct type" { test "result location zero sized array inside struct field implicit cast to slice" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const E = struct { entries: []u32, @@ -287,8 +278,6 @@ test "C pointer slice access" { } test "comptime slices are disambiguated" { - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; - try expect(sliceSum(&[_]u8{ 1, 2 }) == 3); try expect(sliceSum(&[_]u8{ 3, 4 }) == 7); } @@ -343,7 +332,6 @@ test "obtaining a null terminated slice" { test "empty array to slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -380,7 +368,6 @@ test "@ptrCast slice to pointer" { test "slice syntax resulting in pointer-to-array" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { fn doTheTest() !void { @@ -529,7 +516,6 @@ test "slice syntax resulting in pointer-to-array" { test "type coercion of pointer to anon struct literal to pointer to slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const S = struct { const U = union { @@ -563,7 +549,6 @@ test "type coercion of pointer to anon struct literal to pointer to slice" { test "array concat of slices gives slice" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; comptime { var a: []const u8 = "aoeu"; @@ -575,7 +560,6 @@ test "array concat of slices gives slice" { test "slice bounds in comptime concatenation" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const bs = comptime blk: { const b = "........1........"; @@ -592,7 +576,6 @@ test "slice bounds in comptime concatenation" { test "slice sentinel access at comptime" { if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; { const str0 = &[_:0]u8{ '1', '2', '3' };