Skip to content

Commit

Permalink
Merge pull request #9105 from g-w1/plan9
Browse files Browse the repository at this point in the history
stage2: bringup plan9 target and linker
  • Loading branch information
andrewrk authored Jul 8, 2021
2 parents b4b90af + 476faef commit 7b8a968
Show file tree
Hide file tree
Showing 14 changed files with 703 additions and 33 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,8 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/link/C.zig"
"${CMAKE_SOURCE_DIR}/src/link/Coff.zig"
"${CMAKE_SOURCE_DIR}/src/link/Elf.zig"
"${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
"${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
"${CMAKE_SOURCE_DIR}/src/link/MachO/CodeSignature.zig"
Expand Down
63 changes: 40 additions & 23 deletions lib/std/start.zig
Original file line number Diff line number Diff line change
Expand Up @@ -95,30 +95,47 @@ fn _start2() callconv(.Naked) noreturn {
}

fn exit2(code: usize) noreturn {
switch (builtin.stage2_arch) {
.x86_64 => {
asm volatile ("syscall"
:
: [number] "{rax}" (231),
[arg1] "{rdi}" (code)
: "rcx", "r11", "memory"
);
},
.arm => {
asm volatile ("svc #0"
:
: [number] "{r7}" (1),
[arg1] "{r0}" (code)
: "memory"
);
switch (builtin.stage2_os) {
.linux => switch (builtin.stage2_arch) {
.x86_64 => {
asm volatile ("syscall"
:
: [number] "{rax}" (231),
[arg1] "{rdi}" (code)
: "rcx", "r11", "memory"
);
},
.arm => {
asm volatile ("svc #0"
:
: [number] "{r7}" (1),
[arg1] "{r0}" (code)
: "memory"
);
},
.aarch64 => {
asm volatile ("svc #0"
:
: [number] "{x8}" (93),
[arg1] "{x0}" (code)
: "memory", "cc"
);
},
else => @compileError("TODO"),
},
.aarch64 => {
asm volatile ("svc #0"
:
: [number] "{x8}" (93),
[arg1] "{x0}" (code)
: "memory", "cc"
);
// exits(0)
.plan9 => switch (builtin.stage2_arch) {
.x86_64 => {
asm volatile (
\\push $0
\\push $0
\\syscall
:
: [syscall_number] "{rbp}" (8)
: "rcx", "r11", "memory"
);
},
else => @compileError("TODO"),
},
else => @compileError("TODO"),
}
Expand Down
9 changes: 9 additions & 0 deletions lib/std/target.zig
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub const Target = struct {
opencl,
glsl450,
vulkan,
plan9,
other,

pub fn isDarwin(tag: Tag) bool {
Expand Down Expand Up @@ -262,6 +263,7 @@ pub const Target = struct {
.opencl, // TODO: OpenCL versions
.glsl450, // TODO: GLSL versions
.vulkan,
.plan9,
.other,
=> return .{ .none = {} },

Expand Down Expand Up @@ -420,6 +422,7 @@ pub const Target = struct {
.opencl,
.glsl450,
.vulkan,
.plan9,
.other,
=> false,
};
Expand Down Expand Up @@ -515,6 +518,7 @@ pub const Target = struct {
.opencl, // TODO: SPIR-V ABIs with Linkage capability
.glsl450,
.vulkan,
.plan9, // TODO specify abi
=> return .none,
}
}
Expand Down Expand Up @@ -554,6 +558,7 @@ pub const Target = struct {
spirv,
hex,
raw,
plan9,
};

pub const SubSystem = enum {
Expand Down Expand Up @@ -1359,6 +1364,8 @@ pub const Target = struct {
if (cpu_arch.isSPIRV()) {
return .spirv;
}
if (os_tag == .plan9)
return .plan9;
return .elf;
}

Expand Down Expand Up @@ -1432,6 +1439,7 @@ pub const Target = struct {
.opencl,
.glsl450,
.vulkan,
.plan9,
.other,
=> return false,
else => return true,
Expand Down Expand Up @@ -1616,6 +1624,7 @@ pub const Target = struct {
.glsl450,
.vulkan,
.other,
.plan9,
=> return result,

// TODO revisit when multi-arch for Haiku is available
Expand Down
24 changes: 24 additions & 0 deletions lib/std/zig.zig
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,30 @@ pub fn binNameAlloc(allocator: *std.mem.Allocator, options: BinNameOptions) erro
.spirv => return std.fmt.allocPrint(allocator, "{s}.spv", .{root_name}),
.hex => return std.fmt.allocPrint(allocator, "{s}.ihex", .{root_name}),
.raw => return std.fmt.allocPrint(allocator, "{s}.bin", .{root_name}),
.plan9 => {
// copied from 2c(1)
// 0c spim little-endian MIPS 3000 family
// 1c 68000 Motorola MC68000
// 2c 68020 Motorola MC68020
// 5c arm little-endian ARM
// 6c amd64 AMD64 and compatibles (e.g., Intel EM64T)
// 7c arm64 ARM64 (ARMv8)
// 8c 386 Intel i386, i486, Pentium, etc.
// kc sparc Sun SPARC
// qc power Power PC
// vc mips big-endian MIPS 3000 family
const char: u8 = switch (target.cpu.arch) {
.arm => '5',
.x86_64 => '6',
.aarch64 => '7',
.i386 => '8',
.sparc => 'k',
.powerpc, .powerpcle => 'q',
.mips, .mipsel => 'v',
else => 'X', // this arch does not have a char or maybe was not ported to plan9 so we just use X
};
return std.fmt.allocPrint(allocator, "{s}.{c}", .{ root_name, char });
},
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/std/zig/cross_target.zig
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ pub const CrossTarget = struct {
.opencl,
.glsl450,
.vulkan,
.plan9,
.other,
=> {
self.os_version_min = .{ .none = {} };
Expand Down Expand Up @@ -746,6 +747,7 @@ pub const CrossTarget = struct {
.opencl,
.glsl450,
.vulkan,
.plan9,
.other,
=> return error.InvalidOperatingSystemVersion,

Expand Down
3 changes: 3 additions & 0 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3556,6 +3556,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
\\pub const zig_is_stage2 = {};
\\/// Temporary until self-hosted supports the `cpu.arch` value.
\\pub const stage2_arch: std.Target.Cpu.Arch = .{};
\\/// Temporary until self-hosted supports the `os.tag` value.
\\pub const stage2_os: std.Target.Os.Tag = .{};
\\
\\pub const output_mode = std.builtin.OutputMode.{};
\\pub const link_mode = std.builtin.LinkMode.{};
Expand All @@ -3571,6 +3573,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
build_options.version,
!use_stage1,
std.zig.fmtId(@tagName(target.cpu.arch)),
std.zig.fmtId(@tagName(target.os.tag)),
std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),
std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),
comp.bin_file.options.is_test,
Expand Down
8 changes: 8 additions & 0 deletions src/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3437,6 +3437,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo
// in `Decl` to notice that the line number did not change.
mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl });
},
.plan9 => {
// TODO implement for plan9
},
.c, .wasm, .spirv => {},
}
}
Expand Down Expand Up @@ -3514,6 +3517,7 @@ pub fn clearDecl(
.coff => .{ .coff = link.File.Coff.TextBlock.empty },
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
.plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
.c => .{ .c = link.File.C.DeclBlock.empty },
.wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
.spirv => .{ .spirv = {} },
Expand All @@ -3522,6 +3526,7 @@ pub fn clearDecl(
.coff => .{ .coff = {} },
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
.plan9 => .{ .plan9 = {} },
.c => .{ .c = link.File.C.FnBlock.empty },
.wasm => .{ .wasm = link.File.Wasm.FnData.empty },
.spirv => .{ .spirv = .{} },
Expand Down Expand Up @@ -3689,6 +3694,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
.coff => .{ .coff = link.File.Coff.TextBlock.empty },
.elf => .{ .elf = link.File.Elf.TextBlock.empty },
.macho => .{ .macho = link.File.MachO.TextBlock.empty },
.plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
.c => .{ .c = link.File.C.DeclBlock.empty },
.wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
.spirv => .{ .spirv = {} },
Expand All @@ -3697,6 +3703,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node
.coff => .{ .coff = {} },
.elf => .{ .elf = link.File.Elf.SrcFn.empty },
.macho => .{ .macho = link.File.MachO.SrcFn.empty },
.plan9 => .{ .plan9 = {} },
.c => .{ .c = link.File.C.FnBlock.empty },
.wasm => .{ .wasm = link.File.Wasm.FnData.empty },
.spirv => .{ .spirv = .{} },
Expand Down Expand Up @@ -3766,6 +3773,7 @@ pub fn analyzeExport(
.coff => .{ .coff = {} },
.elf => .{ .elf = link.File.Elf.Export{} },
.macho => .{ .macho = link.File.MachO.Export{} },
.plan9 => .{ .plan9 = null },
.c => .{ .c = {} },
.wasm => .{ .wasm = {} },
.spirv => .{ .spirv = {} },
Expand Down
103 changes: 96 additions & 7 deletions src/codegen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2556,9 +2556,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
} else {
return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
}
} else {
unreachable;
}
} else if (self.bin_file.cast(link.File.Plan9)) |p9| {
switch (arch) {
.x86_64 => {
for (info.args) |mc_arg, arg_i| {
const arg = inst.args[arg_i];
const arg_mcv = try self.resolveInst(inst.args[arg_i]);
// Here we do not use setRegOrMem even though the logic is similar, because
// the function call will move the stack pointer, so the offsets are different.
switch (mc_arg) {
.none => continue,
.register => |reg| {
try self.register_manager.getReg(reg, null);
try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
},
.stack_offset => {
// Here we need to emit instructions like this:
// mov qword ptr [rsp + stack_offset], x
return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
},
.ptr_stack_offset => {
return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
},
.ptr_embedded_in_code => {
return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
},
.undef => unreachable,
.immediate => unreachable,
.unreach => unreachable,
.dead => unreachable,
.embedded_in_code => unreachable,
.memory => unreachable,
.compare_flags_signed => unreachable,
.compare_flags_unsigned => unreachable,
}
}
if (inst.func.value()) |func_value| {
if (func_value.castTag(.function)) |func_payload| {
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
const got_addr = p9.bases.data;
const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
// ff 14 25 xx xx xx xx call [addr]
try self.code.ensureCapacity(self.code.items.len + 7);
self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
const fn_got_addr = got_addr + got_index * ptr_bytes;
mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr));
} else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{});
} else {
return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
}
},
else => return self.fail(inst.base.src, "TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
}
} else unreachable;

switch (info.return_value) {
.register => |reg| {
Expand Down Expand Up @@ -3279,10 +3330,44 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);
}

if (mem.eql(u8, inst.asm_source, "syscall")) {
try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
} else if (inst.asm_source.len != 0) {
return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
{
var iter = std.mem.tokenize(inst.asm_source, "\n\r");
while (iter.next()) |ins| {
if (mem.eql(u8, ins, "syscall")) {
try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
} else if (mem.indexOf(u8, ins, "push")) |_| {
const arg = ins[4..];
if (mem.indexOf(u8, arg, "$")) |l| {
const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch return self.fail(inst.base.src, "TODO implement more inline asm int parsing", .{});
try self.code.appendSlice(&.{ 0x6a, n });
} else if (mem.indexOf(u8, arg, "%%")) |l| {
const reg_name = ins[4 + l + 2 ..];
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const low_id: u8 = reg.low_id();
if (reg.isExtended()) {
try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id });
} else {
try self.code.append(0b1010000 | low_id);
}
} else return self.fail(inst.base.src, "TODO more push operands", .{});
} else if (mem.indexOf(u8, ins, "pop")) |_| {
const arg = ins[3..];
if (mem.indexOf(u8, arg, "%%")) |l| {
const reg_name = ins[3 + l + 2 ..];
const reg = parseRegName(reg_name) orelse
return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});
const low_id: u8 = reg.low_id();
if (reg.isExtended()) {
try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id });
} else {
try self.code.append(0b1011000 | low_id);
}
} else return self.fail(inst.base.src, "TODO more pop operands", .{});
} else {
return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
}
}
}

if (inst.output_constraint) |output| {
Expand Down Expand Up @@ -4223,6 +4308,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const decl = payload.data;
const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
return MCValue{ .memory = got_addr };
} else if (self.bin_file.cast(link.File.Plan9)) |p9| {
const decl = payload.data;
const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
return MCValue{ .memory = got_addr };
} else {
return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});
}
Expand Down
1 change: 1 addition & 0 deletions src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 {
.opencl => return error.LLVMBackendDoesNotSupportOpenCL,
.glsl450 => return error.LLVMBackendDoesNotSupportGLSL450,
.vulkan => return error.LLVMBackendDoesNotSupportVulkan,
.plan9 => return error.LLVMBackendDoesNotSupportPlan9,
.other => "unknown",
};

Expand Down
Loading

0 comments on commit 7b8a968

Please sign in to comment.