diff options
Diffstat (limited to 'src-self-hosted/codegen')
| -rw-r--r-- | src-self-hosted/codegen/arm.zig | 607 | ||||
| -rw-r--r-- | src-self-hosted/codegen/c.zig | 299 | ||||
| -rw-r--r-- | src-self-hosted/codegen/llvm.zig | 125 | ||||
| -rw-r--r-- | src-self-hosted/codegen/riscv64.zig | 433 | ||||
| -rw-r--r-- | src-self-hosted/codegen/spu-mk2.zig | 170 | ||||
| -rw-r--r-- | src-self-hosted/codegen/spu-mk2/interpreter.zig | 166 | ||||
| -rw-r--r-- | src-self-hosted/codegen/wasm.zig | 142 | ||||
| -rw-r--r-- | src-self-hosted/codegen/x86.zig | 123 | ||||
| -rw-r--r-- | src-self-hosted/codegen/x86_64.zig | 220 |
9 files changed, 0 insertions, 2285 deletions
diff --git a/src-self-hosted/codegen/arm.zig b/src-self-hosted/codegen/arm.zig deleted file mode 100644 index 05178ea7d3..0000000000 --- a/src-self-hosted/codegen/arm.zig +++ /dev/null @@ -1,607 +0,0 @@ -const std = @import("std"); -const DW = std.dwarf; -const testing = std.testing; - -/// The condition field specifies the flags neccessary for an -/// Instruction to be executed -pub const Condition = enum(u4) { - /// equal - eq, - /// not equal - ne, - /// unsigned higher or same - cs, - /// unsigned lower - cc, - /// negative - mi, - /// positive or zero - pl, - /// overflow - vs, - /// no overflow - vc, - /// unsigned higer - hi, - /// unsigned lower or same - ls, - /// greater or equal - ge, - /// less than - lt, - /// greater than - gt, - /// less than or equal - le, - /// always - al, -}; - -/// Represents a register in the ARM instruction set architecture -pub const Register = enum(u5) { - r0, - r1, - r2, - r3, - r4, - r5, - r6, - r7, - r8, - r9, - r10, - r11, - r12, - r13, - r14, - r15, - - /// Argument / result / scratch register 1 - a1, - /// Argument / result / scratch register 2 - a2, - /// Argument / scratch register 3 - a3, - /// Argument / scratch register 4 - a4, - /// Variable-register 1 - v1, - /// Variable-register 2 - v2, - /// Variable-register 3 - v3, - /// Variable-register 4 - v4, - /// Variable-register 5 - v5, - /// Platform register - v6, - /// Variable-register 7 - v7, - /// Frame pointer or Variable-register 8 - fp, - /// Intra-Procedure-call scratch register - ip, - /// Stack pointer - sp, - /// Link register - lr, - /// Program counter - pc, - - /// Returns the unique 4-bit ID of this register which is used in - /// the machine code - pub fn id(self: Register) u4 { - return @truncate(u4, @enumToInt(self)); - } - - /// Returns the index into `callee_preserved_regs`. - pub fn allocIndex(self: Register) ?u4 { - inline for (callee_preserved_regs) |cpreg, i| { - if (self.id() == cpreg.id()) return i; - } - return null; - } - - pub fn dwarfLocOp(self: Register) u8 { - return @as(u8, self.id()) + DW.OP_reg0; - } -}; - -test "Register.id" { - testing.expectEqual(@as(u4, 15), Register.r15.id()); - testing.expectEqual(@as(u4, 15), Register.pc.id()); -} - -pub const callee_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3, .r4, .r5, .r6, .r7, .r8, .r10 }; -pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 }; -pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 }; - -/// Represents an instruction in the ARM instruction set architecture -pub const Instruction = union(enum) { - DataProcessing: packed struct { - // Note to self: The order of the fields top-to-bottom is - // right-to-left in the actual 32-bit int representation - op2: u12, - rd: u4, - rn: u4, - s: u1, - opcode: u4, - i: u1, - fixed: u2 = 0b00, - cond: u4, - }, - SingleDataTransfer: packed struct { - offset: u12, - rd: u4, - rn: u4, - l: u1, - w: u1, - b: u1, - u: u1, - p: u1, - i: u1, - fixed: u2 = 0b01, - cond: u4, - }, - Branch: packed struct { - offset: u24, - link: u1, - fixed: u3 = 0b101, - cond: u4, - }, - BranchExchange: packed struct { - rn: u4, - fixed_1: u1 = 0b1, - link: u1, - fixed_2: u22 = 0b0001_0010_1111_1111_1111_00, - cond: u4, - }, - SupervisorCall: packed struct { - comment: u24, - fixed: u4 = 0b1111, - cond: u4, - }, - Breakpoint: packed struct { - imm4: u4, - fixed_1: u4 = 0b0111, - imm12: u12, - fixed_2_and_cond: u12 = 0b1110_0001_0010, - }, - - /// Represents the possible operations which can be performed by a - /// DataProcessing instruction - const Opcode = enum(u4) { - // Rd := Op1 AND Op2 - @"and", - // Rd := Op1 EOR Op2 - eor, - // Rd := Op1 - Op2 - sub, - // Rd := Op2 - Op1 - rsb, - // Rd := Op1 + Op2 - add, - // Rd := Op1 + Op2 + C - adc, - // Rd := Op1 - Op2 + C - 1 - sbc, - // Rd := Op2 - Op1 + C - 1 - rsc, - // set condition codes on Op1 AND Op2 - tst, - // set condition codes on Op1 EOR Op2 - teq, - // set condition codes on Op1 - Op2 - cmp, - // set condition codes on Op1 + Op2 - cmn, - // Rd := Op1 OR Op2 - orr, - // Rd := Op2 - mov, - // Rd := Op1 AND NOT Op2 - bic, - // Rd := NOT Op2 - mvn, - }; - - /// Represents the second operand to a data processing instruction - /// which can either be content from a register or an immediate - /// value - pub const Operand = union(enum) { - Register: packed struct { - rm: u4, - shift: u8, - }, - Immediate: packed struct { - imm: u8, - rotate: u4, - }, - - /// Represents multiple ways a register can be shifted. A - /// register can be shifted by a specific immediate value or - /// by the contents of another register - pub const Shift = union(enum) { - Immediate: packed struct { - fixed: u1 = 0b0, - typ: u2, - amount: u5, - }, - Register: packed struct { - fixed_1: u1 = 0b1, - typ: u2, - fixed_2: u1 = 0b0, - rs: u4, - }, - - const Type = enum(u2) { - LogicalLeft, - LogicalRight, - ArithmeticRight, - RotateRight, - }; - - const none = Shift{ - .Immediate = .{ - .amount = 0, - .typ = 0, - }, - }; - - pub fn toU8(self: Shift) u8 { - return switch (self) { - .Register => |v| @bitCast(u8, v), - .Immediate => |v| @bitCast(u8, v), - }; - } - - pub fn reg(rs: Register, typ: Type) Shift { - return Shift{ - .Register = .{ - .rs = rs.id(), - .typ = @enumToInt(typ), - }, - }; - } - - pub fn imm(amount: u5, typ: Type) Shift { - return Shift{ - .Immediate = .{ - .amount = amount, - .typ = @enumToInt(typ), - }, - }; - } - }; - - pub fn toU12(self: Operand) u12 { - return switch (self) { - .Register => |v| @bitCast(u12, v), - .Immediate => |v| @bitCast(u12, v), - }; - } - - pub fn reg(rm: Register, shift: Shift) Operand { - return Operand{ - .Register = .{ - .rm = rm.id(), - .shift = shift.toU8(), - }, - }; - } - - pub fn imm(immediate: u8, rotate: u4) Operand { - return Operand{ - .Immediate = .{ - .imm = immediate, - .rotate = rotate, - }, - }; - } - }; - - /// Represents the offset operand of a load or store - /// instruction. Data can be loaded from memory with either an - /// immediate offset or an offset that is stored in some register. - pub const Offset = union(enum) { - Immediate: u12, - Register: packed struct { - rm: u4, - shift: u8, - }, - - pub const none = Offset{ - .Immediate = 0, - }; - - pub fn toU12(self: Offset) u12 { - return switch (self) { - .Register => |v| @bitCast(u12, v), - .Immediate => |v| v, - }; - } - - pub fn reg(rm: Register, shift: u8) Offset { - return Offset{ - .Register = .{ - .rm = rm.id(), - .shift = shift, - }, - }; - } - - pub fn imm(immediate: u8) Offset { - return Offset{ - .Immediate = immediate, - }; - } - }; - - pub fn toU32(self: Instruction) u32 { - return switch (self) { - .DataProcessing => |v| @bitCast(u32, v), - .SingleDataTransfer => |v| @bitCast(u32, v), - .Branch => |v| @bitCast(u32, v), - .BranchExchange => |v| @bitCast(u32, v), - .SupervisorCall => |v| @bitCast(u32, v), - .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20), - }; - } - - // Helper functions for the "real" functions below - - fn dataProcessing( - cond: Condition, - opcode: Opcode, - s: u1, - rd: Register, - rn: Register, - op2: Operand, - ) Instruction { - return Instruction{ - .DataProcessing = .{ - .cond = @enumToInt(cond), - .i = if (op2 == .Immediate) 1 else 0, - .opcode = @enumToInt(opcode), - .s = s, - .rn = rn.id(), - .rd = rd.id(), - .op2 = op2.toU12(), - }, - }; - } - - fn singleDataTransfer( - cond: Condition, - rd: Register, - rn: Register, - offset: Offset, - pre_post: u1, - up_down: u1, - byte_word: u1, - writeback: u1, - load_store: u1, - ) Instruction { - return Instruction{ - .SingleDataTransfer = .{ - .cond = @enumToInt(cond), - .rn = rn.id(), - .rd = rd.id(), - .offset = offset.toU12(), - .l = load_store, - .w = writeback, - .b = byte_word, - .u = up_down, - .p = pre_post, - .i = if (offset == .Immediate) 0 else 1, - }, - }; - } - - fn branch(cond: Condition, offset: i24, link: u1) Instruction { - return Instruction{ - .Branch = .{ - .cond = @enumToInt(cond), - .link = link, - .offset = @bitCast(u24, offset), - }, - }; - } - - fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction { - return Instruction{ - .BranchExchange = .{ - .cond = @enumToInt(cond), - .link = link, - .rn = rn.id(), - }, - }; - } - - fn supervisorCall(cond: Condition, comment: u24) Instruction { - return Instruction{ - .SupervisorCall = .{ - .cond = @enumToInt(cond), - .comment = comment, - }, - }; - } - - fn breakpoint(imm: u16) Instruction { - return Instruction{ - .Breakpoint = .{ - .imm12 = @truncate(u12, imm >> 4), - .imm4 = @truncate(u4, imm), - }, - }; - } - - // Public functions replicating assembler syntax as closely as - // possible - - // Data processing - - pub fn @"and"(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .@"and", s, rd, rn, op2); - } - - pub fn eor(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .eor, s, rd, rn, op2); - } - - pub fn sub(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .sub, s, rd, rn, op2); - } - - pub fn rsb(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .rsb, s, rd, rn, op2); - } - - pub fn add(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .add, s, rd, rn, op2); - } - - pub fn adc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .adc, s, rd, rn, op2); - } - - pub fn sbc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .sbc, s, rd, rn, op2); - } - - pub fn rsc(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .rsc, s, rd, rn, op2); - } - - pub fn tst(cond: Condition, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .tst, 1, .r0, rn, op2); - } - - pub fn teq(cond: Condition, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .teq, 1, .r0, rn, op2); - } - - pub fn cmp(cond: Condition, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .cmp, 1, .r0, rn, op2); - } - - pub fn cmn(cond: Condition, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .cmn, 1, .r0, rn, op2); - } - - pub fn orr(cond: Condition, s: u1, rd: Register, rn: Register, op2: Operand) Instruction { - return dataProcessing(cond, .orr, s, rd, rn, op2); - } - - pub fn mov(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .mov, s, rd, .r0, op2); - } - - pub fn bic(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .bic, s, rd, rn, op2); - } - - pub fn mvn(cond: Condition, s: u1, rd: Register, op2: Operand) Instruction { - return dataProcessing(cond, .mvn, s, rd, .r0, op2); - } - - // Single data transfer - - pub fn ldr(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { - return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 1); - } - - pub fn str(cond: Condition, rd: Register, rn: Register, offset: Offset) Instruction { - return singleDataTransfer(cond, rd, rn, offset, 1, 1, 0, 0, 0); - } - - // Branch - - pub fn b(cond: Condition, offset: i24) Instruction { - return branch(cond, offset, 0); - } - - pub fn bl(cond: Condition, offset: i24) Instruction { - return branch(cond, offset, 1); - } - - // Branch and exchange - - pub fn bx(cond: Condition, rn: Register) Instruction { - return branchExchange(cond, rn, 0); - } - - pub fn blx(cond: Condition, rn: Register) Instruction { - return branchExchange(cond, rn, 1); - } - - // Supervisor Call - - pub const swi = svc; - - pub fn svc(cond: Condition, comment: u24) Instruction { - return supervisorCall(cond, comment); - } - - // Breakpoint - - pub fn bkpt(imm: u16) Instruction { - return breakpoint(imm); - } -}; - -test "serialize instructions" { - const Testcase = struct { - inst: Instruction, - expected: u32, - }; - - const testcases = [_]Testcase{ - .{ // add r0, r0, r0 - .inst = Instruction.add(.al, 0, .r0, .r0, Instruction.Operand.reg(.r0, Instruction.Operand.Shift.none)), - .expected = 0b1110_00_0_0100_0_0000_0000_00000000_0000, - }, - .{ // mov r4, r2 - .inst = Instruction.mov(.al, 0, .r4, Instruction.Operand.reg(.r2, Instruction.Operand.Shift.none)), - .expected = 0b1110_00_0_1101_0_0000_0100_00000000_0010, - }, - .{ // mov r0, #42 - .inst = Instruction.mov(.al, 0, .r0, Instruction.Operand.imm(42, 0)), - .expected = 0b1110_00_1_1101_0_0000_0000_0000_00101010, - }, - .{ // ldr r0, [r2, #42] - .inst = Instruction.ldr(.al, .r0, .r2, Instruction.Offset.imm(42)), - .expected = 0b1110_01_0_1_1_0_0_1_0010_0000_000000101010, - }, - .{ // str r0, [r3] - .inst = Instruction.str(.al, .r0, .r3, Instruction.Offset.none), - .expected = 0b1110_01_0_1_1_0_0_0_0011_0000_000000000000, - }, - .{ // b #12 - .inst = Instruction.b(.al, 12), - .expected = 0b1110_101_0_0000_0000_0000_0000_0000_1100, - }, - .{ // bl #-4 - .inst = Instruction.bl(.al, -4), - .expected = 0b1110_101_1_1111_1111_1111_1111_1111_1100, - }, - .{ // bx lr - .inst = Instruction.bx(.al, .lr), - .expected = 0b1110_0001_0010_1111_1111_1111_0001_1110, - }, - .{ // svc #0 - .inst = Instruction.svc(.al, 0), - .expected = 0b1110_1111_0000_0000_0000_0000_0000_0000, - }, - .{ // bkpt #42 - .inst = Instruction.bkpt(42), - .expected = 0b1110_0001_0010_000000000010_0111_1010, - }, - }; - - for (testcases) |case| { - const actual = case.inst.toU32(); - testing.expectEqual(case.expected, actual); - } -} diff --git a/src-self-hosted/codegen/c.zig b/src-self-hosted/codegen/c.zig deleted file mode 100644 index 34ddcfbb3b..0000000000 --- a/src-self-hosted/codegen/c.zig +++ /dev/null @@ -1,299 +0,0 @@ -const std = @import("std"); - -const link = @import("../link.zig"); -const Module = @import("../Module.zig"); - -const Inst = @import("../ir.zig").Inst; -const Value = @import("../value.zig").Value; -const Type = @import("../type.zig").Type; - -const C = link.File.C; -const Decl = Module.Decl; -const mem = std.mem; - -/// Maps a name from Zig source to C. Currently, this will always give the same -/// output for any given input, sometimes resulting in broken identifiers. -fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { - return allocator.dupe(u8, name); -} - -fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void { - switch (T.zigTypeTag()) { - .NoReturn => { - try writer.writeAll("zig_noreturn void"); - }, - .Void => try writer.writeAll("void"), - .Int => { - if (T.tag() == .u8) { - ctx.file.need_stdint = true; - try writer.writeAll("uint8_t"); - } else if (T.tag() == .usize) { - ctx.file.need_stddef = true; - try writer.writeAll("size_t"); - } else { - return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{}); - } - }, - else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}), - } -} - -fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void { - switch (T.zigTypeTag()) { - .Int => { - if (T.isSignedInt()) - return writer.print("{}", .{val.toSignedInt()}); - return writer.print("{}", .{val.toUnsignedInt()}); - }, - else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement value {}", .{e}), - } -} - -fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { - const tv = decl.typed_value.most_recent.typed_value; - try renderType(ctx, writer, tv.ty.fnReturnType()); - const name = try map(ctx.file.base.allocator, mem.spanZ(decl.name)); - defer ctx.file.base.allocator.free(name); - try writer.print(" {}(", .{name}); - var param_len = tv.ty.fnParamLen(); - if (param_len == 0) - try writer.writeAll("void") - else { - var index: usize = 0; - while (index < param_len) : (index += 1) { - if (index > 0) { - try writer.writeAll(", "); - } - try renderType(ctx, writer, tv.ty.fnParamType(index)); - try writer.print(" arg{}", .{index}); - } - } - try writer.writeByte(')'); -} - -pub fn generate(file: *C, decl: *Decl) !void { - switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { - .Fn => try genFn(file, decl), - .Array => try genArray(file, decl), - else => |e| return file.fail(decl.src(), "TODO {}", .{e}), - } -} - -fn genArray(file: *C, decl: *Decl) !void { - const tv = decl.typed_value.most_recent.typed_value; - // TODO: prevent inline asm constants from being emitted - const name = try map(file.base.allocator, mem.span(decl.name)); - defer file.base.allocator.free(name); - if (tv.val.cast(Value.Payload.Bytes)) |payload| - if (tv.ty.sentinel()) |sentinel| - if (sentinel.toUnsignedInt() == 0) - try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data }) - else - return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{}) - else - return file.fail(decl.src(), "TODO byte arrays without sentinels", .{}) - else - return file.fail(decl.src(), "TODO non-byte arrays", .{}); -} - -const Context = struct { - file: *C, - decl: *Decl, - inst_map: std.AutoHashMap(*Inst, []u8), - argdex: usize = 0, - unnamed_index: usize = 0, - - fn name(self: *Context) ![]u8 { - const val = try std.fmt.allocPrint(self.file.base.allocator, "__temp_{}", .{self.unnamed_index}); - self.unnamed_index += 1; - return val; - } - - fn deinit(self: *Context) void { - var it = self.inst_map.iterator(); - while (it.next()) |kv| { - self.file.base.allocator.free(kv.value); - } - self.inst_map.deinit(); - self.* = undefined; - } -}; - -fn genFn(file: *C, decl: *Decl) !void { - const writer = file.main.writer(); - const tv = decl.typed_value.most_recent.typed_value; - - var ctx = Context{ - .file = file, - .decl = decl, - .inst_map = std.AutoHashMap(*Inst, []u8).init(file.base.allocator), - }; - defer ctx.deinit(); - - try renderFunctionSignature(&ctx, writer, decl); - - try writer.writeAll(" {"); - - const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func; - const instructions = func.analysis.success.instructions; - if (instructions.len > 0) { - try writer.writeAll("\n"); - for (instructions) |inst| { - if (switch (inst.tag) { - .assembly => try genAsm(&ctx, inst.castTag(.assembly).?), - .call => try genCall(&ctx, inst.castTag(.call).?), - .ret => try genRet(&ctx, inst.castTag(.ret).?), - .retvoid => try genRetVoid(&ctx), - .arg => try genArg(&ctx), - .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?), - .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?), - .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?), - .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?), - else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}), - }) |name| { - try ctx.inst_map.putNoClobber(inst, name); - } - } - } - - try writer.writeAll("}\n\n"); -} - -fn genArg(ctx: *Context) !?[]u8 { - const name = try std.fmt.allocPrint(ctx.file.base.allocator, "arg{}", .{ctx.argdex}); - ctx.argdex += 1; - return name; -} - -fn genRetVoid(ctx: *Context) !?[]u8 { - try ctx.file.main.writer().print(" return;\n", .{}); - return null; -} - -fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { - return ctx.file.fail(ctx.decl.src(), "TODO return", .{}); -} - -fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { - if (inst.base.isUnused()) - return null; - const op = inst.operand; - const writer = ctx.file.main.writer(); - const name = try ctx.name(); - const from = ctx.inst_map.get(op) orelse - return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: intCast argument not found in inst_map", .{}); - try writer.writeAll(" const "); - try renderType(ctx, writer, inst.base.ty); - try writer.print(" {} = (", .{name}); - try renderType(ctx, writer, inst.base.ty); - try writer.print("){};\n", .{from}); - return name; -} - -fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { - const writer = ctx.file.main.writer(); - const header = ctx.file.header.writer(); - try writer.writeAll(" "); - if (inst.func.castTag(.constant)) |func_inst| { - if (func_inst.val.cast(Value.Payload.Function)) |func_val| { - const target = func_val.func.owner_decl; - const target_ty = target.typed_value.most_recent.typed_value.ty; - const ret_ty = target_ty.fnReturnType().tag(); - if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) { - try writer.print("(void)", .{}); - } - const tname = mem.spanZ(target.name); - if (ctx.file.called.get(tname) == null) { - try ctx.file.called.put(tname, void{}); - try renderFunctionSignature(ctx, header, target); - try header.writeAll(";\n"); - } - try writer.print("{}(", .{tname}); - if (inst.args.len != 0) { - for (inst.args) |arg, i| { - if (i > 0) { - try writer.writeAll(", "); - } - if (arg.cast(Inst.Constant)) |con| { - try renderValue(ctx, writer, arg.ty, con.val); - } else { - return ctx.file.fail(ctx.decl.src(), "TODO call pass arg {}", .{arg}); - } - } - } - try writer.writeAll(");\n"); - } else { - return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{}); - } - } else { - return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{}); - } - return null; -} - -fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { - // TODO emit #line directive here with line number and filename - return null; -} - -fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { - // TODO ?? - return null; -} - -fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { - try ctx.file.main.writer().writeAll(" zig_unreachable();\n"); - return null; -} - -fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { - const writer = ctx.file.main.writer(); - try writer.writeAll(" "); - for (as.inputs) |i, index| { - if (i[0] == '{' and i[i.len - 1] == '}') { - const reg = i[1 .. i.len - 1]; - const arg = as.args[index]; - try writer.writeAll("register "); - try renderType(ctx, writer, arg.ty); - try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); - // TODO merge constant handling into inst_map as well - if (arg.castTag(.constant)) |c| { - try renderValue(ctx, writer, arg.ty, c.val); - try writer.writeAll(";\n "); - } else { - const gop = try ctx.inst_map.getOrPut(arg); - if (!gop.found_existing) { - return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); - } - try writer.print("{};\n ", .{gop.entry.value}); - } - } else { - return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); - } - } - try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); - if (as.output) |o| { - return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{}); - } - if (as.inputs.len > 0) { - if (as.output == null) { - try writer.writeAll(" :"); - } - try writer.writeAll(": "); - for (as.inputs) |i, index| { - if (i[0] == '{' and i[i.len - 1] == '}') { - const reg = i[1 .. i.len - 1]; - const arg = as.args[index]; - if (index > 0) { - try writer.writeAll(", "); - } - try writer.print("\"\"({}_constant)", .{reg}); - } else { - // This is blocked by the earlier test - unreachable; - } - } - } - try writer.writeAll(");\n"); - return null; -} diff --git a/src-self-hosted/codegen/llvm.zig b/src-self-hosted/codegen/llvm.zig deleted file mode 100644 index 01fa0baf02..0000000000 --- a/src-self-hosted/codegen/llvm.zig +++ /dev/null @@ -1,125 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; - -pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 { - const llvm_arch = switch (target.cpu.arch) { - .arm => "arm", - .armeb => "armeb", - .aarch64 => "aarch64", - .aarch64_be => "aarch64_be", - .aarch64_32 => "aarch64_32", - .arc => "arc", - .avr => "avr", - .bpfel => "bpfel", - .bpfeb => "bpfeb", - .hexagon => "hexagon", - .mips => "mips", - .mipsel => "mipsel", - .mips64 => "mips64", - .mips64el => "mips64el", - .msp430 => "msp430", - .powerpc => "powerpc", - .powerpc64 => "powerpc64", - .powerpc64le => "powerpc64le", - .r600 => "r600", - .amdgcn => "amdgcn", - .riscv32 => "riscv32", - .riscv64 => "riscv64", - .sparc => "sparc", - .sparcv9 => "sparcv9", - .sparcel => "sparcel", - .s390x => "s390x", - .tce => "tce", - .tcele => "tcele", - .thumb => "thumb", - .thumbeb => "thumbeb", - .i386 => "i386", - .x86_64 => "x86_64", - .xcore => "xcore", - .nvptx => "nvptx", - .nvptx64 => "nvptx64", - .le32 => "le32", - .le64 => "le64", - .amdil => "amdil", - .amdil64 => "amdil64", - .hsail => "hsail", - .hsail64 => "hsail64", - .spir => "spir", - .spir64 => "spir64", - .kalimba => "kalimba", - .shave => "shave", - .lanai => "lanai", - .wasm32 => "wasm32", - .wasm64 => "wasm64", - .renderscript32 => "renderscript32", - .renderscript64 => "renderscript64", - .ve => "ve", - .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, - }; - // TODO Add a sub-arch for some architectures depending on CPU features. - - const llvm_os = switch (target.os.tag) { - .freestanding => "unknown", - .ananas => "ananas", - .cloudabi => "cloudabi", - .dragonfly => "dragonfly", - .freebsd => "freebsd", - .fuchsia => "fuchsia", - .ios => "ios", - .kfreebsd => "kfreebsd", - .linux => "linux", - .lv2 => "lv2", - .macosx => "macosx", - .netbsd => "netbsd", - .openbsd => "openbsd", - .solaris => "solaris", - .windows => "windows", - .haiku => "haiku", - .minix => "minix", - .rtems => "rtems", - .nacl => "nacl", - .cnk => "cnk", - .aix => "aix", - .cuda => "cuda", - .nvcl => "nvcl", - .amdhsa => "amdhsa", - .ps4 => "ps4", - .elfiamcu => "elfiamcu", - .tvos => "tvos", - .watchos => "watchos", - .mesa3d => "mesa3d", - .contiki => "contiki", - .amdpal => "amdpal", - .hermit => "hermit", - .hurd => "hurd", - .wasi => "wasi", - .emscripten => "emscripten", - .uefi => "windows", - .other => "unknown", - }; - - const llvm_abi = switch (target.abi) { - .none => "unknown", - .gnu => "gnu", - .gnuabin32 => "gnuabin32", - .gnuabi64 => "gnuabi64", - .gnueabi => "gnueabi", - .gnueabihf => "gnueabihf", - .gnux32 => "gnux32", - .code16 => "code16", - .eabi => "eabi", - .eabihf => "eabihf", - .android => "android", - .musl => "musl", - .musleabi => "musleabi", - .musleabihf => "musleabihf", - .msvc => "msvc", - .itanium => "itanium", - .cygnus => "cygnus", - .coreclr => "coreclr", - .simulator => "simulator", - .macabi => "macabi", - }; - - return std.fmt.allocPrint(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi }); -} diff --git a/src-self-hosted/codegen/riscv64.zig b/src-self-hosted/codegen/riscv64.zig deleted file mode 100644 index 96b9c58f9c..0000000000 --- a/src-self-hosted/codegen/riscv64.zig +++ /dev/null @@ -1,433 +0,0 @@ -const std = @import("std"); -const DW = std.dwarf; - -// TODO: this is only tagged to facilitate the monstrosity. -// Once packed structs work make it packed. -pub const Instruction = union(enum) { - R: packed struct { - opcode: u7, - rd: u5, - funct3: u3, - rs1: u5, - rs2: u5, - funct7: u7, - }, - I: packed struct { - opcode: u7, - rd: u5, - funct3: u3, - rs1: u5, - imm0_11: u12, - }, - S: packed struct { - opcode: u7, - imm0_4: u5, - funct3: u3, - rs1: u5, - rs2: u5, - imm5_11: u7, - }, - B: packed struct { - opcode: u7, - imm11: u1, - imm1_4: u4, - funct3: u3, - rs1: u5, - rs2: u5, - imm5_10: u6, - imm12: u1, - }, - U: packed struct { - opcode: u7, - rd: u5, - imm12_31: u20, - }, - J: packed struct { - opcode: u7, - rd: u5, - imm12_19: u8, - imm11: u1, - imm1_10: u10, - imm20: u1, - }, - - // TODO: once packed structs work we can remove this monstrosity. - pub fn toU32(self: Instruction) u32 { - return switch (self) { - .R => |v| @bitCast(u32, v), - .I => |v| @bitCast(u32, v), - .S => |v| @bitCast(u32, v), - .B => |v| @intCast(u32, v.opcode) + (@intCast(u32, v.imm11) << 7) + (@intCast(u32, v.imm1_4) << 8) + (@intCast(u32, v.funct3) << 12) + (@intCast(u32, v.rs1) << 15) + (@intCast(u32, v.rs2) << 20) + (@intCast(u32, v.imm5_10) << 25) + (@intCast(u32, v.imm12) << 31), - .U => |v| @bitCast(u32, v), - .J => |v| @bitCast(u32, v), - }; - } - - fn rType(op: u7, fn3: u3, fn7: u7, rd: Register, r1: Register, r2: Register) Instruction { - return Instruction{ - .R = .{ - .opcode = op, - .funct3 = fn3, - .funct7 = fn7, - .rd = @enumToInt(rd), - .rs1 = @enumToInt(r1), - .rs2 = @enumToInt(r2), - }, - }; - } - - // RISC-V is all signed all the time -- convert immediates to unsigned for processing - fn iType(op: u7, fn3: u3, rd: Register, r1: Register, imm: i12) Instruction { - const umm = @bitCast(u12, imm); - - return Instruction{ - .I = .{ - .opcode = op, - .funct3 = fn3, - .rd = @enumToInt(rd), - .rs1 = @enumToInt(r1), - .imm0_11 = umm, - }, - }; - } - - fn sType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i12) Instruction { - const umm = @bitCast(u12, imm); - - return Instruction{ - .S = .{ - .opcode = op, - .funct3 = fn3, - .rs1 = @enumToInt(r1), - .rs2 = @enumToInt(r2), - .imm0_4 = @truncate(u5, umm), - .imm5_11 = @truncate(u7, umm >> 5), - }, - }; - } - - // Use significance value rather than bit value, same for J-type - // -- less burden on callsite, bonus semantic checking - fn bType(op: u7, fn3: u3, r1: Register, r2: Register, imm: i13) Instruction { - const umm = @bitCast(u13, imm); - if (umm % 2 != 0) @panic("Internal error: misaligned branch target"); - - return Instruction{ - .B = .{ - .opcode = op, - .funct3 = fn3, - .rs1 = @enumToInt(r1), - .rs2 = @enumToInt(r2), - .imm1_4 = @truncate(u4, umm >> 1), - .imm5_10 = @truncate(u6, umm >> 5), - .imm11 = @truncate(u1, umm >> 11), - .imm12 = @truncate(u1, umm >> 12), - }, - }; - } - - // We have to extract the 20 bits anyway -- let's not make it more painful - fn uType(op: u7, rd: Register, imm: i20) Instruction { - const umm = @bitCast(u20, imm); - - return Instruction{ - .U = .{ - .opcode = op, - .rd = @enumToInt(rd), - .imm12_31 = umm, - }, - }; - } - - fn jType(op: u7, rd: Register, imm: i21) Instruction { - const umm = @bitcast(u21, imm); - if (umm % 2 != 0) @panic("Internal error: misaligned jump target"); - - return Instruction{ - .J = .{ - .opcode = op, - .rd = @enumToInt(rd), - .imm1_10 = @truncate(u10, umm >> 1), - .imm11 = @truncate(u1, umm >> 1), - .imm12_19 = @truncate(u8, umm >> 12), - .imm20 = @truncate(u1, umm >> 20), - }, - }; - } - - // The meat and potatoes. Arguments are in the order in which they would appear in assembly code. - - // Arithmetic/Logical, Register-Register - - pub fn add(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b000, 0b0000000, rd, r1, r2); - } - - pub fn sub(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b000, 0b0100000, rd, r1, r2); - } - - pub fn @"and"(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b111, 0b0000000, rd, r1, r2); - } - - pub fn @"or"(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b110, 0b0000000, rd, r1, r2); - } - - pub fn xor(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b100, 0b0000000, rd, r1, r2); - } - - pub fn sll(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b001, 0b0000000, rd, r1, r2); - } - - pub fn srl(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b101, 0b0000000, rd, r1, r2); - } - - pub fn sra(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b101, 0b0100000, rd, r1, r2); - } - - pub fn slt(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b010, 0b0000000, rd, r1, r2); - } - - pub fn sltu(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0110011, 0b011, 0b0000000, rd, r1, r2); - } - - // Arithmetic/Logical, Register-Register (32-bit) - - pub fn addw(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0111011, 0b000, rd, r1, r2); - } - - pub fn subw(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0111011, 0b000, 0b0100000, rd, r1, r2); - } - - pub fn sllw(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0111011, 0b001, 0b0000000, rd, r1, r2); - } - - pub fn srlw(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0111011, 0b101, 0b0000000, rd, r1, r2); - } - - pub fn sraw(rd: Register, r1: Register, r2: Register) Instruction { - return rType(0b0111011, 0b101, 0b0100000, rd, r1, r2); - } - - // Arithmetic/Logical, Register-Immediate - - pub fn addi(rd: Register, r1: Register, imm: i12) Instruction { - return iType(0b0010011, 0b000, rd, r1, imm); - } - - pub fn andi(rd: Register, r1: Register, imm: i12) Instruction { - return iType(0b0010011, 0b111, rd, r1, imm); - } - - pub fn ori(rd: Register, r1: Register, imm: i12) Instruction { - return iType(0b0010011, 0b110, rd, r1, imm); - } - - pub fn xori(rd: Register, r1: Register, imm: i12) Instruction { - return iType(0b0010011, 0b100, rd, r1, imm); - } - - pub fn slli(rd: Register, r1: Register, shamt: u6) Instruction { - return iType(0b0010011, 0b001, rd, r1, shamt); - } - - pub fn srli(rd: Register, r1: Register, shamt: u6) Instruction { - return iType(0b0010011, 0b101, rd, r1, shamt); - } - - pub fn srai(rd: Register, r1: Register, shamt: u6) Instruction { - return iType(0b0010011, 0b101, rd, r1, (1 << 10) + shamt); - } - - pub fn slti(rd: Register, r1: Register, imm: i12) Instruction { - return iType(0b0010011, 0b010, rd, r1, imm); - } - - pub fn sltiu(rd: Register, r1: Register, imm: u12) Instruction { - return iType(0b0010011, 0b011, rd, r1, @bitCast(i12, imm)); - } - - // Arithmetic/Logical, Register-Immediate (32-bit) - - pub fn addiw(rd: Register, r1: Register, imm: i12) Instruction { - return iType(0b0011011, 0b000, rd, r1, imm); - } - - pub fn slliw(rd: Register, r1: Register, shamt: u5) Instruction { - return iType(0b0011011, 0b001, rd, r1, shamt); - } - - pub fn srliw(rd: Register, r1: Register, shamt: u5) Instruction { - return iType(0b0011011, 0b101, rd, r1, shamt); - } - - pub fn sraiw(rd: Register, r1: Register, shamt: u5) Instruction { - return iType(0b0011011, 0b101, rd, r1, (1 << 10) + shamt); - } - - // Upper Immediate - - pub fn lui(rd: Register, imm: i20) Instruction { - return uType(0b0110111, rd, imm); - } - - pub fn auipc(rd: Register, imm: i20) Instruction { - return uType(0b0010111, rd, imm); - } - - // Load - - pub fn ld(rd: Register, offset: i12, base: Register) Instruction { - return iType(0b0000011, 0b011, rd, base, offset); - } - - pub fn lw(rd: Register, offset: i12, base: Register) Instruction { - return iType(0b0000011, 0b010, rd, base, offset); - } - - pub fn lwu(rd: Register, offset: i12, base: Register) Instruction { - return iType(0b0000011, 0b110, rd, base, offset); - } - - pub fn lh(rd: Register, offset: i12, base: Register) Instruction { - return iType(0b0000011, 0b001, rd, base, offset); - } - - pub fn lhu(rd: Register, offset: i12, base: Register) Instruction { - return iType(0b0000011, 0b101, rd, base, offset); - } - - pub fn lb(rd: Register, offset: i12, base: Register) Instruction { - return iType(0b0000011, 0b000, rd, base, offset); - } - - pub fn lbu(rd: Register, offset: i12, base: Register) Instruction { - return iType(0b0000011, 0b100, rd, base, offset); - } - - // Store - - pub fn sd(rs: Register, offset: i12, base: Register) Instruction { - return sType(0b0100011, 0b011, base, rs, offset); - } - - pub fn sw(rs: Register, offset: i12, base: Register) Instruction { - return sType(0b0100011, 0b010, base, rs, offset); - } - - pub fn sh(rs: Register, offset: i12, base: Register) Instruction { - return sType(0b0100011, 0b001, base, rs, offset); - } - - pub fn sb(rs: Register, offset: i12, base: Register) Instruction { - return sType(0b0100011, 0b000, base, rs, offset); - } - - // Fence - // TODO: implement fence - - // Branch - - pub fn beq(r1: Register, r2: Register, offset: u13) Instruction { - return bType(0b1100011, 0b000, r1, r2, offset); - } - - pub fn bne(r1: Register, r2: Register, offset: u13) Instruction { - return bType(0b1100011, 0b001, r1, r2, offset); - } - - pub fn blt(r1: Register, r2: Register, offset: u13) Instruction { - return bType(0b1100011, 0b100, r1, r2, offset); - } - - pub fn bge(r1: Register, r2: Register, offset: u13) Instruction { - return bType(0b1100011, 0b101, r1, r2, offset); - } - - pub fn bltu(r1: Register, r2: Register, offset: u13) Instruction { - return bType(0b1100011, 0b110, r1, r2, offset); - } - - pub fn bgeu(r1: Register, r2: Register, offset: u13) Instruction { - return bType(0b1100011, 0b111, r1, r2, offset); - } - - // Jump - - pub fn jal(link: Register, offset: i21) Instruction { - return jType(0b1101111, link, offset); - } - - pub fn jalr(link: Register, offset: i12, base: Register) Instruction { - return iType(0b1100111, 0b000, link, base, offset); - } - - // System - - pub const ecall = iType(0b1110011, 0b000, .zero, .zero, 0x000); - pub const ebreak = iType(0b1110011, 0b000, .zero, .zero, 0x001); -}; - -// zig fmt: off -pub const RawRegister = enum(u5) { - x0, x1, x2, x3, x4, x5, x6, x7, - x8, x9, x10, x11, x12, x13, x14, x15, - x16, x17, x18, x19, x20, x21, x22, x23, - x24, x25, x26, x27, x28, x29, x30, x31, - - pub fn dwarfLocOp(reg: RawRegister) u8 { - return @enumToInt(reg) + DW.OP_reg0; - } -}; - -pub const Register = enum(u5) { - // 64 bit registers - zero, // zero - ra, // return address. caller saved - sp, // stack pointer. callee saved. - gp, // global pointer - tp, // thread pointer - t0, t1, t2, // temporaries. caller saved. - s0, // s0/fp, callee saved. - s1, // callee saved. - a0, a1, // fn args/return values. caller saved. - a2, a3, a4, a5, a6, a7, // fn args. caller saved. - s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved. - t3, t4, t5, t6, // caller saved - - pub fn parseRegName(name: []const u8) ?Register { - if(std.meta.stringToEnum(Register, name)) |reg| return reg; - if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg)); - return null; - } - - /// Returns the index into `callee_preserved_regs`. - pub fn allocIndex(self: Register) ?u4 { - inline for(callee_preserved_regs) |cpreg, i| { - if(self == cpreg) return i; - } - return null; - } - - pub fn dwarfLocOp(reg: Register) u8 { - return @as(u8, @enumToInt(reg)) + DW.OP_reg0; - } -}; - -// zig fmt: on - -pub const callee_preserved_regs = [_]Register{ - .s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11, -}; diff --git a/src-self-hosted/codegen/spu-mk2.zig b/src-self-hosted/codegen/spu-mk2.zig deleted file mode 100644 index 542862caca..0000000000 --- a/src-self-hosted/codegen/spu-mk2.zig +++ /dev/null @@ -1,170 +0,0 @@ -const std = @import("std"); - -pub const Interpreter = @import("spu-mk2/interpreter.zig").Interpreter; - -pub const ExecutionCondition = enum(u3) { - always = 0, - when_zero = 1, - not_zero = 2, - greater_zero = 3, - less_than_zero = 4, - greater_or_equal_zero = 5, - less_or_equal_zero = 6, - overflow = 7, -}; - -pub const InputBehaviour = enum(u2) { - zero = 0, - immediate = 1, - peek = 2, - pop = 3, -}; - -pub const OutputBehaviour = enum(u2) { - discard = 0, - push = 1, - jump = 2, - jump_relative = 3, -}; - -pub const Command = enum(u5) { - copy = 0, - ipget = 1, - get = 2, - set = 3, - store8 = 4, - store16 = 5, - load8 = 6, - load16 = 7, - undefined0 = 8, - undefined1 = 9, - frget = 10, - frset = 11, - bpget = 12, - bpset = 13, - spget = 14, - spset = 15, - add = 16, - sub = 17, - mul = 18, - div = 19, - mod = 20, - @"and" = 21, - @"or" = 22, - xor = 23, - not = 24, - signext = 25, - rol = 26, - ror = 27, - bswap = 28, - asr = 29, - lsl = 30, - lsr = 31, -}; - -pub const Instruction = packed struct { - condition: ExecutionCondition, - input0: InputBehaviour, - input1: InputBehaviour, - modify_flags: bool, - output: OutputBehaviour, - command: Command, - reserved: u1 = 0, - - pub fn format(instr: Instruction, comptime fmt: []const u8, options: std.fmt.FormatOptions, out: anytype) !void { - try std.fmt.format(out, "0x{x:0<4} ", .{@bitCast(u16, instr)}); - try out.writeAll(switch (instr.condition) { - .always => " ", - .when_zero => "== 0", - .not_zero => "!= 0", - .greater_zero => " > 0", - .less_than_zero => " < 0", - .greater_or_equal_zero => ">= 0", - .less_or_equal_zero => "<= 0", - .overflow => "ovfl", - }); - try out.writeAll(" "); - try out.writeAll(switch (instr.input0) { - .zero => "zero", - .immediate => "imm ", - .peek => "peek", - .pop => "pop ", - }); - try out.writeAll(" "); - try out.writeAll(switch (instr.input1) { - .zero => "zero", - .immediate => "imm ", - .peek => "peek", - .pop => "pop ", - }); - try out.writeAll(" "); - try out.writeAll(switch (instr.command) { - .copy => "copy ", - .ipget => "ipget ", - .get => "get ", - .set => "set ", - .store8 => "store8 ", - .store16 => "store16 ", - .load8 => "load8 ", - .load16 => "load16 ", - .undefined0 => "undefined", - .undefined1 => "undefined", - .frget => "frget ", - .frset => "frset ", - .bpget => "bpget ", - .bpset => "bpset ", - .spget => "spget ", - .spset => "spset ", - .add => "add ", - .sub => "sub ", - .mul => "mul ", - .div => "div ", - .mod => "mod ", - .@"and" => "and ", - .@"or" => "or ", - .xor => "xor ", - .not => "not ", - .signext => "signext ", - .rol => "rol ", - .ror => "ror ", - .bswap => "bswap ", - .asr => "asr ", - .lsl => "lsl ", - .lsr => "lsr ", - }); - try out.writeAll(" "); - try out.writeAll(switch (instr.output) { - .discard => "discard", - .push => "push ", - .jump => "jmp ", - .jump_relative => "rjmp ", - }); - try out.writeAll(" "); - try out.writeAll(if (instr.modify_flags) - "+ flags" - else - " "); - } -}; - -pub const FlagRegister = packed struct { - zero: bool, - negative: bool, - carry: bool, - carry_enabled: bool, - interrupt0_enabled: bool, - interrupt1_enabled: bool, - interrupt2_enabled: bool, - interrupt3_enabled: bool, - reserved: u8 = 0, -}; - -pub const Register = enum { - dummy, - - pub fn allocIndex(self: Register) ?u4 { - return null; - } -}; - -pub const callee_preserved_regs = [_]Register{}; diff --git a/src-self-hosted/codegen/spu-mk2/interpreter.zig b/src-self-hosted/codegen/spu-mk2/interpreter.zig deleted file mode 100644 index 1ec99546c6..0000000000 --- a/src-self-hosted/codegen/spu-mk2/interpreter.zig +++ /dev/null @@ -1,166 +0,0 @@ -const std = @import("std"); -const log = std.log.scoped(.SPU_2_Interpreter); -const spu = @import("../spu-mk2.zig"); -const FlagRegister = spu.FlagRegister; -const Instruction = spu.Instruction; -const ExecutionCondition = spu.ExecutionCondition; - -pub fn Interpreter(comptime Bus: type) type { - return struct { - ip: u16 = 0, - sp: u16 = undefined, - bp: u16 = undefined, - fr: FlagRegister = @bitCast(FlagRegister, @as(u16, 0)), - /// This is set to true when we hit an undefined0 instruction, allowing it to - /// be used as a trap for testing purposes - undefined0: bool = false, - /// This is set to true when we hit an undefined1 instruction, allowing it to - /// be used as a trap for testing purposes. undefined1 is used as a breakpoint. - undefined1: bool = false, - bus: Bus, - - pub fn ExecuteBlock(self: *@This(), comptime size: ?u32) !void { - var count: usize = 0; - while (size == null or count < size.?) { - count += 1; - var instruction = @bitCast(Instruction, self.bus.read16(self.ip)); - - log.debug("Executing {}\n", .{instruction}); - - self.ip +%= 2; - - const execute = switch (instruction.condition) { - .always => true, - .not_zero => !self.fr.zero, - .when_zero => self.fr.zero, - .overflow => self.fr.carry, - ExecutionCondition.greater_or_equal_zero => !self.fr.negative, - else => return error.Unimplemented, - }; - - if (execute) { - const val0 = switch (instruction.input0) { - .zero => @as(u16, 0), - .immediate => i: { - const val = self.bus.read16(@intCast(u16, self.ip)); - self.ip +%= 2; - break :i val; - }, - else => |e| e: { - // peek or pop; show value at current SP, and if pop, increment sp - const val = self.bus.read16(self.sp); - if (e == .pop) { - self.sp +%= 2; - } - break :e val; - }, - }; - const val1 = switch (instruction.input1) { - .zero => @as(u16, 0), - .immediate => i: { - const val = self.bus.read16(@intCast(u16, self.ip)); - self.ip +%= 2; - break :i val; - }, - else => |e| e: { - // peek or pop; show value at current SP, and if pop, increment sp - const val = self.bus.read16(self.sp); - if (e == .pop) { - self.sp +%= 2; - } - break :e val; - }, - }; - - const output: u16 = switch (instruction.command) { - .get => self.bus.read16(self.bp +% (2 *% val0)), - .set => a: { - self.bus.write16(self.bp +% 2 *% val0, val1); - break :a val1; - }, - .load8 => self.bus.read8(val0), - .load16 => self.bus.read16(val0), - .store8 => a: { - const val = @truncate(u8, val1); - self.bus.write8(val0, val); - break :a val; - }, - .store16 => a: { - self.bus.write16(val0, val1); - break :a val1; - }, - .copy => val0, - .add => a: { - var val: u16 = undefined; - self.fr.carry = @addWithOverflow(u16, val0, val1, &val); - break :a val; - }, - .sub => a: { - var val: u16 = undefined; - self.fr.carry = @subWithOverflow(u16, val0, val1, &val); - break :a val; - }, - .spset => a: { - self.sp = val0; - break :a val0; - }, - .bpset => a: { - self.bp = val0; - break :a val0; - }, - .frset => a: { - const val = (@bitCast(u16, self.fr) & val1) | (val0 & ~val1); - self.fr = @bitCast(FlagRegister, val); - break :a val; - }, - .bswap => (val0 >> 8) | (val0 << 8), - .bpget => self.bp, - .spget => self.sp, - .ipget => self.ip +% (2 *% val0), - .lsl => val0 << 1, - .lsr => val0 >> 1, - .@"and" => val0 & val1, - .@"or" => val0 | val1, - .xor => val0 ^ val1, - .not => ~val0, - .undefined0 => { - self.undefined0 = true; - // Break out of the loop, and let the caller decide what to do - return; - }, - .undefined1 => { - self.undefined1 = true; - // Break out of the loop, and let the caller decide what to do - return; - }, - .signext => if ((val0 & 0x80) != 0) - (val0 & 0xFF) | 0xFF00 - else - (val0 & 0xFF), - else => return error.Unimplemented, - }; - - switch (instruction.output) { - .discard => {}, - .push => { - self.sp -%= 2; - self.bus.write16(self.sp, output); - }, - .jump => { - self.ip = output; - }, - else => return error.Unimplemented, - } - if (instruction.modify_flags) { - self.fr.negative = (output & 0x8000) != 0; - self.fr.zero = (output == 0x0000); - } - } else { - if (instruction.input0 == .immediate) self.ip +%= 2; - if (instruction.input1 == .immediate) self.ip +%= 2; - break; - } - } - } - }; -} diff --git a/src-self-hosted/codegen/wasm.zig b/src-self-hosted/codegen/wasm.zig deleted file mode 100644 index 4ea8838409..0000000000 --- a/src-self-hosted/codegen/wasm.zig +++ /dev/null @@ -1,142 +0,0 @@ -const std = @import("std"); -const Allocator = std.mem.Allocator; -const ArrayList = std.ArrayList; -const assert = std.debug.assert; -const leb = std.debug.leb; -const mem = std.mem; - -const Module = @import("../Module.zig"); -const Decl = Module.Decl; -const Inst = @import("../ir.zig").Inst; -const Type = @import("../type.zig").Type; -const Value = @import("../value.zig").Value; - -fn genValtype(ty: Type) u8 { - return switch (ty.tag()) { - .u32, .i32 => 0x7F, - .u64, .i64 => 0x7E, - .f32 => 0x7D, - .f64 => 0x7C, - else => @panic("TODO: Implement more types for wasm."), - }; -} - -pub fn genFunctype(buf: *ArrayList(u8), decl: *Decl) !void { - const ty = decl.typed_value.most_recent.typed_value.ty; - const writer = buf.writer(); - - // functype magic - try writer.writeByte(0x60); - - // param types - try leb.writeULEB128(writer, @intCast(u32, ty.fnParamLen())); - if (ty.fnParamLen() != 0) { - const params = try buf.allocator.alloc(Type, ty.fnParamLen()); - defer buf.allocator.free(params); - ty.fnParamTypes(params); - for (params) |param_type| try writer.writeByte(genValtype(param_type)); - } - - // return type - const return_type = ty.fnReturnType(); - switch (return_type.tag()) { - .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)), - else => { - try leb.writeULEB128(writer, @as(u32, 1)); - try writer.writeByte(genValtype(return_type)); - }, - } -} - -pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void { - assert(buf.items.len == 0); - const writer = buf.writer(); - - // Reserve space to write the size after generating the code - try buf.resize(5); - - // Write the size of the locals vec - // TODO: implement locals - try leb.writeULEB128(writer, @as(u32, 0)); - - // Write instructions - // TODO: check for and handle death of instructions - const tv = decl.typed_value.most_recent.typed_value; - const mod_fn = tv.val.cast(Value.Payload.Function).?.func; - for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst); - - // Write 'end' opcode - try writer.writeByte(0x0B); - - // Fill in the size of the generated code to the reserved space at the - // beginning of the buffer. - const size = buf.items.len - 5 + decl.fn_link.wasm.?.idx_refs.items.len * 5; - leb.writeUnsignedFixed(5, buf.items[0..5], @intCast(u32, size)); -} - -fn genInst(buf: *ArrayList(u8), decl: *Decl, inst: *Inst) !void { - return switch (inst.tag) { - .call => genCall(buf, decl, inst.castTag(.call).?), - .constant => genConstant(buf, decl, inst.castTag(.constant).?), - .dbg_stmt => {}, - .ret => genRet(buf, decl, inst.castTag(.ret).?), - .retvoid => {}, - else => error.TODOImplementMoreWasmCodegen, - }; -} - -fn genConstant(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Constant) !void { - const writer = buf.writer(); - switch (inst.base.ty.tag()) { - .u32 => { - try writer.writeByte(0x41); // i32.const - try leb.writeILEB128(writer, inst.val.toUnsignedInt()); - }, - .i32 => { - try writer.writeByte(0x41); // i32.const - try leb.writeILEB128(writer, inst.val.toSignedInt()); - }, - .u64 => { - try writer.writeByte(0x42); // i64.const - try leb.writeILEB128(writer, inst.val.toUnsignedInt()); - }, - .i64 => { - try writer.writeByte(0x42); // i64.const - try leb.writeILEB128(writer, inst.val.toSignedInt()); - }, - .f32 => { - try writer.writeByte(0x43); // f32.const - // TODO: enforce LE byte order - try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32))); - }, - .f64 => { - try writer.writeByte(0x44); // f64.const - // TODO: enforce LE byte order - try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64))); - }, - .void => {}, - else => return error.TODOImplementMoreWasmCodegen, - } -} - -fn genRet(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.UnOp) !void { - try genInst(buf, decl, inst.operand); -} - -fn genCall(buf: *ArrayList(u8), decl: *Decl, inst: *Inst.Call) !void { - const func_inst = inst.func.castTag(.constant).?; - const func_val = func_inst.val.cast(Value.Payload.Function).?; - const target = func_val.func.owner_decl; - const target_ty = target.typed_value.most_recent.typed_value.ty; - - if (inst.args.len != 0) return error.TODOImplementMoreWasmCodegen; - - try buf.append(0x10); // call - - // The function index immediate argument will be filled in using this data - // in link.Wasm.flush(). - try decl.fn_link.wasm.?.idx_refs.append(buf.allocator, .{ - .offset = @intCast(u32, buf.items.len), - .decl = target, - }); -} diff --git a/src-self-hosted/codegen/x86.zig b/src-self-hosted/codegen/x86.zig deleted file mode 100644 index fdad4e56db..0000000000 --- a/src-self-hosted/codegen/x86.zig +++ /dev/null @@ -1,123 +0,0 @@ -const std = @import("std"); -const DW = std.dwarf; - -// zig fmt: off -pub const Register = enum(u8) { - // 0 through 7, 32-bit registers. id is int value - eax, ecx, edx, ebx, esp, ebp, esi, edi, - - // 8-15, 16-bit registers. id is int value - 8. - ax, cx, dx, bx, sp, bp, si, di, - - // 16-23, 8-bit registers. id is int value - 16. - al, cl, dl, bl, ah, ch, dh, bh, - - /// Returns the bit-width of the register. - pub fn size(self: @This()) u7 { - return switch (@enumToInt(self)) { - 0...7 => 32, - 8...15 => 16, - 16...23 => 8, - else => unreachable, - }; - } - - /// Returns the register's id. This is used in practically every opcode the - /// x86 has. It is embedded in some instructions, such as the `B8 +rd` move - /// instruction, and is used in the R/M byte. - pub fn id(self: @This()) u3 { - return @truncate(u3, @enumToInt(self)); - } - - /// Returns the index into `callee_preserved_regs`. - pub fn allocIndex(self: Register) ?u4 { - return switch (self) { - .eax, .ax, .al => 0, - .ecx, .cx, .cl => 1, - .edx, .dx, .dl => 2, - .esi, .si => 3, - .edi, .di => 4, - else => null, - }; - } - - /// Convert from any register to its 32 bit alias. - pub fn to32(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id())); - } - - /// Convert from any register to its 16 bit alias. - pub fn to16(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 8); - } - - /// Convert from any register to its 8 bit alias. - pub fn to8(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 16); - } - - - pub fn dwarfLocOp(reg: Register) u8 { - return switch (reg.to32()) { - .eax => DW.OP_reg0, - .ecx => DW.OP_reg1, - .edx => DW.OP_reg2, - .ebx => DW.OP_reg3, - .esp => DW.OP_reg4, - .ebp => DW.OP_reg5, - .esi => DW.OP_reg6, - .edi => DW.OP_reg7, - else => unreachable, - }; - } -}; - -// zig fmt: on - -pub const callee_preserved_regs = [_]Register{ .eax, .ecx, .edx, .esi, .edi }; - -// TODO add these to Register enum and corresponding dwarfLocOp -// // Return Address register. This is stored in `0(%esp, "")` and is not a physical register. -// RA = (8, "RA"), -// -// ST0 = (11, "st0"), -// ST1 = (12, "st1"), -// ST2 = (13, "st2"), -// ST3 = (14, "st3"), -// ST4 = (15, "st4"), -// ST5 = (16, "st5"), -// ST6 = (17, "st6"), -// ST7 = (18, "st7"), -// -// XMM0 = (21, "xmm0"), -// XMM1 = (22, "xmm1"), -// XMM2 = (23, "xmm2"), -// XMM3 = (24, "xmm3"), -// XMM4 = (25, "xmm4"), -// XMM5 = (26, "xmm5"), -// XMM6 = (27, "xmm6"), -// XMM7 = (28, "xmm7"), -// -// MM0 = (29, "mm0"), -// MM1 = (30, "mm1"), -// MM2 = (31, "mm2"), -// MM3 = (32, "mm3"), -// MM4 = (33, "mm4"), -// MM5 = (34, "mm5"), -// MM6 = (35, "mm6"), -// MM7 = (36, "mm7"), -// -// MXCSR = (39, "mxcsr"), -// -// ES = (40, "es"), -// CS = (41, "cs"), -// SS = (42, "ss"), -// DS = (43, "ds"), -// FS = (44, "fs"), -// GS = (45, "gs"), -// -// TR = (48, "tr"), -// LDTR = (49, "ldtr"), -// -// FS_BASE = (93, "fs.base"), -// GS_BASE = (94, "gs.base"), diff --git a/src-self-hosted/codegen/x86_64.zig b/src-self-hosted/codegen/x86_64.zig deleted file mode 100644 index dea39f82cd..0000000000 --- a/src-self-hosted/codegen/x86_64.zig +++ /dev/null @@ -1,220 +0,0 @@ -const std = @import("std"); -const Type = @import("../Type.zig"); -const DW = std.dwarf; - -// zig fmt: off - -/// Definitions of all of the x64 registers. The order is semantically meaningful. -/// The registers are defined such that IDs go in descending order of 64-bit, -/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen -/// registers. This results in some useful properties: -/// -/// Any 64-bit register can be turned into its 32-bit form by adding 16, and -/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it -/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit -/// form. -/// -/// If (register & 8) is set, the register is extended. -/// -/// The ID can be easily determined by figuring out what range the register is -/// in, and then subtracting the base. -pub const Register = enum(u8) { - // 0 through 15, 64-bit registers. 8-15 are extended. - // id is just the int value. - rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, - r8, r9, r10, r11, r12, r13, r14, r15, - - // 16 through 31, 32-bit registers. 24-31 are extended. - // id is int value - 16. - eax, ecx, edx, ebx, esp, ebp, esi, edi, - r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d, - - // 32-47, 16-bit registers. 40-47 are extended. - // id is int value - 32. - ax, cx, dx, bx, sp, bp, si, di, - r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w, - - // 48-63, 8-bit registers. 56-63 are extended. - // id is int value - 48. - al, cl, dl, bl, ah, ch, dh, bh, - r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b, - - /// Returns the bit-width of the register. - pub fn size(self: Register) u7 { - return switch (@enumToInt(self)) { - 0...15 => 64, - 16...31 => 32, - 32...47 => 16, - 48...64 => 8, - else => unreachable, - }; - } - - /// Returns whether the register is *extended*. Extended registers are the - /// new registers added with amd64, r8 through r15. This also includes any - /// other variant of access to those registers, such as r8b, r15d, and so - /// on. This is needed because access to these registers requires special - /// handling via the REX prefix, via the B or R bits, depending on context. - pub fn isExtended(self: Register) bool { - return @enumToInt(self) & 0x08 != 0; - } - - /// This returns the 4-bit register ID, which is used in practically every - /// opcode. Note that bit 3 (the highest bit) is *never* used directly in - /// an instruction (@see isExtended), and requires special handling. The - /// lower three bits are often embedded directly in instructions (such as - /// the B8 variant of moves), or used in R/M bytes. - pub fn id(self: Register) u4 { - return @truncate(u4, @enumToInt(self)); - } - - /// Returns the index into `callee_preserved_regs`. - pub fn allocIndex(self: Register) ?u4 { - return switch (self) { - .rax, .eax, .ax, .al => 0, - .rcx, .ecx, .cx, .cl => 1, - .rdx, .edx, .dx, .dl => 2, - .rsi, .esi, .si => 3, - .rdi, .edi, .di => 4, - .r8, .r8d, .r8w, .r8b => 5, - .r9, .r9d, .r9w, .r9b => 6, - .r10, .r10d, .r10w, .r10b => 7, - .r11, .r11d, .r11w, .r11b => 8, - else => null, - }; - } - - /// Convert from any register to its 64 bit alias. - pub fn to64(self: Register) Register { - return @intToEnum(Register, self.id()); - } - - /// Convert from any register to its 32 bit alias. - pub fn to32(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 16); - } - - /// Convert from any register to its 16 bit alias. - pub fn to16(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 32); - } - - /// Convert from any register to its 8 bit alias. - pub fn to8(self: Register) Register { - return @intToEnum(Register, @as(u8, self.id()) + 48); - } - - pub fn dwarfLocOp(self: Register) u8 { - return switch (self.to64()) { - .rax => DW.OP_reg0, - .rdx => DW.OP_reg1, - .rcx => DW.OP_reg2, - .rbx => DW.OP_reg3, - .rsi => DW.OP_reg4, - .rdi => DW.OP_reg5, - .rbp => DW.OP_reg6, - .rsp => DW.OP_reg7, - - .r8 => DW.OP_reg8, - .r9 => DW.OP_reg9, - .r10 => DW.OP_reg10, - .r11 => DW.OP_reg11, - .r12 => DW.OP_reg12, - .r13 => DW.OP_reg13, - .r14 => DW.OP_reg14, - .r15 => DW.OP_reg15, - - else => unreachable, - }; - } -}; - -// zig fmt: on - -/// These registers belong to the called function. -pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 }; -pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; -pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx }; - -// TODO add these registers to the enum and populate dwarfLocOp -// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register. -// RA = (16, "RA"), -// -// XMM0 = (17, "xmm0"), -// XMM1 = (18, "xmm1"), -// XMM2 = (19, "xmm2"), -// XMM3 = (20, "xmm3"), -// XMM4 = (21, "xmm4"), -// XMM5 = (22, "xmm5"), -// XMM6 = (23, "xmm6"), -// XMM7 = (24, "xmm7"), -// -// XMM8 = (25, "xmm8"), -// XMM9 = (26, "xmm9"), -// XMM10 = (27, "xmm10"), -// XMM11 = (28, "xmm11"), -// XMM12 = (29, "xmm12"), -// XMM13 = (30, "xmm13"), -// XMM14 = (31, "xmm14"), -// XMM15 = (32, "xmm15"), -// -// ST0 = (33, "st0"), -// ST1 = (34, "st1"), -// ST2 = (35, "st2"), -// ST3 = (36, "st3"), -// ST4 = (37, "st4"), -// ST5 = (38, "st5"), -// ST6 = (39, "st6"), -// ST7 = (40, "st7"), -// -// MM0 = (41, "mm0"), -// MM1 = (42, "mm1"), -// MM2 = (43, "mm2"), -// MM3 = (44, "mm3"), -// MM4 = (45, "mm4"), -// MM5 = (46, "mm5"), -// MM6 = (47, "mm6"), -// MM7 = (48, "mm7"), -// -// RFLAGS = (49, "rFLAGS"), -// ES = (50, "es"), -// CS = (51, "cs"), -// SS = (52, "ss"), -// DS = (53, "ds"), -// FS = (54, "fs"), -// GS = (55, "gs"), -// -// FS_BASE = (58, "fs.base"), -// GS_BASE = (59, "gs.base"), -// -// TR = (62, "tr"), -// LDTR = (63, "ldtr"), -// MXCSR = (64, "mxcsr"), -// FCW = (65, "fcw"), -// FSW = (66, "fsw"), -// -// XMM16 = (67, "xmm16"), -// XMM17 = (68, "xmm17"), -// XMM18 = (69, "xmm18"), -// XMM19 = (70, "xmm19"), -// XMM20 = (71, "xmm20"), -// XMM21 = (72, "xmm21"), -// XMM22 = (73, "xmm22"), -// XMM23 = (74, "xmm23"), -// XMM24 = (75, "xmm24"), -// XMM25 = (76, "xmm25"), -// XMM26 = (77, "xmm26"), -// XMM27 = (78, "xmm27"), -// XMM28 = (79, "xmm28"), -// XMM29 = (80, "xmm29"), -// XMM30 = (81, "xmm30"), -// XMM31 = (82, "xmm31"), -// -// K0 = (118, "k0"), -// K1 = (119, "k1"), -// K2 = (120, "k2"), -// K3 = (121, "k3"), -// K4 = (122, "k4"), -// K5 = (123, "k5"), -// K6 = (124, "k6"), -// K7 = (125, "k7"), |
