Commit 1467590e40

Koakuma <koachan@protonmail.com>
2022-04-10 15:52:16
stage2: sparcv9: Implement enough instruction to compile simple exes
1 parent 1f63afa
Changed files (4)
src/arch/sparcv9/bits.zig
@@ -979,6 +979,38 @@ pub const Instruction = union(enum) {
         };
     }
 
+    pub fn ldub(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
+        return switch (s2) {
+            Register => format3a(0b11, 0b00_0001, rs1, rs2, rd),
+            i13 => format3b(0b11, 0b00_0001, rs1, rs2, rd),
+            else => unreachable,
+        };
+    }
+
+    pub fn lduh(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
+        return switch (s2) {
+            Register => format3a(0b11, 0b00_0010, rs1, rs2, rd),
+            i13 => format3b(0b11, 0b00_0010, rs1, rs2, rd),
+            else => unreachable,
+        };
+    }
+
+    pub fn lduw(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
+        return switch (s2) {
+            Register => format3a(0b11, 0b00_0000, rs1, rs2, rd),
+            i13 => format3b(0b11, 0b00_0000, rs1, rs2, rd),
+            else => unreachable,
+        };
+    }
+
+    pub fn ldx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
+        return switch (s2) {
+            Register => format3a(0b11, 0b00_1011, rs1, rs2, rd),
+            i13 => format3b(0b11, 0b00_1011, rs1, rs2, rd),
+            else => unreachable,
+        };
+    }
+
     pub fn nop() Instruction {
         return sethi(0, .g0);
     }
src/arch/sparcv9/CodeGen.zig
@@ -193,6 +193,43 @@ const CallMCValues = struct {
     }
 };
 
+const BigTomb = struct {
+    function: *Self,
+    inst: Air.Inst.Index,
+    tomb_bits: Liveness.Bpi,
+    big_tomb_bits: u32,
+    bit_index: usize,
+
+    fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
+        const this_bit_index = bt.bit_index;
+        bt.bit_index += 1;
+
+        const op_int = @enumToInt(op_ref);
+        if (op_int < Air.Inst.Ref.typed_value_map.len) return;
+        const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
+
+        if (this_bit_index < Liveness.bpi - 1) {
+            const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
+            if (!dies) return;
+        } else {
+            const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
+            const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
+            if (!dies) return;
+        }
+        bt.function.processDeath(op_index);
+    }
+
+    fn finishAir(bt: *BigTomb, result: MCValue) void {
+        const is_used = !bt.function.liveness.isUnused(bt.inst);
+        if (is_used) {
+            log.debug("%{d} => {}", .{ bt.inst, result });
+            const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
+            branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
+        }
+        bt.function.finishAirBookkeeping();
+    }
+};
+
 pub fn generate(
     bin_file: *link.File,
     src_loc: Module.SrcLoc,
@@ -684,8 +721,16 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
         return self.finishAir(inst, result, buf);
     }
 
-    @panic("TODO implement asm return");
-    //return self.fail("TODO implement asm return for {}", .{self.target.cpu.arch});
+    var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len);
+    for (outputs) |output| {
+        if (output == .none) continue;
+
+        bt.feed(output);
+    }
+    for (inputs) |input| {
+        bt.feed(input);
+    }
+    return bt.finishAir(result);
 }
 
 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
@@ -1071,13 +1116,65 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
     self.finishAirBookkeeping();
 }
 
-fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, off: i13, abi_size: u64) !void {
-    _ = value_reg;
-    _ = addr_reg;
-    _ = off;
+fn genLoad(self: *Self, value_reg: Register, addr_reg: Register, comptime off_type: type, off: off_type, abi_size: u64) !void {
+    assert(off_type == Register or off_type == i13);
+
+    const is_imm = (off_type == i13);
+    const rs2_or_imm = if (is_imm) .{ .imm = off } else .{ .rs2 = off };
 
     switch (abi_size) {
-        1, 2, 4, 8 => return self.fail("TODO: A.27 Load Integer", .{}),
+        1 => {
+            _ = try self.addInst(.{
+                .tag = .ldub,
+                .data = .{
+                    .arithmetic_3op = .{
+                        .is_imm = is_imm,
+                        .rd = value_reg,
+                        .rs1 = addr_reg,
+                        .rs2_or_imm = rs2_or_imm,
+                    },
+                },
+            });
+        },
+        2 => {
+            _ = try self.addInst(.{
+                .tag = .lduh,
+                .data = .{
+                    .arithmetic_3op = .{
+                        .is_imm = is_imm,
+                        .rd = value_reg,
+                        .rs1 = addr_reg,
+                        .rs2_or_imm = rs2_or_imm,
+                    },
+                },
+            });
+        },
+        4 => {
+            _ = try self.addInst(.{
+                .tag = .lduw,
+                .data = .{
+                    .arithmetic_3op = .{
+                        .is_imm = is_imm,
+                        .rd = value_reg,
+                        .rs1 = addr_reg,
+                        .rs2_or_imm = rs2_or_imm,
+                    },
+                },
+            });
+        },
+        8 => {
+            _ = try self.addInst(.{
+                .tag = .ldx,
+                .data = .{
+                    .arithmetic_3op = .{
+                        .is_imm = is_imm,
+                        .rd = value_reg,
+                        .rs1 = addr_reg,
+                        .rs2_or_imm = rs2_or_imm,
+                    },
+                },
+            });
+        },
         3, 5, 6, 7 => return self.fail("TODO: genLoad for more abi_sizes", .{}),
         else => unreachable,
     }
@@ -1226,12 +1323,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             // The value is in memory at a hard-coded address.
             // If the type is a pointer, it means the pointer address is at this memory location.
             try self.genSetReg(ty, reg, .{ .immediate = addr });
-            try self.genLoad(reg, reg, 0, ty.abiSize(self.target.*));
+            try self.genLoad(reg, reg, i13, 0, ty.abiSize(self.target.*));
         },
         .stack_offset => |off| {
             const simm13 = math.cast(u12, off) catch
                 return self.fail("TODO larger stack offsets", .{});
-            try self.genLoad(reg, .sp, simm13, ty.abiSize(self.target.*));
+            try self.genLoad(reg, .sp, i13, simm13, ty.abiSize(self.target.*));
         },
     }
 }
@@ -1269,14 +1366,10 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
         return MCValue{ .undef = {} };
 
     if (typed_value.val.castTag(.decl_ref)) |payload| {
-        _ = payload;
-        return self.fail("TODO implement lowerDeclRef non-mut", .{});
-        // return self.lowerDeclRef(typed_value, payload.data);
+        return self.lowerDeclRef(typed_value, payload.data);
     }
     if (typed_value.val.castTag(.decl_ref_mut)) |payload| {
-        _ = payload;
-        return self.fail("TODO implement lowerDeclRef mut", .{});
-        // return self.lowerDeclRef(typed_value, payload.data.decl);
+        return self.lowerDeclRef(typed_value, payload.data.decl);
     }
     const target = self.target.*;
 
@@ -1315,6 +1408,39 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
     }
 }
 
+fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
+    try self.ensureProcessDeathCapacity(operand_count + 1);
+    return BigTomb{
+        .function = self,
+        .inst = inst,
+        .tomb_bits = self.liveness.getTombBits(inst),
+        .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
+        .bit_index = 0,
+    };
+}
+
+fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue {
+    const ptr_bits = self.target.cpu.arch.ptrBitWidth();
+    const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+
+    // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
+    if (tv.ty.zigTypeTag() == .Pointer) blk: {
+        if (tv.ty.castPtrToFn()) |_| break :blk;
+        if (!tv.ty.elemType2().hasRuntimeBits()) {
+            return MCValue.none;
+        }
+    }
+
+    decl.alive = true;
+    if (self.bin_file.cast(link.File.Elf)) |elf_file| {
+        const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
+        const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
+        return MCValue{ .memory = got_addr };
+    } else {
+        return self.fail("TODO codegen non-ELF const Decl pointer", .{});
+    }
+}
+
 fn parseRegName(name: []const u8) ?Register {
     if (@hasDecl(Register, "parseRegName")) {
         return Register.parseRegName(name);
src/arch/sparcv9/Emit.zig
@@ -59,6 +59,11 @@ pub fn emitMir(
             .jmpl => @panic("TODO implement sparcv9 jmpl"),
             .jmpl_i => @panic("TODO implement sparcv9 jmpl to reg"),
 
+            .ldub => try emit.mirArithmetic3Op(inst),
+            .lduh => try emit.mirArithmetic3Op(inst),
+            .lduw => try emit.mirArithmetic3Op(inst),
+            .ldx => try emit.mirArithmetic3Op(inst),
+
             .@"or" => try emit.mirArithmetic3Op(inst),
 
             .nop => try emit.mirNop(),
@@ -68,7 +73,7 @@ pub fn emitMir(
             .save => try emit.mirArithmetic3Op(inst),
             .restore => try emit.mirArithmetic3Op(inst),
 
-            .sethi => @panic("TODO implement sparcv9 sethi"),
+            .sethi => try emit.mirSethi(inst),
 
             .sllx => @panic("TODO implement sparcv9 sllx"),
 
@@ -158,6 +163,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
         const imm = data.rs2_or_imm.imm;
         switch (tag) {
             .add => try emit.writeInstruction(Instruction.add(i13, rs1, imm, rd)),
+            .ldub => try emit.writeInstruction(Instruction.ldub(i13, rs1, imm, rd)),
+            .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)),
+            .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)),
+            .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)),
             .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
             .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
             .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
@@ -168,6 +177,10 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
         const rs2 = data.rs2_or_imm.rs2;
         switch (tag) {
             .add => try emit.writeInstruction(Instruction.add(Register, rs1, rs2, rd)),
+            .ldub => try emit.writeInstruction(Instruction.ldub(Register, rs1, rs2, rd)),
+            .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)),
+            .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)),
+            .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)),
             .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
             .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
             .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
@@ -181,6 +194,17 @@ fn mirNop(emit: *Emit) !void {
     try emit.writeInstruction(Instruction.nop());
 }
 
+fn mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+    const data = emit.mir.instructions.items(.data)[inst].sethi;
+
+    const imm = data.imm;
+    const rd = data.rd;
+
+    assert(tag == .sethi);
+    try emit.writeInstruction(Instruction.sethi(imm, rd));
+}
+
 fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
     const tag = emit.mir.instructions.items(.tag)[inst];
     const data = emit.mir.instructions.items(.data)[inst].trap;
src/arch/sparcv9/Mir.zig
@@ -60,6 +60,16 @@ pub const Inst = struct {
         jmpl,
         jmpl_i,
 
+        /// A.27 Load Integer
+        /// Those uses the arithmetic_3op field.
+        /// Note that the ldd variant of this instruction is deprecated, do not emit
+        /// it unless specifically requested (e.g. by inline assembly).
+        // TODO add other operations.
+        ldub,
+        lduh,
+        lduw,
+        ldx,
+
         /// A.31 Logical Operations
         /// Those uses the arithmetic_3op field.
         // TODO add other operations.