Commit c0ae9647f9

joachimschmidt557 <joachim.schmidt557@outlook.com>
2021-12-28 22:00:14
stage2 ARM: implement slice_elem_val for types with size <= 4
1 parent 4f4f0bc
Changed files (3)
src
test
stage2
src/arch/arm/CodeGen.zig
@@ -1159,13 +1159,29 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
-    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch});
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+        const mcv = try self.resolveInst(ty_op.operand);
+        switch (mcv) {
+            .stack_offset => |off| {
+                break :result MCValue{ .stack_offset = off };
+            },
+            else => return self.fail("TODO implement slice_ptr for {}", .{mcv}),
+        }
+    };
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
     const ty_op = self.air.instructions.items(.data)[inst].ty_op;
-    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_len for {}", .{self.target.cpu.arch});
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+        const mcv = try self.resolveInst(ty_op.operand);
+        switch (mcv) {
+            .stack_offset => |off| {
+                break :result MCValue{ .stack_offset = off + 4 };
+            },
+            else => return self.fail("TODO implement slice_len for {}", .{mcv}),
+        }
+    };
     return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
 }
 
@@ -1184,7 +1200,76 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
     const is_volatile = false; // TODO
     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
-    const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch});
+    const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {
+        const slice_mcv = try self.resolveInst(bin_op.lhs);
+        const index_mcv = try self.resolveInst(bin_op.rhs);
+
+        const slice_ty = self.air.typeOf(bin_op.lhs);
+        const elem_ty = slice_ty.childType();
+        const elem_size = elem_ty.abiSize(self.target.*);
+
+        var buf: Type.SlicePtrFieldTypeBuffer = undefined;
+        const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
+
+        // TODO optimize this for the case when elem_size is a power
+        // of two (includes elem_size == 1)
+        const offset_mcv = try self.genArmMulConstant(inst, bin_op.rhs, 1, @intCast(u32, elem_size));
+        assert(offset_mcv == .register); // result of multiplication should always be register
+
+        const base_mcv: MCValue = switch (slice_mcv) {
+            .stack_offset => |off| blk: {
+                const reg = try self.register_manager.allocReg(null, &.{offset_mcv.register});
+                try self.genSetReg(slice_ptr_field_type, reg, MCValue{ .stack_offset = off });
+                break :blk MCValue{ .register = reg };
+            },
+            else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}),
+        };
+
+        if (elem_size <= 4) {
+            const dst_reg = try self.register_manager.allocReg(inst, &.{ base_mcv.register, offset_mcv.register });
+            switch (elem_size) {
+                1, 4 => {
+                    const tag: Mir.Inst.Tag = switch (elem_size) {
+                        1 => .ldrb,
+                        4 => .ldr,
+                        else => unreachable,
+                    };
+
+                    _ = try self.addInst(.{
+                        .tag = tag,
+                        .cond = .al,
+                        .data = .{ .rr_offset = .{
+                            .rt = dst_reg,
+                            .rn = base_mcv.register,
+                            .offset = .{ .offset = Instruction.Offset.reg(offset_mcv.register, 0) },
+                        } },
+                    });
+                },
+                2 => {
+                    _ = try self.addInst(.{
+                        .tag = .ldrh,
+                        .cond = .al,
+                        .data = .{ .rr_extra_offset = .{
+                            .rt = dst_reg,
+                            .rn = base_mcv.register,
+                            .offset = .{ .offset = Instruction.ExtraLoadStoreOffset.reg(offset_mcv.register) },
+                        } },
+                    });
+                },
+                else => unreachable,
+            }
+
+            break :result MCValue{ .register = dst_reg };
+        } else {
+            // const dst_mcv = try self.allocRegOrMem(inst, false);
+            return self.fail("TODO implement slice_elem_val for elem_size >= 4", .{});
+        }
+
+        _ = offset_mcv;
+        _ = slice_mcv;
+        _ = index_mcv;
+        _ = offset_mcv;
+    };
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
 
@@ -1839,6 +1924,58 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai
     return dst_mcv;
 }
 
+fn genArmMulConstant(self: *Self, inst: Air.Inst.Index, op: Air.Inst.Ref, op_index: Liveness.OperandInt, imm: u32) !MCValue {
+    const mcv = try self.resolveInst(op);
+    const rhs = MCValue{ .immediate = imm };
+
+    const lhs_is_register = mcv == .register;
+    const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op, op_index, mcv);
+
+    // Destination must be a register
+    // LHS must be a register
+    // RHS must be a register
+    var dst_mcv: MCValue = undefined;
+    var lhs_mcv: MCValue = mcv;
+    var rhs_mcv: MCValue = rhs;
+
+    // Allocate registers for operands and/or destination
+    if (reuse_lhs) {
+        // Allocate 1 register
+        rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(null, &.{mcv.register}) };
+        dst_mcv = mcv;
+    } else {
+        // Allocate 1 or 2 registers
+        if (lhs_is_register) {
+            // Move RHS to register
+            dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{mcv.register}) };
+            rhs_mcv = dst_mcv;
+        } else {
+            // Move LHS and RHS to register
+            const regs = try self.register_manager.allocRegs(2, .{ inst, null }, &.{});
+            lhs_mcv = MCValue{ .register = regs[0] };
+            rhs_mcv = MCValue{ .register = regs[1] };
+            dst_mcv = lhs_mcv;
+        }
+    }
+
+    // Move the operands to the newly allocated registers
+    if (!lhs_is_register) {
+        try self.genSetReg(self.air.typeOf(op), lhs_mcv.register, mcv);
+    }
+    try self.genSetReg(Type.initTag(.usize), rhs_mcv.register, rhs);
+
+    _ = try self.addInst(.{
+        .tag = .mul,
+        .cond = .al,
+        .data = .{ .rrr = .{
+            .rd = dst_mcv.register,
+            .rn = lhs_mcv.register,
+            .rm = rhs_mcv.register,
+        } },
+    });
+    return dst_mcv;
+}
+
 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
     const ty_str = self.air.instructions.items(.data)[inst].ty_str;
     const zir = &self.mod_fn.owner_decl.getFileScope().zir;
src/codegen.zig
@@ -168,9 +168,10 @@ pub fn generateSymbol(
                 ),
             };
         },
-        .Array => {
-            // TODO populate .debug_info for the array
-            if (typed_value.val.castTag(.bytes)) |payload| {
+        .Array => switch (typed_value.val.tag()) {
+            .bytes => {
+                // TODO populate .debug_info for the array
+                const payload = typed_value.val.castTag(.bytes).?;
                 if (typed_value.ty.sentinel()) |sentinel| {
                     try code.ensureUnusedCapacity(payload.data.len + 1);
                     code.appendSliceAssumeCapacity(payload.data);
@@ -188,94 +189,83 @@ pub fn generateSymbol(
                 } else {
                     return Result{ .externally_managed = payload.data };
                 }
-            }
-            return Result{
+            },
+            .array => {
+                // TODO populate .debug_info for the array
+                const elem_vals = typed_value.val.castTag(.array).?.data;
+                const elem_ty = typed_value.ty.elemType();
+                for (elem_vals) |elem_val| {
+                    switch (try generateSymbol(bin_file, src_loc, .{
+                        .ty = elem_ty,
+                        .val = elem_val,
+                    }, code, debug_output)) {
+                        .appended => {},
+                        .externally_managed => |slice| {
+                            code.appendSliceAssumeCapacity(slice);
+                            return Result{ .appended = {} };
+                        },
+                        .fail => |em| return Result{ .fail = em },
+                    }
+                }
+                return Result{ .appended = {} };
+            },
+            else => return Result{
                 .fail = try ErrorMsg.create(
                     bin_file.allocator,
                     src_loc,
-                    "TODO implement generateSymbol for more kinds of arrays",
-                    .{},
+                    "TODO implement generateSymbol for array type value: {s}",
+                    .{@tagName(typed_value.val.tag())},
                 ),
-            };
+            },
         },
-        .Pointer => switch (typed_value.ty.ptrSize()) {
-            .Slice => {
+        .Pointer => switch (typed_value.val.tag()) {
+            .variable => {
+                const decl = typed_value.val.castTag(.variable).?.data.owner_decl;
+                return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output);
+            },
+            .decl_ref => {
+                const decl = typed_value.val.castTag(.decl_ref).?.data;
+                return lowerDeclRef(bin_file, src_loc, typed_value, decl, code, debug_output);
+            },
+            .slice => {
                 // TODO populate .debug_info for the slice
+                const slice = typed_value.val.castTag(.slice).?.data;
 
                 // generate ptr
                 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
                 const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
                 switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = slice_ptr_field_type,
-                    .val = typed_value.val.slicePtr(),
+                    .val = slice.ptr,
                 }, code, debug_output)) {
                     .appended => {},
-                    .externally_managed => |slice| {
-                        code.appendSliceAssumeCapacity(slice);
+                    .externally_managed => |external_slice| {
+                        code.appendSliceAssumeCapacity(external_slice);
                     },
                     .fail => |em| return Result{ .fail = em },
                 }
 
                 // generate length
-                var int_buffer: Value.Payload.U64 = .{
-                    .base = .{ .tag = .int_u64 },
-                    .data = typed_value.val.sliceLen(),
-                };
                 switch (try generateSymbol(bin_file, src_loc, .{
                     .ty = Type.initTag(.usize),
-                    .val = Value.initPayload(&int_buffer.base),
+                    .val = slice.len,
                 }, code, debug_output)) {
                     .appended => {},
-                    .externally_managed => |slice| {
-                        code.appendSliceAssumeCapacity(slice);
+                    .externally_managed => |external_slice| {
+                        code.appendSliceAssumeCapacity(external_slice);
                     },
                     .fail => |em| return Result{ .fail = em },
                 }
 
-                return Result{
-                    .fail = try ErrorMsg.create(
-                        bin_file.allocator,
-                        src_loc,
-                        "TODO implement generateSymbol for slice {}",
-                        .{typed_value.val},
-                    ),
-                };
+                return Result{ .appended = {} };
             },
-            else => {
-                // TODO populate .debug_info for the pointer
-                if (typed_value.val.castTag(.decl_ref)) |payload| {
-                    const decl = payload.data;
-                    if (decl.analysis != .complete) return error.AnalysisFail;
-                    decl.alive = true;
-                    // TODO handle the dependency of this symbol on the decl's vaddr.
-                    // If the decl changes vaddr, then this symbol needs to get regenerated.
-                    const vaddr = bin_file.getDeclVAddr(decl);
-                    const endian = bin_file.options.target.cpu.arch.endian();
-                    switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
-                        16 => {
-                            try code.resize(2);
-                            mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian);
-                        },
-                        32 => {
-                            try code.resize(4);
-                            mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian);
-                        },
-                        64 => {
-                            try code.resize(8);
-                            mem.writeInt(u64, code.items[0..8], vaddr, endian);
-                        },
-                        else => unreachable,
-                    }
-                    return Result{ .appended = {} };
-                }
-                return Result{
-                    .fail = try ErrorMsg.create(
-                        bin_file.allocator,
-                        src_loc,
-                        "TODO implement generateSymbol for pointer {}",
-                        .{typed_value.val},
-                    ),
-                };
+            else => return Result{
+                .fail = try ErrorMsg.create(
+                    bin_file.allocator,
+                    src_loc,
+                    "TODO implement generateSymbol for pointer type value: '{s}'",
+                    .{@tagName(typed_value.val.tag())},
+                ),
             },
         },
         .Int => {
@@ -401,3 +391,61 @@ pub fn generateSymbol(
         },
     }
 }
+
+fn lowerDeclRef(
+    bin_file: *link.File,
+    src_loc: Module.SrcLoc,
+    typed_value: TypedValue,
+    decl: *Module.Decl,
+    code: *std.ArrayList(u8),
+    debug_output: DebugInfoOutput,
+) GenerateSymbolError!Result {
+    if (typed_value.ty.isSlice()) {
+        // generate ptr
+        var buf: Type.SlicePtrFieldTypeBuffer = undefined;
+        const slice_ptr_field_type = typed_value.ty.slicePtrFieldType(&buf);
+        switch (try generateSymbol(bin_file, src_loc, .{
+            .ty = slice_ptr_field_type,
+            .val = typed_value.val,
+        }, code, debug_output)) {
+            .appended => {},
+            .externally_managed => |external_slice| {
+                code.appendSliceAssumeCapacity(external_slice);
+            },
+            .fail => |em| return Result{ .fail = em },
+        }
+
+        // generate length
+        var slice_len: Value.Payload.U64 = .{
+            .base = .{ .tag = .int_u64 },
+            .data = typed_value.val.sliceLen(),
+        };
+        switch (try generateSymbol(bin_file, src_loc, .{
+            .ty = Type.initTag(.usize),
+            .val = Value.initPayload(&slice_len.base),
+        }, code, debug_output)) {
+            .appended => {},
+            .externally_managed => |external_slice| {
+                code.appendSliceAssumeCapacity(external_slice);
+            },
+            .fail => |em| return Result{ .fail = em },
+        }
+
+        return Result{ .appended = {} };
+    }
+
+    if (decl.analysis != .complete) return error.AnalysisFail;
+    decl.alive = true;
+    // TODO handle the dependency of this symbol on the decl's vaddr.
+    // If the decl changes vaddr, then this symbol needs to get regenerated.
+    const vaddr = bin_file.getDeclVAddr(decl);
+    const endian = bin_file.options.target.cpu.arch.endian();
+    switch (bin_file.options.target.cpu.arch.ptrBitWidth()) {
+        16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(u16, vaddr), endian),
+        32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(u32, vaddr), endian),
+        64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian),
+        else => unreachable,
+    }
+
+    return Result{ .appended = {} };
+}
test/stage2/arm.zig
@@ -637,4 +637,25 @@ pub fn addCases(ctx: *TestContext) !void {
             "Hello, World!\n",
         );
     }
+
+    {
+        var case = ctx.exe("slices", linux_arm);
+        case.addCompareOutput(
+            \\var array = [_]u32{ 0, 42, 123, 69 };
+            \\var s: []const u32 = &array;
+            \\
+            \\pub fn main() void {
+            \\    assert(s[0] == 0);
+            \\    assert(s[1] == 42);
+            \\    assert(s[2] == 123);
+            \\    assert(s[3] == 69);
+            \\}
+            \\
+            \\fn assert(ok: bool) void {
+            \\    if (!ok) unreachable;
+            \\}
+        ,
+            "",
+        );
+    }
 }