stage2: sparcv9: Implement enough instruction to compile simple exes

This commit is contained in:
Koakuma 2022-04-10 20:52:16 +07:00
parent 1f63afa7c9
commit 1467590e40
4 changed files with 208 additions and 16 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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.

View File

@ -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);
}