Commit 63b54bcf51

Andrew Kelley <andrew@ziglang.org>
2020-04-24 00:58:47
codegen for inline assembly
1 parent 99ec614
Changed files (1)
src-self-hosted
src-self-hosted/codegen.zig
@@ -4,6 +4,7 @@ const assert = std.debug.assert;
 const ir = @import("ir.zig");
 const Type = @import("type.zig").Type;
 const Value = @import("value.zig").Value;
+const Target = std.Target;
 
 pub const ErrorMsg = struct {
     byte_offset: usize,
@@ -69,6 +70,9 @@ const Function = struct {
         immediate: u64,
         /// The constant was emitted into the code, at this offset.
         embedded_in_code: usize,
+        /// The value is in a target-specific register. The value can
+        /// be @intToEnum casted to the respective Reg enum.
+        register: usize,
     };
 
     fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
@@ -108,14 +112,9 @@ const Function = struct {
                     try self.code.resize(self.code.items.len + 2);
                     self.code.items[self.code.items.len - 2] = 0xeb;
                     self.code.items[self.code.items.len - 1] = @intCast(u8, amount);
-                } else if (amount <= std.math.maxInt(u16)) {
-                    try self.code.resize(self.code.items.len + 3);
-                    self.code.items[self.code.items.len - 3] = 0xe9; // jmp rel16
-                    const imm_ptr = self.code.items[self.code.items.len - 2 ..][0..2];
-                    mem.writeIntLittle(u16, imm_ptr, @intCast(u16, amount));
                 } else {
                     try self.code.resize(self.code.items.len + 5);
-                    self.code.items[self.code.items.len - 5] = 0xea; // jmp rel32
+                    self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
                     const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
                     mem.writeIntLittle(u32, imm_ptr, amount);
                 }
@@ -125,7 +124,104 @@ const Function = struct {
     }
 
     fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue {
-        return self.fail(inst.base.src, "TODO machine code gen assembly", .{});
+        // TODO convert to inline function
+        switch (self.module.target.cpu.arch) {
+            .arm => return self.genAsmArch(.arm, inst),
+            .armeb => return self.genAsmArch(.armeb, inst),
+            .aarch64 => return self.genAsmArch(.aarch64, inst),
+            .aarch64_be => return self.genAsmArch(.aarch64_be, inst),
+            .aarch64_32 => return self.genAsmArch(.aarch64_32, inst),
+            .arc => return self.genAsmArch(.arc, inst),
+            .avr => return self.genAsmArch(.avr, inst),
+            .bpfel => return self.genAsmArch(.bpfel, inst),
+            .bpfeb => return self.genAsmArch(.bpfeb, inst),
+            .hexagon => return self.genAsmArch(.hexagon, inst),
+            .mips => return self.genAsmArch(.mips, inst),
+            .mipsel => return self.genAsmArch(.mipsel, inst),
+            .mips64 => return self.genAsmArch(.mips64, inst),
+            .mips64el => return self.genAsmArch(.mips64el, inst),
+            .msp430 => return self.genAsmArch(.msp430, inst),
+            .powerpc => return self.genAsmArch(.powerpc, inst),
+            .powerpc64 => return self.genAsmArch(.powerpc64, inst),
+            .powerpc64le => return self.genAsmArch(.powerpc64le, inst),
+            .r600 => return self.genAsmArch(.r600, inst),
+            .amdgcn => return self.genAsmArch(.amdgcn, inst),
+            .riscv32 => return self.genAsmArch(.riscv32, inst),
+            .riscv64 => return self.genAsmArch(.riscv64, inst),
+            .sparc => return self.genAsmArch(.sparc, inst),
+            .sparcv9 => return self.genAsmArch(.sparcv9, inst),
+            .sparcel => return self.genAsmArch(.sparcel, inst),
+            .s390x => return self.genAsmArch(.s390x, inst),
+            .tce => return self.genAsmArch(.tce, inst),
+            .tcele => return self.genAsmArch(.tcele, inst),
+            .thumb => return self.genAsmArch(.thumb, inst),
+            .thumbeb => return self.genAsmArch(.thumbeb, inst),
+            .i386 => return self.genAsmArch(.i386, inst),
+            .x86_64 => return self.genAsmArch(.x86_64, inst),
+            .xcore => return self.genAsmArch(.xcore, inst),
+            .nvptx => return self.genAsmArch(.nvptx, inst),
+            .nvptx64 => return self.genAsmArch(.nvptx64, inst),
+            .le32 => return self.genAsmArch(.le32, inst),
+            .le64 => return self.genAsmArch(.le64, inst),
+            .amdil => return self.genAsmArch(.amdil, inst),
+            .amdil64 => return self.genAsmArch(.amdil64, inst),
+            .hsail => return self.genAsmArch(.hsail, inst),
+            .hsail64 => return self.genAsmArch(.hsail64, inst),
+            .spir => return self.genAsmArch(.spir, inst),
+            .spir64 => return self.genAsmArch(.spir64, inst),
+            .kalimba => return self.genAsmArch(.kalimba, inst),
+            .shave => return self.genAsmArch(.shave, inst),
+            .lanai => return self.genAsmArch(.lanai, inst),
+            .wasm32 => return self.genAsmArch(.wasm32, inst),
+            .wasm64 => return self.genAsmArch(.wasm64, inst),
+            .renderscript32 => return self.genAsmArch(.renderscript32, inst),
+            .renderscript64 => return self.genAsmArch(.renderscript64, inst),
+            .ve => return self.genAsmArch(.ve, inst),
+        }
+    }
+
+    fn genAsmArch(self: *Function, comptime arch: Target.Cpu.Arch, inst: *ir.Inst.Assembly) !MCValue {
+        if (arch != .x86_64 and arch != .i386) {
+            return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
+        }
+        if (!mem.eql(u8, inst.args.asm_source, "syscall")) {
+            return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
+        }
+        for (inst.args.inputs) |input, i| {
+            if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
+                return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
+            }
+            const reg_name = input[1 .. input.len - 1];
+            const reg = parseRegName(arch, reg_name) orelse
+                return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
+            const arg = try self.resolveInst(inst.args.args[i]);
+            try self.genSetReg(inst.base.src, arch, reg, arg);
+        }
+
+        if (inst.args.output) |output| {
+            if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
+                return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
+            }
+            const reg_name = output[2 .. output.len - 1];
+            const reg = parseRegName(arch, reg_name) orelse
+                return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
+            return MCValue{ .register = @enumToInt(reg) };
+        } else {
+            return MCValue.none;
+        }
+    }
+
+    fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) !void {
+        switch (arch) {
+            .x86_64 => switch (reg) {
+                .rax => return self.fail(src, "TODO implement genSetReg for x86_64 'rax'", .{}),
+                .rdi => return self.fail(src, "TODO implement genSetReg for x86_64 'rdi'", .{}),
+                .rsi => return self.fail(src, "TODO implement genSetReg for x86_64 'rsi'", .{}),
+                .rdx => return self.fail(src, "TODO implement genSetReg for x86_64 'rdx'", .{}),
+                else => return self.fail(src, "TODO implement genSetReg for x86_64 '{}'", .{@tagName(reg)}),
+            },
+            else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
+        }
     }
 
     fn genPtrToInt(self: *Function, inst: *ir.Inst.PtrToInt) !MCValue {
@@ -192,3 +288,112 @@ const Function = struct {
         return error.CodegenFail;
     }
 };
+
+fn Reg(comptime arch: Target.Cpu.Arch) type {
+    return switch (arch) {
+        .i386 => enum {
+            eax,
+            ebx,
+            ecx,
+            edx,
+            ebp,
+            esp,
+            esi,
+            edi,
+
+            ax,
+            bx,
+            cx,
+            dx,
+            bp,
+            sp,
+            si,
+            di,
+
+            ah,
+            bh,
+            ch,
+            dh,
+
+            al,
+            bl,
+            cl,
+            dl,
+        },
+        .x86_64 => enum {
+            rax,
+            rbx,
+            rcx,
+            rdx,
+            rbp,
+            rsp,
+            rsi,
+            rdi,
+            r8,
+            r9,
+            r10,
+            r11,
+            r12,
+            r13,
+            r14,
+            r15,
+
+            eax,
+            ebx,
+            ecx,
+            edx,
+            ebp,
+            esp,
+            esi,
+            edi,
+            r8d,
+            r9d,
+            r10d,
+            r11d,
+            r12d,
+            r13d,
+            r14d,
+            r15d,
+
+            ax,
+            bx,
+            cx,
+            dx,
+            bp,
+            sp,
+            si,
+            di,
+            r8w,
+            r9w,
+            r10w,
+            r11w,
+            r12w,
+            r13w,
+            r14w,
+            r15w,
+
+            ah,
+            bh,
+            ch,
+            dh,
+
+            al,
+            bl,
+            cl,
+            dl,
+            r8b,
+            r9b,
+            r10b,
+            r11b,
+            r12b,
+            r13b,
+            r14b,
+            r15b,
+        },
+        else => @compileError("TODO add more register enums"),
+    };
+}
+
+fn parseRegName(comptime arch: Target.Cpu.Arch, name: []const u8) ?Reg(arch) {
+    return std.meta.stringToEnum(Reg(arch), name);
+}