Commit f89dbe6c4e
Changed files (4)
lib
std
src-self-hosted
lib/std/target.zig
@@ -404,6 +404,7 @@ pub const Target = struct {
};
pub const ObjectFormat = enum {
+ /// TODO Get rid of this one.
unknown,
coff,
elf,
src-self-hosted/codegen.zig
@@ -39,7 +39,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.
defer function.inst_table.deinit();
defer function.errors.deinit();
- for (module_fn.body) |inst| {
+ for (module_fn.body.instructions) |inst| {
const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
error.CodegenFail => {
assert(function.errors.items.len != 0);
@@ -77,32 +77,62 @@ const Function = struct {
fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
switch (inst.tag) {
- .unreach => return self.genPanic(inst.src),
+ .unreach => return MCValue{ .unreach = {} },
.constant => unreachable, // excluded from function bodies
.assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
.ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
.bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
+ .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
+ .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
+ .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
+ .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
+ .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
}
}
- fn genPanic(self: *Function, src: usize) !MCValue {
- // TODO change this to call the panic function
+ fn genBreakpoint(self: *Function, src: usize) !MCValue {
switch (self.module.target.cpu.arch) {
.i386, .x86_64 => {
try self.code.append(0xcc); // int3
},
- else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}),
+ else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.module.target.cpu.arch}),
}
return .unreach;
}
- fn genRet(self: *Function, src: usize) !void {
- // TODO change this to call the panic function
+ fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
switch (self.module.target.cpu.arch) {
.i386, .x86_64 => {
try self.code.append(0xc3); // ret
},
- else => return self.fail(src, "TODO implement ret for {}", .{self.module.target.cpu.arch}),
+ else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.module.target.cpu.arch}),
+ }
+ return .unreach;
+ }
+
+ fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
+ switch (self.module.target.cpu.arch) {
+ else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.module.target.cpu.arch}),
+ }
+ }
+
+ fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue {
+ switch (self.module.target.cpu.arch) {
+ else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.module.target.cpu.arch}),
+ }
+ }
+
+ fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue {
+ switch (self.module.target.cpu.arch) {
+ else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.module.target.cpu.arch}),
+ }
+ }
+
+ fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue {
+ // Here you can specialize this instruction if it makes sense to, otherwise the default
+ // will call genIsNull and invert the result.
+ switch (self.module.target.cpu.arch) {
+ else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
}
}
src-self-hosted/ir.zig
@@ -156,6 +156,9 @@ pub const Module = struct {
arena: std.heap.ArenaAllocator,
fns: []Fn,
target: Target,
+ link_mode: std.builtin.LinkMode,
+ output_mode: std.builtin.OutputMode,
+ object_format: std.Target.ObjectFormat,
pub const Export = struct {
name: []const u8,
@@ -190,7 +193,14 @@ pub const ErrorMsg = struct {
msg: []const u8,
};
-pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !Module {
+pub const AnalyzeOptions = struct {
+ target: Target,
+ output_mode: std.builtin.OutputMode,
+ link_mode: std.builtin.LinkMode,
+ object_format: ?std.Target.ObjectFormat = null,
+};
+
+pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeOptions) !Module {
var ctx = Analyze{
.allocator = allocator,
.arena = std.heap.ArenaAllocator.init(allocator),
@@ -199,7 +209,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !
.decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
.exports = std.ArrayList(Module.Export).init(allocator),
.fns = std.ArrayList(Module.Fn).init(allocator),
- .target = target,
+ .target = options.target,
};
defer ctx.errors.deinit();
defer ctx.decl_table.deinit();
@@ -218,7 +228,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !
.errors = ctx.errors.toOwnedSlice(),
.fns = ctx.fns.toOwnedSlice(),
.arena = ctx.arena,
- .target = target,
+ .target = ctx.target,
+ .link_mode = options.link_mode,
+ .output_mode = options.output_mode,
+ .object_format = options.object_format orelse ctx.target.getObjectFormat(),
};
}
@@ -1241,7 +1254,11 @@ pub fn main() anyerror!void {
const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
- var analyzed_module = try analyze(allocator, zir_module, native_info.target);
+ var analyzed_module = try analyze(allocator, zir_module, .{
+ .target = native_info.target,
+ .output_mode = .Obj,
+ .link_mode = .Static,
+ });
defer analyzed_module.deinit(allocator);
if (analyzed_module.errors.len != 0) {
@@ -1263,31 +1280,17 @@ pub fn main() anyerror!void {
try bos.flush();
}
- // executable
- //const link = @import("link.zig");
- //var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out");
- //defer result.deinit(allocator);
- //if (result.errors.len != 0) {
- // for (result.errors) |err_msg| {
- // const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
- // std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
- // }
- // if (debug_error_trace) return error.LinkFailure;
- // std.process.exit(1);
- //}
-
- // object file
const link = @import("link.zig");
- //var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out");
- //defer result.deinit(allocator);
- //if (result.errors.len != 0) {
- // for (result.errors) |err_msg| {
- // const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
- // std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
- // }
- // if (debug_error_trace) return error.LinkFailure;
- // std.process.exit(1);
- //}
+ var result = try link.updateFilePath(allocator, analyzed_module, std.fs.cwd(), "zir.o");
+ defer result.deinit(allocator);
+ if (result.errors.len != 0) {
+ for (result.errors) |err_msg| {
+ const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
+ std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
+ }
+ if (debug_error_trace) return error.LinkFailure;
+ std.process.exit(1);
+ }
}
// Performance optimization ideas:
src-self-hosted/link.zig
@@ -7,11 +7,6 @@ const fs = std.fs;
const elf = std.elf;
const codegen = @import("codegen.zig");
-/// On common systems with a 0o022 umask, 0o777 will still result in a file created
-/// with 0o755 permissions, but it works appropriately if the system is configured
-/// more leniently. As another data point, C's fopen seems to open files with the
-/// 666 mode.
-const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
const default_entry_addr = 0x8000000;
pub const ErrorMsg = struct {
@@ -35,29 +30,29 @@ pub const Result = struct {
/// If incremental linking fails, falls back to truncating the file and rewriting it.
/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
/// This operation is not atomic.
-pub fn updateExecutableFilePath(
+pub fn updateFilePath(
allocator: *Allocator,
module: ir.Module,
dir: fs.Dir,
sub_path: []const u8,
) !Result {
- const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode });
+ const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(module) });
defer file.close();
- return updateExecutableFile(allocator, module, file);
+ return updateFile(allocator, module, file);
}
/// Atomically overwrites the old file, if present.
-pub fn writeExecutableFilePath(
+pub fn writeFilePath(
allocator: *Allocator,
module: ir.Module,
dir: fs.Dir,
sub_path: []const u8,
) !Result {
- const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode });
+ const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) });
defer af.deinit();
- const result = try writeExecutableFile(allocator, module, af.file);
+ const result = try writeFile(allocator, module, af.file);
try af.finish();
return result;
}
@@ -67,10 +62,10 @@ pub fn writeExecutableFilePath(
/// Returns an error if `file` is not already open with +read +write +seek abilities.
/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
/// This operation is not atomic.
-pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
- return updateExecutableFileInner(allocator, module, file) catch |err| switch (err) {
+pub fn updateFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
+ return updateFileInner(allocator, module, file) catch |err| switch (err) {
error.IncrFailed => {
- return writeExecutableFile(allocator, module, file);
+ return writeFile(allocator, module, file);
},
else => |e| return e,
};
@@ -750,7 +745,20 @@ const Update = struct {
/// Truncates the existing file contents and overwrites the contents.
/// Returns an error if `file` is not already open with +read +write +seek abilities.
-pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
+pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
+ switch (module.output_mode) {
+ .Exe => {},
+ .Obj => return error.TODOImplementWritingObjectFiles,
+ .Lib => return error.TODOImplementWritingLibFiles,
+ }
+ switch (module.object_format) {
+ .unknown => unreachable, // TODO remove this tag from the enum
+ .coff => return error.TODOImplementWritingCOFF,
+ .elf => {},
+ .macho => return error.TODOImplementWritingMachO,
+ .wasm => return error.TODOImplementWritingWasmObjects,
+ }
+
var update = Update{
.file = file,
.module = &module,
@@ -778,7 +786,7 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi
}
/// Returns error.IncrFailed if incremental update could not be performed.
-fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
+fn updateFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
//var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
// TODO implement incremental linking
@@ -822,3 +830,19 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
.sh_entsize = @intCast(u32, shdr.sh_entsize),
};
}
+
+fn determineMode(module: ir.Module) fs.File.Mode {
+ // On common systems with a 0o022 umask, 0o777 will still result in a file created
+ // with 0o755 permissions, but it works appropriately if the system is configured
+ // more leniently. As another data point, C's fopen seems to open files with the
+ // 666 mode.
+ const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777;
+ switch (module.output_mode) {
+ .Lib => return switch (module.link_mode) {
+ .Dynamic => executable_mode,
+ .Static => fs.File.default_mode,
+ },
+ .Exe => return executable_mode,
+ .Obj => return fs.File.default_mode,
+ }
+}