authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-24 15:37:21-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-04-24 15:37:21-04:00
log7634e67ba503fdbdf75daff48a13f9d35e331cd4
tree17266638acfc3403956633c8c28a7a4ab871275c
parentc829f2f7b7c5bdd13d3c39ae2960ed108393a210
parent9ebf25d1458988b6aa75d4608062f18f802c6a38
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5158 from ziglang/zir-to-elf

beginnings of (non-LLVM) self-hosted machine code generation and linking

8 files changed, 1388 insertions(+), 930 deletions(-)

lib/std/fs.zig+4-2
...@@ -1345,8 +1345,10 @@ pub const Dir = struct {...@@ -1345,8 +1345,10 @@ pub const Dir = struct {
1345 mode: File.Mode = File.default_mode,1345 mode: File.Mode = File.default_mode,
1346 };1346 };
13471347
1348 /// `dest_path` must remain valid for the lifetime of `AtomicFile`.1348 /// Directly access the `.file` field, and then call `AtomicFile.finish`
1349 /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.1349 /// to atomically replace `dest_path` with contents.
1350 /// Always call `AtomicFile.deinit` to clean up, regardless of whether `AtomicFile.finish` succeeded.
1351 /// `dest_path` must remain valid until `AtomicFile.deinit` is called.
1350 pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {1352 pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {
1351 if (path.dirname(dest_path)) |dirname| {1353 if (path.dirname(dest_path)) |dirname| {
1352 const dir = try self.openDir(dirname, .{});1354 const dir = try self.openDir(dirname, .{});
lib/std/fs/file.zig+1-1
...@@ -93,7 +93,7 @@ pub const File = struct {...@@ -93,7 +93,7 @@ pub const File = struct {
93 /// This means that a process that does not respect the locking API can still get access93 /// This means that a process that does not respect the locking API can still get access
94 /// to the file, despite the lock.94 /// to the file, despite the lock.
95 ///95 ///
96 /// Windows' file locks are mandatory, and any process attempting to access the file will96 /// Windows's file locks are mandatory, and any process attempting to access the file will
97 /// receive an error.97 /// receive an error.
98 ///98 ///
99 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt99 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
lib/std/mem.zig+15-3
...@@ -2027,7 +2027,13 @@ test "sliceAsBytes and bytesAsSlice back" {...@@ -2027,7 +2027,13 @@ test "sliceAsBytes and bytesAsSlice back" {
2027/// Round an address up to the nearest aligned address2027/// Round an address up to the nearest aligned address
2028/// The alignment must be a power of 2 and greater than 0.2028/// The alignment must be a power of 2 and greater than 0.
2029pub fn alignForward(addr: usize, alignment: usize) usize {2029pub fn alignForward(addr: usize, alignment: usize) usize {
2030 return alignBackward(addr + (alignment - 1), alignment);2030 return alignForwardGeneric(usize, addr, alignment);
2031}
2032
2033/// Round an address up to the nearest aligned address
2034/// The alignment must be a power of 2 and greater than 0.
2035pub fn alignForwardGeneric(comptime T: type, addr: T, alignment: T) T {
2036 return alignBackwardGeneric(T, addr + (alignment - 1), alignment);
2031}2037}
20322038
2033test "alignForward" {2039test "alignForward" {
...@@ -2048,8 +2054,14 @@ test "alignForward" {...@@ -2048,8 +2054,14 @@ test "alignForward" {
2048/// Round an address up to the previous aligned address2054/// Round an address up to the previous aligned address
2049/// The alignment must be a power of 2 and greater than 0.2055/// The alignment must be a power of 2 and greater than 0.
2050pub fn alignBackward(addr: usize, alignment: usize) usize {2056pub fn alignBackward(addr: usize, alignment: usize) usize {
2051 assert(@popCount(usize, alignment) == 1);2057 return alignBackwardGeneric(usize, addr, alignment);
2052 // 000010000 // example addr2058}
2059
2060/// Round an address up to the previous aligned address
2061/// The alignment must be a power of 2 and greater than 0.
2062pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {
2063 assert(@popCount(T, alignment) == 1);
2064 // 000010000 // example alignment
2053 // 000001111 // subtract 12065 // 000001111 // subtract 1
2054 // 111110000 // binary not2066 // 111110000 // binary not
2055 return addr & ~(alignment - 1);2067 return addr & ~(alignment - 1);
src-self-hosted/codegen.zig+482-405
...@@ -1,447 +1,524 @@...@@ -1,447 +1,524 @@
1const std = @import("std");1const std = @import("std");
2const Compilation = @import("compilation.zig").Compilation;2const mem = std.mem;
3const llvm = @import("llvm.zig");3const assert = std.debug.assert;
4const c = @import("c.zig");
5const ir = @import("ir.zig");4const ir = @import("ir.zig");
6const Value = @import("value.zig").Value;
7const Type = @import("type.zig").Type;5const Type = @import("type.zig").Type;
8const Scope = @import("scope.zig").Scope;6const Value = @import("value.zig").Value;
9const util = @import("util.zig");7const Target = std.Target;
10const event = std.event;
11const assert = std.debug.assert;
12const DW = std.dwarf;
13const maxInt = std.math.maxInt;
14
15pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) Compilation.BuildError!void {
16 fn_val.base.ref();
17 defer fn_val.base.deref(comp);
18 defer code.destroy(comp.gpa());
19
20 var output_path = try comp.createRandomOutputPath(comp.target.oFileExt());
21 errdefer output_path.deinit();
22
23 const llvm_handle = try comp.zig_compiler.getAnyLlvmContext();
24 defer llvm_handle.release(comp.zig_compiler);
258
26 const context = llvm_handle.node.data;9pub const ErrorMsg = struct {
10 byte_offset: usize,
11 msg: []const u8,
12};
2713
28 const module = llvm.ModuleCreateWithNameInContext(comp.name.span(), context) orelse return error.OutOfMemory;14pub const Symbol = struct {
29 defer llvm.DisposeModule(module);15 errors: []ErrorMsg,
3016
31 llvm.SetTarget(module, comp.llvm_triple.span());17 pub fn deinit(self: *Symbol, allocator: *mem.Allocator) void {
32 llvm.SetDataLayout(module, comp.target_layout_str);18 for (self.errors) |err| {
19 allocator.free(err.msg);
20 }
21 allocator.free(self.errors);
22 self.* = undefined;
23 }
24};
3325
34 if (comp.target.getObjectFormat() == .coff) {26pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !Symbol {
35 llvm.AddModuleCodeViewFlag(module);27 switch (typed_value.ty.zigTypeTag()) {
36 } else {28 .Fn => {
37 llvm.AddModuleDebugInfoFlag(module);29 const index = typed_value.val.cast(Value.Payload.Function).?.index;
30 const module_fn = module.fns[index];
31
32 var function = Function{
33 .module = &module,
34 .mod_fn = &module_fn,
35 .code = code,
36 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator),
37 .errors = std.ArrayList(ErrorMsg).init(code.allocator),
38 };
39 defer function.inst_table.deinit();
40 defer function.errors.deinit();
41
42 for (module_fn.body) |inst| {
43 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
44 error.CodegenFail => {
45 assert(function.errors.items.len != 0);
46 break;
47 },
48 else => |e| return e,
49 };
50 try function.inst_table.putNoClobber(inst, new_inst);
51 }
52
53 return Symbol{ .errors = function.errors.toOwnedSlice() };
54 },
55 else => @panic("TODO implement generateSymbol for non-function types"),
38 }56 }
57}
3958
40 const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;59const Function = struct {
41 defer llvm.DisposeBuilder(builder);60 module: *const ir.Module,
4261 mod_fn: *const ir.Module.Fn,
43 const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory;62 code: *std.ArrayList(u8),
44 defer llvm.DisposeDIBuilder(dibuilder);63 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
4564 errors: std.ArrayList(ErrorMsg),
46 // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes65
47 // the git revision.66 const MCValue = union(enum) {
48 const producer = try std.fmt.allocPrintZ(&code.arena.allocator, "zig {}.{}.{}", .{67 none,
49 @as(u32, c.ZIG_VERSION_MAJOR),68 unreach,
50 @as(u32, c.ZIG_VERSION_MINOR),69 /// A pointer-sized integer that fits in a register.
51 @as(u32, c.ZIG_VERSION_PATCH),70 immediate: u64,
52 });71 /// The constant was emitted into the code, at this offset.
53 const flags = "";72 embedded_in_code: usize,
54 const runtime_version = 0;73 /// The value is in a target-specific register. The value can
55 const compile_unit_file = llvm.CreateFile(74 /// be @intToEnum casted to the respective Reg enum.
56 dibuilder,75 register: usize,
57 comp.name.span(),
58 comp.root_package.root_src_dir.span(),
59 ) orelse return error.OutOfMemory;
60 const is_optimized = comp.build_mode != .Debug;
61 const compile_unit = llvm.CreateCompileUnit(
62 dibuilder,
63 DW.LANG_C99,
64 compile_unit_file,
65 producer,
66 is_optimized,
67 flags,
68 runtime_version,
69 "",
70 0,
71 !comp.strip,
72 ) orelse return error.OutOfMemory;
73
74 var ofile = ObjectFile{
75 .comp = comp,
76 .module = module,
77 .builder = builder,
78 .dibuilder = dibuilder,
79 .context = context,
80 .lock = event.Lock.init(),
81 .arena = &code.arena.allocator,
82 };76 };
8377
84 try renderToLlvmModule(&ofile, fn_val, code);78 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
8579 switch (inst.tag) {
86 // TODO module level assembly80 .unreach => return self.genPanic(inst.src),
87 //if (buf_len(&g->global_asm) != 0) {81 .constant => unreachable, // excluded from function bodies
88 // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));82 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
89 //}83 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
9084 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
91 llvm.DIBuilderFinalize(dibuilder);85 }
92
93 if (comp.verbose_llvm_ir) {
94 std.debug.warn("raw module:\n", .{});
95 llvm.DumpModule(ofile.module);
96 }86 }
9787
98 // verify the llvm module when safety is on88 fn genPanic(self: *Function, src: usize) !MCValue {
99 if (std.debug.runtime_safety) {89 // TODO change this to call the panic function
100 var error_ptr: ?[*:0]u8 = null;90 switch (self.module.target.cpu.arch) {
101 _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);91 .i386, .x86_64 => {
92 try self.code.append(0xcc); // int3
93 },
94 else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}),
95 }
96 return .unreach;
102 }97 }
10398
104 const is_small = comp.build_mode == .ReleaseSmall;99 fn genRet(self: *Function, src: usize) !void {
105 const is_debug = comp.build_mode == .Debug;100 // TODO change this to call the panic function
106101 switch (self.module.target.cpu.arch) {
107 var err_msg: [*:0]u8 = undefined;102 .i386, .x86_64 => {
108 // TODO integrate this with evented I/O103 try self.code.append(0xc3); // ret
109 if (llvm.TargetMachineEmitToFile(104 },
110 comp.target_machine,105 else => return self.fail(src, "TODO implement ret for {}", .{self.module.target.cpu.arch}),
111 module,
112 output_path.span(),
113 llvm.EmitBinary,
114 &err_msg,
115 is_debug,
116 is_small,
117 )) {
118 if (std.debug.runtime_safety) {
119 std.debug.panic("unable to write object file {}: {s}\n", .{ output_path.span(), err_msg });
120 }106 }
121 return error.WritingObjectFileFailed;
122 }
123 //validate_inline_fns(g); TODO
124 fn_val.containing_object = output_path;
125 if (comp.verbose_llvm_ir) {
126 std.debug.warn("optimized module:\n", .{});
127 llvm.DumpModule(ofile.module);
128 }
129 if (comp.verbose_link) {
130 std.debug.warn("created {}\n", .{output_path.span()});
131 }107 }
132}
133108
134pub const ObjectFile = struct {109 fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void {
135 comp: *Compilation,110 switch (self.module.target.cpu.arch) {
136 module: *llvm.Module,111 .i386, .x86_64 => {
137 builder: *llvm.Builder,112 if (amount <= std.math.maxInt(u8)) {
138 dibuilder: *llvm.DIBuilder,113 try self.code.resize(self.code.items.len + 2);
139 context: *llvm.Context,114 self.code.items[self.code.items.len - 2] = 0xeb;
140 lock: event.Lock,115 self.code.items[self.code.items.len - 1] = @intCast(u8, amount);
141 arena: *std.mem.Allocator,116 } else {
142117 try self.code.resize(self.code.items.len + 5);
143 fn gpa(self: *ObjectFile) *std.mem.Allocator {118 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
144 return self.comp.gpa();119 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
120 mem.writeIntLittle(u32, imm_ptr, amount);
121 }
122 },
123 else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.module.target.cpu.arch}),
124 }
145 }125 }
146};
147126
148pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {127 fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue {
149 // TODO audit more of codegen.cpp:fn_llvm_value and port more logic128 // TODO convert to inline function
150 const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);129 switch (self.module.target.cpu.arch) {
151 const llvm_fn = llvm.AddFunction(130 .arm => return self.genAsmArch(.arm, inst),
152 ofile.module,131 .armeb => return self.genAsmArch(.armeb, inst),
153 fn_val.symbol_name.span(),132 .aarch64 => return self.genAsmArch(.aarch64, inst),
154 llvm_fn_type,133 .aarch64_be => return self.genAsmArch(.aarch64_be, inst),
155 ) orelse return error.OutOfMemory;134 .aarch64_32 => return self.genAsmArch(.aarch64_32, inst),
156135 .arc => return self.genAsmArch(.arc, inst),
157 const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp);136 .avr => return self.genAsmArch(.avr, inst),
158 if (want_fn_safety and ofile.comp.haveLibC()) {137 .bpfel => return self.genAsmArch(.bpfel, inst),
159 try addLLVMFnAttr(ofile, llvm_fn, "sspstrong");138 .bpfeb => return self.genAsmArch(.bpfeb, inst),
160 try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4");139 .hexagon => return self.genAsmArch(.hexagon, inst),
140 .mips => return self.genAsmArch(.mips, inst),
141 .mipsel => return self.genAsmArch(.mipsel, inst),
142 .mips64 => return self.genAsmArch(.mips64, inst),
143 .mips64el => return self.genAsmArch(.mips64el, inst),
144 .msp430 => return self.genAsmArch(.msp430, inst),
145 .powerpc => return self.genAsmArch(.powerpc, inst),
146 .powerpc64 => return self.genAsmArch(.powerpc64, inst),
147 .powerpc64le => return self.genAsmArch(.powerpc64le, inst),
148 .r600 => return self.genAsmArch(.r600, inst),
149 .amdgcn => return self.genAsmArch(.amdgcn, inst),
150 .riscv32 => return self.genAsmArch(.riscv32, inst),
151 .riscv64 => return self.genAsmArch(.riscv64, inst),
152 .sparc => return self.genAsmArch(.sparc, inst),
153 .sparcv9 => return self.genAsmArch(.sparcv9, inst),
154 .sparcel => return self.genAsmArch(.sparcel, inst),
155 .s390x => return self.genAsmArch(.s390x, inst),
156 .tce => return self.genAsmArch(.tce, inst),
157 .tcele => return self.genAsmArch(.tcele, inst),
158 .thumb => return self.genAsmArch(.thumb, inst),
159 .thumbeb => return self.genAsmArch(.thumbeb, inst),
160 .i386 => return self.genAsmArch(.i386, inst),
161 .x86_64 => return self.genAsmArch(.x86_64, inst),
162 .xcore => return self.genAsmArch(.xcore, inst),
163 .nvptx => return self.genAsmArch(.nvptx, inst),
164 .nvptx64 => return self.genAsmArch(.nvptx64, inst),
165 .le32 => return self.genAsmArch(.le32, inst),
166 .le64 => return self.genAsmArch(.le64, inst),
167 .amdil => return self.genAsmArch(.amdil, inst),
168 .amdil64 => return self.genAsmArch(.amdil64, inst),
169 .hsail => return self.genAsmArch(.hsail, inst),
170 .hsail64 => return self.genAsmArch(.hsail64, inst),
171 .spir => return self.genAsmArch(.spir, inst),
172 .spir64 => return self.genAsmArch(.spir64, inst),
173 .kalimba => return self.genAsmArch(.kalimba, inst),
174 .shave => return self.genAsmArch(.shave, inst),
175 .lanai => return self.genAsmArch(.lanai, inst),
176 .wasm32 => return self.genAsmArch(.wasm32, inst),
177 .wasm64 => return self.genAsmArch(.wasm64, inst),
178 .renderscript32 => return self.genAsmArch(.renderscript32, inst),
179 .renderscript64 => return self.genAsmArch(.renderscript64, inst),
180 .ve => return self.genAsmArch(.ve, inst),
181 }
161 }182 }
162183
163 // TODO184 fn genAsmArch(self: *Function, comptime arch: Target.Cpu.Arch, inst: *ir.Inst.Assembly) !MCValue {
164 //if (fn_val.align_stack) |align_stack| {185 if (arch != .x86_64 and arch != .i386) {
165 // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);186 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
166 //}187 }
167188 for (inst.args.inputs) |input, i| {
168 const fn_type = fn_val.base.typ.cast(Type.Fn).?;189 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
169 const fn_type_normal = &fn_type.key.data.Normal;190 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
170191 }
171 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");192 const reg_name = input[1 .. input.len - 1];
172 //add_uwtable_attr(g, fn_table_entry->llvm_value);193 const reg = parseRegName(arch, reg_name) orelse
173 try addLLVMFnAttr(ofile, llvm_fn, "nobuiltin");194 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
174195 const arg = try self.resolveInst(inst.args.args[i]);
175 //if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) {196 try self.genSetReg(inst.base.src, arch, reg, arg);
176 // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true");
177 // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim-non-leaf", nullptr);
178 //}
179
180 //if (fn_table_entry->section_name) {
181 // LLVMSetSection(fn_table_entry->llvm_value, buf_ptr(fn_table_entry->section_name));
182 //}
183 //if (fn_table_entry->align_bytes > 0) {
184 // LLVMSetAlignment(fn_table_entry->llvm_value, (unsigned)fn_table_entry->align_bytes);
185 //} else {
186 // // We'd like to set the best alignment for the function here, but on Darwin LLVM gives
187 // // "Cannot getTypeInfo() on a type that is unsized!" assertion failure when calling
188 // // any of the functions for getting alignment. Not specifying the alignment should
189 // // use the ABI alignment, which is fine.
190 //}
191
192 //if (!type_has_bits(return_type)) {
193 // // nothing to do
194 //} else if (type_is_codegen_pointer(return_type)) {
195 // addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
196 //} else if (handle_is_ptr(return_type) &&
197 // calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc))
198 //{
199 // addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
200 // addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
201 //}
202
203 // TODO set parameter attributes
204
205 // TODO
206 //uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
207 //if (err_ret_trace_arg_index != UINT32_MAX) {
208 // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
209 //}
210
211 const cur_ret_ptr = if (fn_type_normal.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null;
212
213 // build all basic blocks
214 for (code.basic_block_list.span()) |bb| {
215 bb.llvm_block = llvm.AppendBasicBlockInContext(
216 ofile.context,
217 llvm_fn,
218 bb.name_hint,
219 ) orelse return error.OutOfMemory;
220 }
221 const entry_bb = code.basic_block_list.at(0);
222 llvm.PositionBuilderAtEnd(ofile.builder, entry_bb.llvm_block);
223
224 llvm.ClearCurrentDebugLocation(ofile.builder);
225
226 // TODO set up error return tracing
227 // TODO allocate temporary stack values
228
229 const var_list = fn_type.non_key.Normal.variable_list.span();
230 // create debug variable declarations for variables and allocate all local variables
231 for (var_list) |var_scope, i| {
232 const var_type = switch (var_scope.data) {
233 .Const => unreachable,
234 .Param => |param| param.typ,
235 };
236 // if (!type_has_bits(var->value->type)) {
237 // continue;
238 // }
239 // if (ir_get_var_is_comptime(var))
240 // continue;
241 // if (type_requires_comptime(var->value->type))
242 // continue;
243 // if (var->src_arg_index == SIZE_MAX) {
244 // var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
245
246 // var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
247 // buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
248 // var->value->type->di_type, !g->strip_debug_symbols, 0);
249
250 // } else {
251 // it's a parameter
252 // assert(var->gen_arg_index != SIZE_MAX);
253 // TypeTableEntry *gen_type;
254 // FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index];
255
256 if (var_type.handleIsPtr()) {
257 // if (gen_info->is_byval) {
258 // gen_type = var->value->type;
259 // } else {
260 // gen_type = gen_info->type;
261 // }
262 var_scope.data.Param.llvm_value = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
263 } else {
264 // gen_type = var->value->type;
265 var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, .Abi);
266 }197 }
267 // if (var->decl_node) {
268 // var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
269 // buf_ptr(&var->name), import->di_file,
270 // (unsigned)(var->decl_node->line + 1),
271 // gen_type->di_type, !g->strip_debug_symbols, 0, (unsigned)(var->gen_arg_index + 1));
272 // }
273
274 // }
275 }
276198
277 // TODO finishing error return trace setup. we have to do this after all the allocas.199 if (mem.eql(u8, inst.args.asm_source, "syscall")) {
278200 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
279 // create debug variable declarations for parameters201 } else {
280 // rely on the first variables in the variable_list being parameters.202 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
281 //size_t next_var_i = 0;
282 for (fn_type.key.data.Normal.params) |param, i| {
283 //FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
284 //if (info->gen_index == SIZE_MAX)
285 // continue;
286 const scope_var = var_list[i];
287 //assert(variable->src_arg_index != SIZE_MAX);
288 //next_var_i += 1;
289 //assert(variable);
290 //assert(variable->value_ref);
291
292 if (!param.typ.handleIsPtr()) {
293 //clear_debug_source_node(g);
294 const llvm_param = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
295 _ = try renderStoreUntyped(
296 ofile,
297 llvm_param,
298 scope_var.data.Param.llvm_value,
299 .Abi,
300 .Non,
301 );
302 }203 }
303204
304 //if (variable->decl_node) {205 if (inst.args.output) |output| {
305 // gen_var_debug_decl(g, variable);206 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
306 //}207 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
208 }
209 const reg_name = output[2 .. output.len - 1];
210 const reg = parseRegName(arch, reg_name) orelse
211 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
212 return MCValue{ .register = @enumToInt(reg) };
213 } else {
214 return MCValue.none;
215 }
307 }216 }
308217
309 for (code.basic_block_list.span()) |current_block| {218 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) !void {
310 llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block);219 switch (arch) {
311 for (current_block.instruction_list.span()) |instruction| {220 .x86_64 => switch (reg) {
312 if (instruction.ref_count == 0 and !instruction.hasSideEffects()) continue;221 .rax => switch (mcv) {
313222 .none, .unreach => unreachable,
314 instruction.llvm_value = try instruction.render(ofile, fn_val);223 .immediate => |x| {
224 // Setting the eax register zeroes the upper part of rax, so if the number is small
225 // enough, that is preferable.
226 // Best case: zero
227 // 31 c0 xor eax,eax
228 if (x == 0) {
229 return self.code.appendSlice(&[_]u8{ 0x31, 0xc0 });
230 }
231 // Next best case: set eax with 4 bytes
232 // b8 04 03 02 01 mov eax,0x01020304
233 if (x <= std.math.maxInt(u32)) {
234 try self.code.resize(self.code.items.len + 5);
235 self.code.items[self.code.items.len - 5] = 0xb8;
236 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
237 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
238 return;
239 }
240 // Worst case: set rax with 8 bytes
241 // 48 b8 08 07 06 05 04 03 02 01 movabs rax,0x0102030405060708
242 try self.code.resize(self.code.items.len + 10);
243 self.code.items[self.code.items.len - 10] = 0x48;
244 self.code.items[self.code.items.len - 9] = 0xb8;
245 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
246 mem.writeIntLittle(u64, imm_ptr, x);
247 return;
248 },
249 .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rax = embedded_in_code", .{}),
250 .register => return self.fail(src, "TODO implement x86_64 genSetReg %rax = register", .{}),
251 },
252 .rdx => switch (mcv) {
253 .none, .unreach => unreachable,
254 .immediate => |x| {
255 // Setting the edx register zeroes the upper part of rdx, so if the number is small
256 // enough, that is preferable.
257 // Best case: zero
258 // 31 d2 xor edx,edx
259 if (x == 0) {
260 return self.code.appendSlice(&[_]u8{ 0x31, 0xd2 });
261 }
262 // Next best case: set edx with 4 bytes
263 // ba 04 03 02 01 mov edx,0x1020304
264 if (x <= std.math.maxInt(u32)) {
265 try self.code.resize(self.code.items.len + 5);
266 self.code.items[self.code.items.len - 5] = 0xba;
267 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
268 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
269 return;
270 }
271 // Worst case: set rdx with 8 bytes
272 // 48 ba 08 07 06 05 04 03 02 01 movabs rdx,0x0102030405060708
273 try self.code.resize(self.code.items.len + 10);
274 self.code.items[self.code.items.len - 10] = 0x48;
275 self.code.items[self.code.items.len - 9] = 0xba;
276 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
277 mem.writeIntLittle(u64, imm_ptr, x);
278 return;
279 },
280 .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = embedded_in_code", .{}),
281 .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = register", .{}),
282 },
283 .rdi => switch (mcv) {
284 .none, .unreach => unreachable,
285 .immediate => |x| {
286 // Setting the edi register zeroes the upper part of rdi, so if the number is small
287 // enough, that is preferable.
288 // Best case: zero
289 // 31 ff xor edi,edi
290 if (x == 0) {
291 return self.code.appendSlice(&[_]u8{ 0x31, 0xff });
292 }
293 // Next best case: set edi with 4 bytes
294 // bf 04 03 02 01 mov edi,0x1020304
295 if (x <= std.math.maxInt(u32)) {
296 try self.code.resize(self.code.items.len + 5);
297 self.code.items[self.code.items.len - 5] = 0xbf;
298 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
299 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
300 return;
301 }
302 // Worst case: set rdi with 8 bytes
303 // 48 bf 08 07 06 05 04 03 02 01 movabs rax,0x0102030405060708
304 try self.code.resize(self.code.items.len + 10);
305 self.code.items[self.code.items.len - 10] = 0x48;
306 self.code.items[self.code.items.len - 9] = 0xbf;
307 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
308 mem.writeIntLittle(u64, imm_ptr, x);
309 return;
310 },
311 .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = embedded_in_code", .{}),
312 .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = register", .{}),
313 },
314 .rsi => switch (mcv) {
315 .none, .unreach => unreachable,
316 .immediate => return self.fail(src, "TODO implement x86_64 genSetReg %rsi = immediate", .{}),
317 .embedded_in_code => |code_offset| {
318 // Examples:
319 // lea rsi, [rip + 0x01020304]
320 // lea rsi, [rip - 7]
321 // f: 48 8d 35 04 03 02 01 lea rsi,[rip+0x1020304] # 102031a <_start+0x102031a>
322 // 16: 48 8d 35 f9 ff ff ff lea rsi,[rip+0xfffffffffffffff9] # 16 <_start+0x16>
323 //
324 // We need the offset from RIP in a signed i32 twos complement.
325 // The instruction is 7 bytes long and RIP points to the next instruction.
326 try self.code.resize(self.code.items.len + 7);
327 const rip = self.code.items.len;
328 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
329 const offset = @intCast(i32, big_offset);
330 self.code.items[self.code.items.len - 7] = 0x48;
331 self.code.items[self.code.items.len - 6] = 0x8d;
332 self.code.items[self.code.items.len - 5] = 0x35;
333 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
334 mem.writeIntLittle(i32, imm_ptr, offset);
335 return;
336 },
337 .register => return self.fail(src, "TODO implement x86_64 genSetReg %rsi = register", .{}),
338 },
339 else => return self.fail(src, "TODO implement genSetReg for x86_64 '{}'", .{@tagName(reg)}),
340 },
341 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
315 }342 }
316 current_block.llvm_exit_block = llvm.GetInsertBlock(ofile.builder);
317 }343 }
318}
319
320fn addLLVMAttr(
321 ofile: *ObjectFile,
322 val: *llvm.Value,
323 attr_index: llvm.AttributeIndex,
324 attr_name: []const u8,
325) !void {
326 const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len);
327 assert(kind_id != 0);
328 const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, 0) orelse return error.OutOfMemory;
329 llvm.AddAttributeAtIndex(val, attr_index, llvm_attr);
330}
331
332fn addLLVMAttrStr(
333 ofile: *ObjectFile,
334 val: *llvm.Value,
335 attr_index: llvm.AttributeIndex,
336 attr_name: []const u8,
337 attr_val: []const u8,
338) !void {
339 const llvm_attr = llvm.CreateStringAttribute(
340 ofile.context,
341 attr_name.ptr,
342 @intCast(c_uint, attr_name.len),
343 attr_val.ptr,
344 @intCast(c_uint, attr_val.len),
345 ) orelse return error.OutOfMemory;
346 llvm.AddAttributeAtIndex(val, attr_index, llvm_attr);
347}
348
349fn addLLVMAttrInt(
350 val: *llvm.Value,
351 attr_index: llvm.AttributeIndex,
352 attr_name: []const u8,
353 attr_val: u64,
354) !void {
355 const kind_id = llvm.GetEnumAttributeKindForName(attr_name.ptr, attr_name.len);
356 assert(kind_id != 0);
357 const llvm_attr = llvm.CreateEnumAttribute(ofile.context, kind_id, attr_val) orelse return error.OutOfMemory;
358 llvm.AddAttributeAtIndex(val, attr_index, llvm_attr);
359}
360344
361fn addLLVMFnAttr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8) !void {345 fn genPtrToInt(self: *Function, inst: *ir.Inst.PtrToInt) !MCValue {
362 return addLLVMAttr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name);346 // no-op
363}347 return self.resolveInst(inst.args.ptr);
364
365fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: []const u8) !void {
366 return addLLVMAttrStr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val);
367}
368
369fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: u64) !void {
370 return addLLVMAttrInt(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val);
371}
372
373fn renderLoadUntyped(
374 ofile: *ObjectFile,
375 ptr: *llvm.Value,
376 alignment: Type.Pointer.Align,
377 vol: Type.Pointer.Vol,
378 name: [*:0]const u8,
379) !*llvm.Value {
380 const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
381 switch (vol) {
382 .Non => {},
383 .Volatile => llvm.SetVolatile(result, 1),
384 }348 }
385 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.GetElementType(llvm.TypeOf(ptr))));
386 return result;
387}
388349
389fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*:0]const u8) !*llvm.Value {350 fn genBitCast(self: *Function, inst: *ir.Inst.BitCast) !MCValue {
390 return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);351 const operand = try self.resolveInst(inst.args.operand);
391}352 return operand;
392
393pub fn getHandleValue(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer) !?*llvm.Value {
394 const child_type = ptr_type.key.child_type;
395 if (!child_type.hasBits()) {
396 return null;
397 }353 }
398 if (child_type.handleIsPtr()) {354
399 return ptr;355 fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue {
356 if (self.inst_table.getValue(inst)) |mcv| {
357 return mcv;
358 }
359 if (inst.cast(ir.Inst.Constant)) |const_inst| {
360 const mcvalue = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
361 try self.inst_table.putNoClobber(inst, mcvalue);
362 return mcvalue;
363 } else {
364 return self.inst_table.getValue(inst).?;
365 }
400 }366 }
401 return try renderLoad(ofile, ptr, ptr_type, "");
402}
403367
404pub fn renderStoreUntyped(368 fn genTypedValue(self: *Function, src: usize, typed_value: ir.TypedValue) !MCValue {
405 ofile: *ObjectFile,369 switch (typed_value.ty.zigTypeTag()) {
406 value: *llvm.Value,370 .Pointer => {
407 ptr: *llvm.Value,371 const ptr_elem_type = typed_value.ty.elemType();
408 alignment: Type.Pointer.Align,372 switch (ptr_elem_type.zigTypeTag()) {
409 vol: Type.Pointer.Vol,373 .Array => {
410) !*llvm.Value {374 // TODO more checks to make sure this can be emitted as a string literal
411 const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory;375 const bytes = try typed_value.val.toAllocatedBytes(self.code.allocator);
412 switch (vol) {376 defer self.code.allocator.free(bytes);
413 .Non => {},377 const smaller_len = std.math.cast(u32, bytes.len) catch
414 .Volatile => llvm.SetVolatile(result, 1),378 return self.fail(src, "TODO handle a larger string constant", .{});
379
380 // Emit the string literal directly into the code; jump over it.
381 try self.genRelativeFwdJump(src, smaller_len);
382 const offset = self.code.items.len;
383 try self.code.appendSlice(bytes);
384 return MCValue{ .embedded_in_code = offset };
385 },
386 else => |t| return self.fail(src, "TODO implement emitTypedValue for pointer to '{}'", .{@tagName(t)}),
387 }
388 },
389 .Int => {
390 const info = typed_value.ty.intInfo(self.module.target);
391 const ptr_bits = self.module.target.cpu.arch.ptrBitWidth();
392 if (info.bits > ptr_bits or info.signed) {
393 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
394 }
395 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
396 },
397 .ComptimeInt => unreachable, // semantic analysis prevents this
398 .ComptimeFloat => unreachable, // semantic analysis prevents this
399 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
400 }
415 }401 }
416 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value)));
417 return result;
418}
419402
420pub fn renderStore(403 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {
421 ofile: *ObjectFile,404 @setCold(true);
422 value: *llvm.Value,405 const msg = try std.fmt.allocPrint(self.errors.allocator, format, args);
423 ptr: *llvm.Value,406 {
424 ptr_type: *Type.Pointer,407 errdefer self.errors.allocator.free(msg);
425) !*llvm.Value {408 (try self.errors.addOne()).* = .{
426 return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol);409 .byte_offset = src,
427}410 .msg = msg,
411 };
412 }
413 return error.CodegenFail;
414 }
415};
428416
429pub fn renderAlloca(417fn Reg(comptime arch: Target.Cpu.Arch) type {
430 ofile: *ObjectFile,418 return switch (arch) {
431 var_type: *Type,419 .i386 => enum {
432 name: []const u8,420 eax,
433 alignment: Type.Pointer.Align,421 ebx,
434) !*llvm.Value {422 ecx,
435 const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);423 edx,
436 const name_with_null = try std.cstr.addNullByte(ofile.arena, name);424 ebp,
437 const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, @ptrCast([*:0]const u8, name_with_null.ptr)) orelse return error.OutOfMemory;425 esp,
438 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));426 esi,
439 return result;427 edi,
428
429 ax,
430 bx,
431 cx,
432 dx,
433 bp,
434 sp,
435 si,
436 di,
437
438 ah,
439 bh,
440 ch,
441 dh,
442
443 al,
444 bl,
445 cl,
446 dl,
447 },
448 .x86_64 => enum {
449 rax,
450 rbx,
451 rcx,
452 rdx,
453 rbp,
454 rsp,
455 rsi,
456 rdi,
457 r8,
458 r9,
459 r10,
460 r11,
461 r12,
462 r13,
463 r14,
464 r15,
465
466 eax,
467 ebx,
468 ecx,
469 edx,
470 ebp,
471 esp,
472 esi,
473 edi,
474 r8d,
475 r9d,
476 r10d,
477 r11d,
478 r12d,
479 r13d,
480 r14d,
481 r15d,
482
483 ax,
484 bx,
485 cx,
486 dx,
487 bp,
488 sp,
489 si,
490 di,
491 r8w,
492 r9w,
493 r10w,
494 r11w,
495 r12w,
496 r13w,
497 r14w,
498 r15w,
499
500 ah,
501 bh,
502 ch,
503 dh,
504
505 al,
506 bl,
507 cl,
508 dl,
509 r8b,
510 r9b,
511 r10b,
512 r11b,
513 r12b,
514 r13b,
515 r14b,
516 r15b,
517 },
518 else => @compileError("TODO add more register enums"),
519 };
440}520}
441521
442pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: *llvm.Type) u32 {522fn parseRegName(comptime arch: Target.Cpu.Arch, name: []const u8) ?Reg(arch) {
443 return switch (alignment) {523 return std.meta.stringToEnum(Reg(arch), name);
444 .Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type),
445 .Override => |a| a,
446 };
447}524}
src-self-hosted/ir.zig+61-19
...@@ -24,6 +24,7 @@ pub const Inst = struct {...@@ -24,6 +24,7 @@ pub const Inst = struct {
24 constant,24 constant,
25 assembly,25 assembly,
26 ptrtoint,26 ptrtoint,
27 bitcast,
27 };28 };
2829
29 pub fn cast(base: *Inst, comptime T: type) ?*T {30 pub fn cast(base: *Inst, comptime T: type) ?*T {
...@@ -45,6 +46,7 @@ pub const Inst = struct {...@@ -45,6 +46,7 @@ pub const Inst = struct {
4546
46 .assembly,47 .assembly,
47 .ptrtoint,48 .ptrtoint,
49 .bitcast,
48 => null,50 => null,
49 };51 };
50 }52 }
...@@ -84,6 +86,15 @@ pub const Inst = struct {...@@ -84,6 +86,15 @@ pub const Inst = struct {
84 ptr: *Inst,86 ptr: *Inst,
85 },87 },
86 };88 };
89
90 pub const BitCast = struct {
91 pub const base_tag = Tag.bitcast;
92
93 base: Inst,
94 args: struct {
95 operand: *Inst,
96 },
97 };
87};98};
8899
89pub const TypedValue = struct {100pub const TypedValue = struct {
...@@ -96,6 +107,7 @@ pub const Module = struct {...@@ -96,6 +107,7 @@ pub const Module = struct {
96 errors: []ErrorMsg,107 errors: []ErrorMsg,
97 arena: std.heap.ArenaAllocator,108 arena: std.heap.ArenaAllocator,
98 fns: []Fn,109 fns: []Fn,
110 target: Target,
99111
100 pub const Export = struct {112 pub const Export = struct {
101 name: []const u8,113 name: []const u8,
...@@ -122,9 +134,7 @@ pub const ErrorMsg = struct {...@@ -122,9 +134,7 @@ pub const ErrorMsg = struct {
122 msg: []const u8,134 msg: []const u8,
123};135};
124136
125pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {137pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !Module {
126 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
127
128 var ctx = Analyze{138 var ctx = Analyze{
129 .allocator = allocator,139 .allocator = allocator,
130 .arena = std.heap.ArenaAllocator.init(allocator),140 .arena = std.heap.ArenaAllocator.init(allocator),
...@@ -133,7 +143,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {...@@ -133,7 +143,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
133 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),143 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
134 .exports = std.ArrayList(Module.Export).init(allocator),144 .exports = std.ArrayList(Module.Export).init(allocator),
135 .fns = std.ArrayList(Module.Fn).init(allocator),145 .fns = std.ArrayList(Module.Fn).init(allocator),
136 .target = native_info.target,146 .target = target,
137 };147 };
138 defer ctx.errors.deinit();148 defer ctx.errors.deinit();
139 defer ctx.decl_table.deinit();149 defer ctx.decl_table.deinit();
...@@ -152,6 +162,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {...@@ -152,6 +162,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
152 .errors = ctx.errors.toOwnedSlice(),162 .errors = ctx.errors.toOwnedSlice(),
153 .fns = ctx.fns.toOwnedSlice(),163 .fns = ctx.fns.toOwnedSlice(),
154 .arena = ctx.arena,164 .arena = ctx.arena,
165 .target = target,
155 };166 };
156}167}
157168
...@@ -234,7 +245,7 @@ const Analyze = struct {...@@ -234,7 +245,7 @@ const Analyze = struct {
234 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {245 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {
235 const new_inst = try self.resolveInst(func, old_inst);246 const new_inst = try self.resolveInst(func, old_inst);
236 const wanted_type = Type.initTag(.const_slice_u8);247 const wanted_type = Type.initTag(.const_slice_u8);
237 const coerced_inst = try self.coerce(wanted_type, new_inst);248 const coerced_inst = try self.coerce(func, wanted_type, new_inst);
238 const val = try self.resolveConstValue(coerced_inst);249 const val = try self.resolveConstValue(coerced_inst);
239 return val.toAllocatedBytes(&self.arena.allocator);250 return val.toAllocatedBytes(&self.arena.allocator);
240 }251 }
...@@ -242,7 +253,7 @@ const Analyze = struct {...@@ -242,7 +253,7 @@ const Analyze = struct {
242 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {253 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {
243 const new_inst = try self.resolveInst(func, old_inst);254 const new_inst = try self.resolveInst(func, old_inst);
244 const wanted_type = Type.initTag(.@"type");255 const wanted_type = Type.initTag(.@"type");
245 const coerced_inst = try self.coerce(wanted_type, new_inst);256 const coerced_inst = try self.coerce(func, wanted_type, new_inst);
246 const val = try self.resolveConstValue(coerced_inst);257 const val = try self.resolveConstValue(coerced_inst);
247 return val.toType();258 return val.toType();
248 }259 }
...@@ -409,6 +420,7 @@ const Analyze = struct {...@@ -409,6 +420,7 @@ const Analyze = struct {
409 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),420 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
410 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),421 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
411 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),422 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
423 .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?),
412 }424 }
413 }425 }
414426
...@@ -472,7 +484,7 @@ const Analyze = struct {...@@ -472,7 +484,7 @@ const Analyze = struct {
472 fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst {484 fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst {
473 const dest_type = try self.resolveType(func, as.positionals.dest_type);485 const dest_type = try self.resolveType(func, as.positionals.dest_type);
474 const new_inst = try self.resolveInst(func, as.positionals.value);486 const new_inst = try self.resolveInst(func, as.positionals.value);
475 return self.coerce(dest_type, new_inst);487 return self.coerce(func, dest_type, new_inst);
476 }488 }
477489
478 fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {490 fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
...@@ -545,12 +557,18 @@ const Analyze = struct {...@@ -545,12 +557,18 @@ const Analyze = struct {
545 }557 }
546558
547 if (dest_is_comptime_int or new_inst.value() != null) {559 if (dest_is_comptime_int or new_inst.value() != null) {
548 return self.coerce(dest_type, new_inst);560 return self.coerce(func, dest_type, new_inst);
549 }561 }
550562
551 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});563 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
552 }564 }
553565
566 fn analyzeInstBitCast(self: *Analyze, func: ?*Fn, inst: *text.Inst.BitCast) InnerError!*Inst {
567 const dest_type = try self.resolveType(func, inst.positionals.dest_type);
568 const operand = try self.resolveInst(func, inst.positionals.operand);
569 return self.bitcast(func, dest_type, operand);
570 }
571
554 fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {572 fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {
555 const ptr = try self.resolveInst(func, deref.positionals.ptr);573 const ptr = try self.resolveInst(func, deref.positionals.ptr);
556 const elem_ty = switch (ptr.ty.zigTypeTag()) {574 const elem_ty = switch (ptr.ty.zigTypeTag()) {
...@@ -583,7 +601,8 @@ const Analyze = struct {...@@ -583,7 +601,8 @@ const Analyze = struct {
583 elem.* = try self.resolveConstString(func, assembly.kw_args.clobbers[i]);601 elem.* = try self.resolveConstString(func, assembly.kw_args.clobbers[i]);
584 }602 }
585 for (args) |*elem, i| {603 for (args) |*elem, i| {
586 elem.* = try self.resolveInst(func, assembly.kw_args.args[i]);604 const arg = try self.resolveInst(func, assembly.kw_args.args[i]);
605 elem.* = try self.coerce(func, Type.initTag(.usize), arg);
587 }606 }
588607
589 const f = try self.requireFunctionBody(func, assembly.base.src);608 const f = try self.requireFunctionBody(func, assembly.base.src);
...@@ -602,10 +621,14 @@ const Analyze = struct {...@@ -602,10 +621,14 @@ const Analyze = struct {
602 return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});621 return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
603 }622 }
604623
605 fn coerce(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {624 fn coerce(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst {
625 // If the types are the same, we can return the operand.
626 if (dest_type.eql(inst.ty))
627 return inst;
628
606 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);629 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
607 if (in_memory_result == .ok) {630 if (in_memory_result == .ok) {
608 return self.bitcast(dest_type, inst);631 return self.bitcast(func, dest_type, inst);
609 }632 }
610633
611 // *[N]T to []T634 // *[N]T to []T
...@@ -634,12 +657,14 @@ const Analyze = struct {...@@ -634,12 +657,14 @@ const Analyze = struct {
634 return self.fail(inst.src, "TODO implement type coercion", .{});657 return self.fail(inst.src, "TODO implement type coercion", .{});
635 }658 }
636659
637 fn bitcast(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {660 fn bitcast(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst {
638 if (inst.value()) |val| {661 if (inst.value()) |val| {
639 // Keep the comptime Value representation; take the new type.662 // Keep the comptime Value representation; take the new type.
640 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });663 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
641 }664 }
642 return self.fail(inst.src, "TODO implement runtime bitcast", .{});665 // TODO validate the type size and other compile errors
666 const f = try self.requireFunctionBody(func, inst.src);
667 return self.addNewInstArgs(f, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst });
643 }668 }
644669
645 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {670 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
...@@ -699,7 +724,9 @@ pub fn main() anyerror!void {...@@ -699,7 +724,9 @@ pub fn main() anyerror!void {
699 std.process.exit(1);724 std.process.exit(1);
700 }725 }
701726
702 var analyzed_module = try analyze(allocator, zir_module);727 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
728
729 var analyzed_module = try analyze(allocator, zir_module, native_info.target);
703 defer analyzed_module.deinit(allocator);730 defer analyzed_module.deinit(allocator);
704731
705 if (analyzed_module.errors.len != 0) {732 if (analyzed_module.errors.len != 0) {
...@@ -711,12 +738,27 @@ pub fn main() anyerror!void {...@@ -711,12 +738,27 @@ pub fn main() anyerror!void {
711 std.process.exit(1);738 std.process.exit(1);
712 }739 }
713740
714 var new_zir_module = try text.emit_zir(allocator, analyzed_module);741 const output_zir = true;
715 defer new_zir_module.deinit(allocator);742 if (output_zir) {
743 var new_zir_module = try text.emit_zir(allocator, analyzed_module);
744 defer new_zir_module.deinit(allocator);
716745
717 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());746 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
718 try new_zir_module.writeToStream(allocator, bos.outStream());747 try new_zir_module.writeToStream(allocator, bos.outStream());
719 try bos.flush();748 try bos.flush();
749 }
750
751 const link = @import("link.zig");
752 var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out");
753 defer result.deinit(allocator);
754 if (result.errors.len != 0) {
755 for (result.errors) |err_msg| {
756 const loc = findLineColumn(source, err_msg.byte_offset);
757 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
758 }
759 if (debug_error_trace) return error.ParseFailure;
760 std.process.exit(1);
761 }
720}762}
721763
722fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {764fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
src-self-hosted/ir/text.zig+27
...@@ -31,6 +31,7 @@ pub const Inst = struct {...@@ -31,6 +31,7 @@ pub const Inst = struct {
31 primitive,31 primitive,
32 fntype,32 fntype,
33 intcast,33 intcast,
34 bitcast,
34 };35 };
3536
36 pub fn TagToType(tag: Tag) type {37 pub fn TagToType(tag: Tag) type {
...@@ -48,6 +49,7 @@ pub const Inst = struct {...@@ -48,6 +49,7 @@ pub const Inst = struct {
48 .primitive => Primitive,49 .primitive => Primitive,
49 .fntype => FnType,50 .fntype => FnType,
50 .intcast => IntCast,51 .intcast => IntCast,
52 .bitcast => BitCast,
51 };53 };
52 }54 }
5355
...@@ -258,6 +260,17 @@ pub const Inst = struct {...@@ -258,6 +260,17 @@ pub const Inst = struct {
258 },260 },
259 kw_args: struct {},261 kw_args: struct {},
260 };262 };
263
264 pub const BitCast = struct {
265 pub const base_tag = Tag.bitcast;
266 base: Inst,
267
268 positionals: struct {
269 dest_type: *Inst,
270 operand: *Inst,
271 },
272 kw_args: struct {},
273 };
261};274};
262275
263pub const ErrorMsg = struct {276pub const ErrorMsg = struct {
...@@ -331,6 +344,7 @@ pub const Module = struct {...@@ -331,6 +344,7 @@ pub const Module = struct {
331 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),344 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
332 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),345 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
333 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),346 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
347 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
334 }348 }
335 }349 }
336350
...@@ -957,6 +971,19 @@ const EmitZIR = struct {...@@ -957,6 +971,19 @@ const EmitZIR = struct {
957 };971 };
958 break :blk &new_inst.base;972 break :blk &new_inst.base;
959 },973 },
974 .bitcast => blk: {
975 const old_inst = inst.cast(ir.Inst.BitCast).?;
976 const new_inst = try self.arena.allocator.create(Inst.BitCast);
977 new_inst.* = .{
978 .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag },
979 .positionals = .{
980 .dest_type = try self.emitType(inst.src, inst.ty),
981 .operand = try self.resolveInst(&inst_table, old_inst.args.operand),
982 },
983 .kw_args = .{},
984 };
985 break :blk &new_inst.base;
986 },
960 };987 };
961 try instructions.append(new_inst);988 try instructions.append(new_inst);
962 try inst_table.putNoClobber(inst, new_inst);989 try inst_table.putNoClobber(inst, new_inst);
src-self-hosted/link.zig+748-500
...@@ -1,576 +1,824 @@...@@ -1,576 +1,824 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
3const c = @import("c.zig");
4const Compilation = @import("compilation.zig").Compilation;
5const Target = std.Target;
6const ObjectFormat = Target.ObjectFormat;
7const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
8const assert = std.debug.assert;3const assert = std.debug.assert;
9const util = @import("util.zig");4const Allocator = std.mem.Allocator;
105const ir = @import("ir.zig");
11const Context = struct {6const fs = std.fs;
12 comp: *Compilation,7const elf = std.elf;
13 arena: std.heap.ArenaAllocator,8const codegen = @import("codegen.zig");
14 args: std.ArrayList([*:0]const u8),9
15 link_in_crt: bool,10/// On common systems with a 0o022 umask, 0o777 will still result in a file created
11/// with 0o755 permissions, but it works appropriately if the system is configured
12/// more leniently. As another data point, C's fopen seems to open files with the
13/// 666 mode.
14const executable_mode = 0o777;
15const default_entry_addr = 0x8000000;
16
17pub const ErrorMsg = struct {
18 byte_offset: usize,
19 msg: []const u8,
20};
1621
17 link_err: error{OutOfMemory}!void,22pub const Result = struct {
18 link_msg: std.ArrayListSentineled(u8, 0),23 errors: []ErrorMsg,
1924
20 libc: *LibCInstallation,25 pub fn deinit(self: *Result, allocator: *mem.Allocator) void {
21 out_file_path: std.ArrayListSentineled(u8, 0),26 for (self.errors) |err| {
27 allocator.free(err.msg);
28 }
29 allocator.free(self.errors);
30 self.* = undefined;
31 }
22};32};
2333
24pub fn link(comp: *Compilation) !void {34/// Attempts incremental linking, if the file already exists.
25 var ctx = Context{35/// If incremental linking fails, falls back to truncating the file and rewriting it.
26 .comp = comp,36/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
27 .arena = std.heap.ArenaAllocator.init(comp.gpa()),37/// This operation is not atomic.
28 .args = undefined,38pub fn updateExecutableFilePath(
29 .link_in_crt = comp.haveLibC() and comp.kind == .Exe,39 allocator: *Allocator,
30 .link_err = {},40 module: ir.Module,
31 .link_msg = undefined,41 dir: fs.Dir,
32 .libc = undefined,42 sub_path: []const u8,
33 .out_file_path = undefined,43) !Result {
34 };44 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode });
35 defer ctx.arena.deinit();45 defer file.close();
36 ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator);46
37 ctx.link_msg = std.ArrayListSentineled(u8, 0).initNull(&ctx.arena.allocator);47 return updateExecutableFile(allocator, module, file);
3848}
39 ctx.out_file_path = try std.ArrayListSentineled(u8, 0).init(&ctx.arena.allocator, comp.name.span());49
40 switch (comp.kind) {50/// Atomically overwrites the old file, if present.
41 .Exe => {51pub fn writeExecutableFilePath(
42 try ctx.out_file_path.append(comp.target.exeFileExt());52 allocator: *Allocator,
43 },53 module: ir.Module,
44 .Lib => {54 dir: fs.Dir,
45 try ctx.out_file_path.append(if (comp.is_static) comp.target.staticLibSuffix() else comp.target.dynamicLibSuffix());55 sub_path: []const u8,
46 },56) !Result {
47 .Obj => {57 const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode });
48 try ctx.out_file_path.append(comp.target.oFileExt());58 defer af.deinit();
59
60 const result = try writeExecutableFile(allocator, module, af.file);
61 try af.finish();
62 return result;
63}
64
65/// Attempts incremental linking, if the file already exists.
66/// If incremental linking fails, falls back to truncating the file and rewriting it.
67/// Returns an error if `file` is not already open with +read +write +seek abilities.
68/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
69/// This operation is not atomic.
70pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
71 return updateExecutableFileInner(allocator, module, file) catch |err| switch (err) {
72 error.IncrFailed => {
73 return writeExecutableFile(allocator, module, file);
49 },74 },
50 }75 else => |e| return e,
76 };
77}
5178
52 // even though we're calling LLD as a library it thinks the first79const Update = struct {
53 // argument is its own exe name80 file: fs.File,
54 try ctx.args.append("lld");81 module: *const ir.Module,
55
56 if (comp.haveLibC()) {
57 // TODO https://github.com/ziglang/zig/issues/3190
58 var libc = ctx.comp.override_libc orelse blk: {
59 @panic("this code has bitrotted");
60 //switch (comp.target) {
61 // Target.Native => {
62 // break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
63 // },
64 // else => return error.LibCRequiredButNotProvidedOrFound,
65 //}
66 };
67 ctx.libc = libc;
68 }
6982
70 try constructLinkerArgs(&ctx);83 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
84 /// Same order as in the file.
85 sections: std.ArrayList(elf.Elf64_Shdr),
86 shdr_table_offset: ?u64,
7187
72 if (comp.verbose_link) {88 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
73 for (ctx.args.span()) |arg, i| {89 /// Same order as in the file.
74 const space = if (i == 0) "" else " ";90 program_headers: std.ArrayList(elf.Elf64_Phdr),
75 std.debug.warn("{}{s}", .{ space, arg });91 phdr_table_offset: ?u64,
76 }92 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
77 std.debug.warn("\n", .{});93 phdr_load_re_index: ?u16,
94 entry_addr: ?u64,
95
96 shstrtab: std.ArrayList(u8),
97 shstrtab_index: ?u16,
98
99 text_section_index: ?u16,
100 symtab_section_index: ?u16,
101
102 /// The same order as in the file
103 symbols: std.ArrayList(elf.Elf64_Sym),
104
105 errors: std.ArrayList(ErrorMsg),
106
107 fn deinit(self: *Update) void {
108 self.sections.deinit();
109 self.program_headers.deinit();
110 self.shstrtab.deinit();
111 self.symbols.deinit();
112 self.errors.deinit();
78 }113 }
79114
80 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());115 // `expand_num / expand_den` is the factor of padding when allocation
81 const args_slice = ctx.args.span();116 const alloc_num = 4;
82117 const alloc_den = 3;
83 {118
84 // LLD is not thread-safe, so we grab a global lock.119 /// Returns end pos of collision, if any.
85 const held = comp.zig_compiler.lld_lock.acquire();120 fn detectAllocCollision(self: *Update, start: u64, size: u64) ?u64 {
86 defer held.release();121 const small_ptr = self.module.target.cpu.arch.ptrBitWidth() == 32;
87122 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
88 // Not evented I/O. LLD does its own multithreading internally.123 if (start < ehdr_size)
89 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {124 return ehdr_size;
90 if (!ctx.link_msg.isNull()) {125
91 // TODO capture these messages and pass them through the system, reporting them through the126 const end = start + satMul(size, alloc_num) / alloc_den;
92 // event system instead of printing them directly here.127
93 // perhaps try to parse and understand them.128 if (self.shdr_table_offset) |off| {
94 std.debug.warn("{}\n", .{ctx.link_msg.span()});129 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
130 const tight_size = self.sections.items.len * shdr_size;
131 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
132 const test_end = off + increased_size;
133 if (end > off and start < test_end) {
134 return test_end;
95 }135 }
96 return error.LinkFailed;
97 }136 }
98 }
99}
100137
101extern fn ZigLLDLink(138 if (self.phdr_table_offset) |off| {
102 oformat: c.ZigLLVM_ObjectFormatType,139 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
103 args: [*]const [*]const u8,140 const tight_size = self.sections.items.len * phdr_size;
104 arg_count: usize,141 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
105 append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,142 const test_end = off + increased_size;
106 context: *c_void,143 if (end > off and start < test_end) {
107) bool;144 return test_end;
108145 }
109fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) callconv(.C) void {146 }
110 const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
111 ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);
112}
113147
114fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {148 for (self.sections.items) |section| {
115 if (ctx.link_msg.isNull()) {149 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
116 try ctx.link_msg.resize(0);150 const test_end = section.sh_offset + increased_size;
151 if (end > section.sh_offset and start < test_end) {
152 return test_end;
153 }
154 }
155 for (self.program_headers.items) |program_header| {
156 const increased_size = satMul(program_header.p_filesz, alloc_num) / alloc_den;
157 const test_end = program_header.p_offset + increased_size;
158 if (end > program_header.p_offset and start < test_end) {
159 return test_end;
160 }
161 }
162 return null;
117 }163 }
118 try ctx.link_msg.append(msg);
119}
120164
121fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType {165 fn allocatedSize(self: *Update, start: u64) u64 {
122 return switch (ofmt) {166 var min_pos: u64 = std.math.maxInt(u64);
123 .unknown => .ZigLLVM_UnknownObjectFormat,167 if (self.shdr_table_offset) |off| {
124 .coff => .ZigLLVM_COFF,168 if (off > start and off < min_pos) min_pos = off;
125 .elf => .ZigLLVM_ELF,169 }
126 .macho => .ZigLLVM_MachO,170 if (self.phdr_table_offset) |off| {
127 .wasm => .ZigLLVM_Wasm,171 if (off > start and off < min_pos) min_pos = off;
128 };172 }
129}173 for (self.sections.items) |section| {
130174 if (section.sh_offset <= start) continue;
131fn constructLinkerArgs(ctx: *Context) !void {175 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
132 switch (ctx.comp.target.getObjectFormat()) {176 }
133 .unknown => unreachable,177 for (self.program_headers.items) |program_header| {
134 .coff => return constructLinkerArgsCoff(ctx),178 if (program_header.p_offset <= start) continue;
135 .elf => return constructLinkerArgsElf(ctx),179 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
136 .macho => return constructLinkerArgsMachO(ctx),180 }
137 .wasm => return constructLinkerArgsWasm(ctx),181 return min_pos - start;
138 }182 }
139}
140183
141fn constructLinkerArgsElf(ctx: *Context) !void {184 fn findFreeSpace(self: *Update, object_size: u64, min_alignment: u16) u64 {
142 // TODO commented out code in this function185 var start: u64 = 0;
143 //if (g->linker_script) {186 while (self.detectAllocCollision(start, object_size)) |item_end| {
144 // lj->args.append("-T");187 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
145 // lj->args.append(g->linker_script);188 }
146 //}189 return start;
147 try ctx.args.append("--gc-sections");
148 if (ctx.comp.link_eh_frame_hdr) {
149 try ctx.args.append("--eh-frame-hdr");
150 }190 }
151191
152 //lj->args.append("-m");192 fn makeString(self: *Update, bytes: []const u8) !u32 {
153 //lj->args.append(getLDMOption(&g->zig_target));193 const result = self.shstrtab.items.len;
154194 try self.shstrtab.appendSlice(bytes);
155 //bool is_lib = g->out_type == OutTypeLib;195 try self.shstrtab.append(0);
156 //bool shared = !g->is_static && is_lib;196 return @intCast(u32, result);
157 //Buf *soname = nullptr;
158 if (ctx.comp.is_static) {
159 //if (util.isArmOrThumb(ctx.comp.target)) {
160 // try ctx.args.append("-Bstatic");
161 //} else {
162 // try ctx.args.append("-static");
163 //}
164 }
165 //} else if (shared) {
166 // lj->args.append("-shared");
167
168 // if (buf_len(&lj->out_file) == 0) {
169 // buf_appendf(&lj->out_file, "lib%s.so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize "",
170 // buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
171 // }
172 // soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
173 //}
174
175 try ctx.args.append("-o");
176 try ctx.args.append(ctx.out_file_path.span());
177
178 if (ctx.link_in_crt) {
179 const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
180 try addPathJoin(ctx, ctx.libc.crt_dir.?, crt1o);
181 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crti.o");
182 }197 }
183198
184 if (ctx.comp.haveLibC()) {199 fn perform(self: *Update) !void {
185 try ctx.args.append("-L");200 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
186 // TODO addNullByte should probably return [:0]u8201 32 => .p32,
187 try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.crt_dir.?)).ptr));202 64 => .p64,
188203 else => return error.UnsupportedArchitecture,
189 //if (!ctx.comp.is_static) {204 };
190 // const dl = blk: {205 const small_ptr = switch (ptr_width) {
191 // //if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;206 .p32 => true,
192 // //if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl;207 .p64 => false,
193 // return error.LibCMissingDynamicLinker;208 };
194 // };209 // This means the entire read-only executable program code needs to be rewritten.
195 // try ctx.args.append("-dynamic-linker");210 var phdr_load_re_dirty = false;
196 // try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));211 var phdr_table_dirty = false;
197 //}212 var shdr_table_dirty = false;
198 }213 var shstrtab_dirty = false;
214 var symtab_dirty = false;
215
216 if (self.phdr_load_re_index == null) {
217 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
218 const file_size = 256 * 1024;
219 const p_align = 0x1000;
220 const off = self.findFreeSpace(file_size, p_align);
221 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
222 try self.program_headers.append(.{
223 .p_type = elf.PT_LOAD,
224 .p_offset = off,
225 .p_filesz = file_size,
226 .p_vaddr = default_entry_addr,
227 .p_paddr = default_entry_addr,
228 .p_memsz = 0,
229 .p_align = p_align,
230 .p_flags = elf.PF_X | elf.PF_R,
231 });
232 self.entry_addr = null;
233 phdr_load_re_dirty = true;
234 phdr_table_dirty = true;
235 }
236 if (self.sections.items.len == 0) {
237 // There must always be a null section in index 0
238 try self.sections.append(.{
239 .sh_name = 0,
240 .sh_type = elf.SHT_NULL,
241 .sh_flags = 0,
242 .sh_addr = 0,
243 .sh_offset = 0,
244 .sh_size = 0,
245 .sh_link = 0,
246 .sh_info = 0,
247 .sh_addralign = 0,
248 .sh_entsize = 0,
249 });
250 shdr_table_dirty = true;
251 }
252 if (self.shstrtab_index == null) {
253 self.shstrtab_index = @intCast(u16, self.sections.items.len);
254 assert(self.shstrtab.items.len == 0);
255 try self.shstrtab.append(0); // need a 0 at position 0
256 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
257 //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
258 try self.sections.append(.{
259 .sh_name = try self.makeString(".shstrtab"),
260 .sh_type = elf.SHT_STRTAB,
261 .sh_flags = 0,
262 .sh_addr = 0,
263 .sh_offset = off,
264 .sh_size = self.shstrtab.items.len,
265 .sh_link = 0,
266 .sh_info = 0,
267 .sh_addralign = 1,
268 .sh_entsize = 0,
269 });
270 shstrtab_dirty = true;
271 shdr_table_dirty = true;
272 }
273 if (self.text_section_index == null) {
274 self.text_section_index = @intCast(u16, self.sections.items.len);
275 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
276
277 try self.sections.append(.{
278 .sh_name = try self.makeString(".text"),
279 .sh_type = elf.SHT_PROGBITS,
280 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
281 .sh_addr = phdr.p_vaddr,
282 .sh_offset = phdr.p_offset,
283 .sh_size = phdr.p_filesz,
284 .sh_link = 0,
285 .sh_info = 0,
286 .sh_addralign = phdr.p_align,
287 .sh_entsize = 0,
288 });
289 shdr_table_dirty = true;
290 }
291 if (self.symtab_section_index == null) {
292 self.symtab_section_index = @intCast(u16, self.sections.items.len);
293 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
294 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
295 const file_size = self.module.exports.len * each_size;
296 const off = self.findFreeSpace(file_size, min_align);
297 //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
298
299 try self.sections.append(.{
300 .sh_name = try self.makeString(".symtab"),
301 .sh_type = elf.SHT_SYMTAB,
302 .sh_flags = 0,
303 .sh_addr = 0,
304 .sh_offset = off,
305 .sh_size = file_size,
306 // The section header index of the associated string table.
307 .sh_link = self.shstrtab_index.?,
308 .sh_info = @intCast(u32, self.module.exports.len),
309 .sh_addralign = min_align,
310 .sh_entsize = each_size,
311 });
312 symtab_dirty = true;
313 shdr_table_dirty = true;
314 }
315 const shsize: u64 = switch (ptr_width) {
316 .p32 => @sizeOf(elf.Elf32_Shdr),
317 .p64 => @sizeOf(elf.Elf64_Shdr),
318 };
319 const shalign: u16 = switch (ptr_width) {
320 .p32 => @alignOf(elf.Elf32_Shdr),
321 .p64 => @alignOf(elf.Elf64_Shdr),
322 };
323 if (self.shdr_table_offset == null) {
324 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
325 shdr_table_dirty = true;
326 }
327 const phsize: u64 = switch (ptr_width) {
328 .p32 => @sizeOf(elf.Elf32_Phdr),
329 .p64 => @sizeOf(elf.Elf64_Phdr),
330 };
331 const phalign: u16 = switch (ptr_width) {
332 .p32 => @alignOf(elf.Elf32_Phdr),
333 .p64 => @alignOf(elf.Elf64_Phdr),
334 };
335 if (self.phdr_table_offset == null) {
336 self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
337 phdr_table_dirty = true;
338 }
339 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
199340
200 //if (shared) {341 try self.writeCodeAndSymbols(phdr_table_dirty, shdr_table_dirty);
201 // lj->args.append("-soname");
202 // lj->args.append(buf_ptr(soname));
203 //}
204342
205 // .o files343 if (phdr_table_dirty) {
206 for (ctx.comp.link_objects) |link_object| {344 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
207 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);345 const needed_size = self.program_headers.items.len * phsize;
208 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));346
209 }347 if (needed_size > allocated_size) {
210 try addFnObjects(ctx);348 self.phdr_table_offset = null; // free the space
211349 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
212 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {350 }
213 // if (g->libc_link_lib == nullptr) {351
214 // Buf *builtin_o_path = build_o(g, "builtin");352 const allocator = self.program_headers.allocator;
215 // lj->args.append(buf_ptr(builtin_o_path));353 switch (ptr_width) {
216 // }354 .p32 => {
217355 const buf = try allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
218 // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage356 defer allocator.free(buf);
219 // Buf *compiler_rt_o_path = build_compiler_rt(g);357
220 // lj->args.append(buf_ptr(compiler_rt_o_path));358 for (buf) |*phdr, i| {
221 //}359 phdr.* = progHeaderTo32(self.program_headers.items[i]);
222360 if (foreign_endian) {
223 //for (size_t i = 0; i < g->link_libs_list.length; i += 1) {361 bswapAllFields(elf.Elf32_Phdr, phdr);
224 // LinkLib *link_lib = g->link_libs_list.at(i);362 }
225 // if (buf_eql_str(link_lib->name, "c")) {363 }
226 // continue;364 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
227 // }365 },
228 // Buf *arg;366 .p64 => {
229 // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||367 const buf = try allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
230 // buf_ends_with_str(link_lib->name, ".so"))368 defer allocator.free(buf);
231 // {369
232 // arg = link_lib->name;370 for (buf) |*phdr, i| {
233 // } else {371 phdr.* = self.program_headers.items[i];
234 // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));372 if (foreign_endian) {
235 // }373 bswapAllFields(elf.Elf64_Phdr, phdr);
236 // lj->args.append(buf_ptr(arg));374 }
237 //}375 }
238376 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
239 // libc dep377 },
240 if (ctx.comp.haveLibC()) {378 }
241 if (ctx.comp.is_static) {379 }
242 try ctx.args.append("--start-group");380
243 try ctx.args.append("-lgcc");381 {
244 try ctx.args.append("-lgcc_eh");382 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
245 try ctx.args.append("-lc");383 if (shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
246 try ctx.args.append("-lm");384 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
247 try ctx.args.append("--end-group");385 const needed_size = self.shstrtab.items.len;
386
387 if (needed_size > allocated_size) {
388 shstrtab_sect.sh_size = 0; // free the space
389 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
390 }
391 shstrtab_sect.sh_size = needed_size;
392 //std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
393
394 try self.file.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
395 if (!shdr_table_dirty) {
396 // Then it won't get written with the others and we need to do it.
397 try self.writeSectHeader(self.shstrtab_index.?);
398 }
399 }
400 }
401 if (shdr_table_dirty) {
402 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
403 const needed_size = self.sections.items.len * phsize;
404
405 if (needed_size > allocated_size) {
406 self.shdr_table_offset = null; // free the space
407 self.shdr_table_offset = self.findFreeSpace(needed_size, phalign);
408 }
409
410 const allocator = self.sections.allocator;
411 switch (ptr_width) {
412 .p32 => {
413 const buf = try allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
414 defer allocator.free(buf);
415
416 for (buf) |*shdr, i| {
417 shdr.* = sectHeaderTo32(self.sections.items[i]);
418 if (foreign_endian) {
419 bswapAllFields(elf.Elf32_Shdr, shdr);
420 }
421 }
422 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
423 },
424 .p64 => {
425 const buf = try allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
426 defer allocator.free(buf);
427
428 for (buf) |*shdr, i| {
429 shdr.* = self.sections.items[i];
430 //std.debug.warn("writing section {}\n", .{shdr.*});
431 if (foreign_endian) {
432 bswapAllFields(elf.Elf64_Shdr, shdr);
433 }
434 }
435 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
436 },
437 }
438 }
439 if (self.entry_addr == null) {
440 const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{});
441 errdefer self.errors.allocator.free(msg);
442 try self.errors.append(.{
443 .byte_offset = 0,
444 .msg = msg,
445 });
248 } else {446 } else {
249 try ctx.args.append("-lgcc");447 try self.writeElfHeader();
250 try ctx.args.append("--as-needed");
251 try ctx.args.append("-lgcc_s");
252 try ctx.args.append("--no-as-needed");
253 try ctx.args.append("-lc");
254 try ctx.args.append("-lm");
255 try ctx.args.append("-lgcc");
256 try ctx.args.append("--as-needed");
257 try ctx.args.append("-lgcc_s");
258 try ctx.args.append("--no-as-needed");
259 }448 }
449 // TODO find end pos and truncate
260 }450 }
261451
262 // crt end452 fn writeElfHeader(self: *Update) !void {
263 if (ctx.link_in_crt) {453 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
264 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crtn.o");
265 }
266454
267 //if (ctx.comp.target != Target.Native) {455 var index: usize = 0;
268 // try ctx.args.append("--allow-shlib-undefined");456 hdr_buf[0..4].* = "\x7fELF".*;
269 //}457 index += 4;
270}
271458
272fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {459 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
273 const full_path = try std.fs.path.join(&ctx.arena.allocator, &[_][]const u8{ dirname, basename });460 32 => .p32,
274 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);461 64 => .p64,
275 try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr));462 else => return error.UnsupportedArchitecture,
276}463 };
464 hdr_buf[index] = switch (ptr_width) {
465 .p32 => elf.ELFCLASS32,
466 .p64 => elf.ELFCLASS64,
467 };
468 index += 1;
277469
278fn constructLinkerArgsCoff(ctx: *Context) !void {470 const endian = self.module.target.cpu.arch.endian();
279 try ctx.args.append("-NOLOGO");471 hdr_buf[index] = switch (endian) {
472 .Little => elf.ELFDATA2LSB,
473 .Big => elf.ELFDATA2MSB,
474 };
475 index += 1;
280476
281 if (!ctx.comp.strip) {477 hdr_buf[index] = 1; // ELF version
282 try ctx.args.append("-DEBUG");478 index += 1;
283 }
284479
285 switch (ctx.comp.target.cpu.arch) {480 // OS ABI, often set to 0 regardless of target platform
286 .i386 => try ctx.args.append("-MACHINE:X86"),481 // ABI Version, possibly used by glibc but not by static executables
287 .x86_64 => try ctx.args.append("-MACHINE:X64"),482 // padding
288 .aarch64 => try ctx.args.append("-MACHINE:ARM"),483 mem.set(u8, hdr_buf[index..][0..9], 0);
289 else => return error.UnsupportedLinkArchitecture,484 index += 9;
290 }
291485
292 const is_library = ctx.comp.kind == .Lib;486 assert(index == 16);
293487
294 const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", .{ctx.out_file_path.span()});488 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian);
295 try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr));489 index += 2;
296490
297 if (ctx.comp.haveLibC()) {491 const machine = self.module.target.cpu.arch.toElfMachine();
298 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr));492 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
299 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr));493 index += 2;
300 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.crt_dir.?})).ptr));
301 }
302494
303 if (ctx.link_in_crt) {495 // ELF Version, again
304 const lib_str = if (ctx.comp.is_static) "lib" else "";496 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
305 const d_str = if (ctx.comp.build_mode == .Debug) "d" else "";497 index += 4;
306498
307 if (ctx.comp.is_static) {499 switch (ptr_width) {
308 const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str});500 .p32 => {
309 try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr));501 // e_entry
310 } else {502 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian);
311 const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str});503 index += 4;
312 try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr));504
313 }505 // e_phoff
506 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
507 index += 4;
314508
315 const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", .{509 // e_shoff
316 lib_str,510 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
317 d_str,511 index += 4;
318 });512 },
319 try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr));513 .p64 => {
320514 // e_entry
321 const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str });515 mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian);
322 try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr));516 index += 8;
323517
324 // Visual C++ 2015 Conformance Changes518 // e_phoff
325 // https://msdn.microsoft.com/en-us/library/bb531344.aspx519 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
326 try ctx.args.append("legacy_stdio_definitions.lib");520 index += 8;
327521
328 // msvcrt depends on kernel32522 // e_shoff
329 try ctx.args.append("kernel32.lib");523 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
330 } else {524 index += 8;
331 try ctx.args.append("-NODEFAULTLIB");525 },
332 if (!is_library) {
333 try ctx.args.append("-ENTRY:WinMainCRTStartup");
334 }526 }
335 }
336527
337 if (is_library and !ctx.comp.is_static) {528 const e_flags = 0;
338 try ctx.args.append("-DLL");529 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
339 }530 index += 4;
340531
341 for (ctx.comp.link_objects) |link_object| {532 const e_ehsize: u16 = switch (ptr_width) {
342 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);533 .p32 => @sizeOf(elf.Elf32_Ehdr),
343 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));534 .p64 => @sizeOf(elf.Elf64_Ehdr),
344 }535 };
345 try addFnObjects(ctx);536 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
537 index += 2;
346538
347 switch (ctx.comp.kind) {539 const e_phentsize: u16 = switch (ptr_width) {
348 .Exe, .Lib => {540 .p32 => @sizeOf(elf.Elf32_Phdr),
349 if (!ctx.comp.haveLibC()) {541 .p64 => @sizeOf(elf.Elf64_Phdr),
350 @panic("TODO");542 };
351 }543 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
352 },544 index += 2;
353 .Obj => {},
354 }
355}
356545
357fn constructLinkerArgsMachO(ctx: *Context) !void {546 const e_phnum = @intCast(u16, self.program_headers.items.len);
358 try ctx.args.append("-demangle");547 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
548 index += 2;
359549
360 if (ctx.comp.linker_rdynamic) {550 const e_shentsize: u16 = switch (ptr_width) {
361 try ctx.args.append("-export_dynamic");551 .p32 => @sizeOf(elf.Elf32_Shdr),
362 }552 .p64 => @sizeOf(elf.Elf64_Shdr),
553 };
554 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
555 index += 2;
363556
364 const is_lib = ctx.comp.kind == .Lib;557 const e_shnum = @intCast(u16, self.sections.items.len);
365 const shared = !ctx.comp.is_static and is_lib;558 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
366 if (ctx.comp.is_static) {559 index += 2;
367 try ctx.args.append("-static");
368 } else {
369 try ctx.args.append("-dynamic");
370 }
371560
372 try ctx.args.append("-arch");561 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
373 try ctx.args.append(util.getDarwinArchString(ctx.comp.target));562 index += 2;
374563
375 const platform = try DarwinPlatform.get(ctx.comp);564 assert(index == e_ehsize);
376 switch (platform.kind) {565
377 .MacOS => try ctx.args.append("-macosx_version_min"),566 try self.file.pwriteAll(hdr_buf[0..index], 0);
378 .IPhoneOS => try ctx.args.append("-iphoneos_version_min"),
379 .IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"),
380 }567 }
381 const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", .{568
382 platform.major,569 fn writeCodeAndSymbols(self: *Update, phdr_table_dirty: bool, shdr_table_dirty: bool) !void {
383 platform.minor,570 // index 0 is always a null symbol
384 platform.micro,571 try self.symbols.resize(1);
385 });572 self.symbols.items[0] = .{
386 try ctx.args.append(@ptrCast([*:0]const u8, ver_str.ptr));573 .st_name = 0,
387574 .st_info = 0,
388 if (ctx.comp.kind == .Exe) {575 .st_other = 0,
389 if (ctx.comp.is_static) {576 .st_shndx = 0,
390 try ctx.args.append("-no_pie");577 .st_value = 0,
391 } else {578 .st_size = 0,
392 try ctx.args.append("-pie");579 };
580
581 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
582 var vaddr: u64 = phdr.p_vaddr;
583 var file_off: u64 = phdr.p_offset;
584
585 var code = std.ArrayList(u8).init(self.sections.allocator);
586 defer code.deinit();
587
588 for (self.module.exports) |exp| {
589 code.shrink(0);
590 var symbol = try codegen.generateSymbol(exp.typed_value, self.module.*, &code);
591 defer symbol.deinit(code.allocator);
592 if (symbol.errors.len != 0) {
593 for (symbol.errors) |err| {
594 const msg = try mem.dupe(self.errors.allocator, u8, err.msg);
595 errdefer self.errors.allocator.free(msg);
596 try self.errors.append(.{
597 .byte_offset = err.byte_offset,
598 .msg = msg,
599 });
600 }
601 continue;
602 }
603 try self.file.pwriteAll(code.items, file_off);
604
605 if (mem.eql(u8, exp.name, "_start")) {
606 self.entry_addr = vaddr;
607 }
608 (try self.symbols.addOne()).* = .{
609 .st_name = try self.makeString(exp.name),
610 .st_info = (elf.STB_LOCAL << 4) | elf.STT_FUNC,
611 .st_other = 0,
612 .st_shndx = self.text_section_index.?,
613 .st_value = vaddr,
614 .st_size = code.items.len,
615 };
616 vaddr += code.items.len;
617 }
618
619 {
620 // Now that we know the code size, we need to update the program header for executable code
621 phdr.p_memsz = vaddr - phdr.p_vaddr;
622 phdr.p_filesz = phdr.p_memsz;
623
624 const shdr = &self.sections.items[self.text_section_index.?];
625 shdr.sh_size = phdr.p_filesz;
626
627 if (!phdr_table_dirty) {
628 // Then it won't get written with the others and we need to do it.
629 try self.writeProgHeader(self.phdr_load_re_index.?);
630 }
631 if (!shdr_table_dirty) {
632 // Then it won't get written with the others and we need to do it.
633 try self.writeSectHeader(self.text_section_index.?);
634 }
393 }635 }
636
637 return self.writeSymbols();
394 }638 }
395639
396 try ctx.args.append("-o");640 fn writeProgHeader(self: *Update, index: usize) !void {
397 try ctx.args.append(ctx.out_file_path.span());641 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
398642 const offset = self.program_headers.items[index].p_offset;
399 if (shared) {643 switch (self.module.target.cpu.arch.ptrBitWidth()) {
400 try ctx.args.append("-headerpad_max_install_names");644 32 => {
401 } else if (ctx.comp.is_static) {645 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
402 try ctx.args.append("-lcrt0.o");646 if (foreign_endian) {
403 } else {647 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
404 switch (platform.kind) {
405 .MacOS => {
406 if (platform.versionLessThan(10, 5)) {
407 try ctx.args.append("-lcrt1.o");
408 } else if (platform.versionLessThan(10, 6)) {
409 try ctx.args.append("-lcrt1.10.5.o");
410 } else if (platform.versionLessThan(10, 8)) {
411 try ctx.args.append("-lcrt1.10.6.o");
412 }648 }
649 return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset);
413 },650 },
414 .IPhoneOS => {651 64 => {
415 if (ctx.comp.target.cpu.arch == .aarch64) {652 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
416 // iOS does not need any crt1 files for arm64653 if (foreign_endian) {
417 } else if (platform.versionLessThan(3, 1)) {654 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
418 try ctx.args.append("-lcrt1.o");
419 } else if (platform.versionLessThan(6, 0)) {
420 try ctx.args.append("-lcrt1.3.1.o");
421 }655 }
656 return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset);
422 },657 },
423 .IPhoneOSSimulator => {}, // no crt1.o needed658 else => return error.UnsupportedArchitecture,
424 }659 }
425 }660 }
426661
427 for (ctx.comp.link_objects) |link_object| {662 fn writeSectHeader(self: *Update, index: usize) !void {
428 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);663 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
429 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));664 const offset = self.sections.items[index].sh_offset;
430 }665 switch (self.module.target.cpu.arch.ptrBitWidth()) {
431 try addFnObjects(ctx);666 32 => {
432667 var shdr: [1]elf.Elf32_Shdr = undefined;
433 // TODO668 shdr[0] = sectHeaderTo32(self.sections.items[index]);
434 //if (ctx.comp.target == Target.Native) {669 if (foreign_endian) {
435 // for (ctx.comp.link_libs_list.span()) |lib| {670 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
436 // if (mem.eql(u8, lib.name, "c")) {671 }
437 // // on Darwin, libSystem has libc in it, but also you have to use it672 return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset);
438 // // to make syscalls because the syscall numbers are not documented673 },
439 // // and change between versions.674 64 => {
440 // // so we always link against libSystem675 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
441 // try ctx.args.append("-lSystem");676 if (foreign_endian) {
442 // } else {677 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
443 // if (mem.indexOfScalar(u8, lib.name, '/') == null) {678 }
444 // const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name});679 return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset);
445 // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));680 },
446 // } else {681 else => return error.UnsupportedArchitecture,
447 // const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
448 // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
449 // }
450 // }
451 // }
452 //} else {
453 // try ctx.args.append("-undefined");
454 // try ctx.args.append("dynamic_lookup");
455 //}
456
457 if (platform.kind == .MacOS) {
458 if (platform.versionLessThan(10, 5)) {
459 try ctx.args.append("-lgcc_s.10.4");
460 } else if (platform.versionLessThan(10, 6)) {
461 try ctx.args.append("-lgcc_s.10.5");
462 }682 }
463 } else {
464 @panic("TODO");
465 }683 }
466}
467
468fn constructLinkerArgsWasm(ctx: *Context) void {
469 @panic("TODO");
470}
471684
472fn addFnObjects(ctx: *Context) !void {685 fn writeSymbols(self: *Update) !void {
473 const held = ctx.comp.fn_link_set.acquire();686 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
474 defer held.release();687 32 => .p32,
475688 64 => .p64,
476 var it = held.value.first;689 else => return error.UnsupportedArchitecture,
477 while (it) |node| {
478 const fn_val = node.data orelse {
479 // handle the tombstone. See Value.Fn.destroy.
480 it = node.next;
481 held.value.remove(node);
482 ctx.comp.gpa().destroy(node);
483 continue;
484 };690 };
485 try ctx.args.append(fn_val.containing_object.span());691 const small_ptr = ptr_width == .p32;
486 it = node.next;692 const syms_sect = &self.sections.items[self.symtab_section_index.?];
693 const sym_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
694 const sym_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
695
696 const allocated_size = self.allocatedSize(syms_sect.sh_offset);
697 const needed_size = self.symbols.items.len * sym_size;
698 if (needed_size > allocated_size) {
699 syms_sect.sh_size = 0; // free the space
700 syms_sect.sh_offset = self.findFreeSpace(needed_size, sym_align);
701 //std.debug.warn("moved symtab to 0x{x} to 0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
702 }
703 //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
704 syms_sect.sh_size = needed_size;
705 syms_sect.sh_info = @intCast(u32, self.symbols.items.len);
706 const allocator = self.symbols.allocator;
707 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
708 switch (ptr_width) {
709 .p32 => {
710 const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len);
711 defer allocator.free(buf);
712
713 for (buf) |*sym, i| {
714 sym.* = .{
715 .st_name = self.symbols.items[i].st_name,
716 .st_value = @intCast(u32, self.symbols.items[i].st_value),
717 .st_size = @intCast(u32, self.symbols.items[i].st_size),
718 .st_info = self.symbols.items[i].st_info,
719 .st_other = self.symbols.items[i].st_other,
720 .st_shndx = self.symbols.items[i].st_shndx,
721 };
722 if (foreign_endian) {
723 bswapAllFields(elf.Elf32_Sym, sym);
724 }
725 }
726 try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);
727 },
728 .p64 => {
729 const buf = try allocator.alloc(elf.Elf64_Sym, self.symbols.items.len);
730 defer allocator.free(buf);
731
732 for (buf) |*sym, i| {
733 sym.* = .{
734 .st_name = self.symbols.items[i].st_name,
735 .st_value = self.symbols.items[i].st_value,
736 .st_size = self.symbols.items[i].st_size,
737 .st_info = self.symbols.items[i].st_info,
738 .st_other = self.symbols.items[i].st_other,
739 .st_shndx = self.symbols.items[i].st_shndx,
740 };
741 if (foreign_endian) {
742 bswapAllFields(elf.Elf64_Sym, sym);
743 }
744 }
745 try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);
746 },
747 }
487 }748 }
488}749};
489750
490const DarwinPlatform = struct {751/// Truncates the existing file contents and overwrites the contents.
491 kind: Kind,752/// Returns an error if `file` is not already open with +read +write +seek abilities.
492 major: u32,753pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
493 minor: u32,754 var update = Update{
494 micro: u32,755 .file = file,
756 .module = &module,
757 .sections = std.ArrayList(elf.Elf64_Shdr).init(allocator),
758 .shdr_table_offset = null,
759 .program_headers = std.ArrayList(elf.Elf64_Phdr).init(allocator),
760 .phdr_table_offset = null,
761 .phdr_load_re_index = null,
762 .entry_addr = null,
763 .shstrtab = std.ArrayList(u8).init(allocator),
764 .shstrtab_index = null,
765 .text_section_index = null,
766 .symtab_section_index = null,
767
768 .symbols = std.ArrayList(elf.Elf64_Sym).init(allocator),
769
770 .errors = std.ArrayList(ErrorMsg).init(allocator),
771 };
772 defer update.deinit();
495773
496 const Kind = enum {774 try update.perform();
497 MacOS,775 return Result{
498 IPhoneOS,776 .errors = update.errors.toOwnedSlice(),
499 IPhoneOSSimulator,
500 };777 };
778}
501779
502 fn get(comp: *Compilation) !DarwinPlatform {780/// Returns error.IncrFailed if incremental update could not be performed.
503 var result: DarwinPlatform = undefined;781fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
504 const ver_str = switch (comp.darwin_version_min) {782 //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
505 .MacOS => |ver| blk: {
506 result.kind = .MacOS;
507 break :blk ver;
508 },
509 .Ios => |ver| blk: {
510 result.kind = .IPhoneOS;
511 break :blk ver;
512 },
513 .None => blk: {
514 assert(comp.target.os.tag == .macosx);
515 result.kind = .MacOS;
516 break :blk "10.14";
517 },
518 };
519783
520 var had_extra: bool = undefined;784 // TODO implement incremental linking
521 try darwinGetReleaseVersion(785 return error.IncrFailed;
522 ver_str,786}
523 &result.major,
524 &result.minor,
525 &result.micro,
526 &had_extra,
527 );
528 if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
529 return error.InvalidDarwinVersionString;
530 }
531787
532 if (result.kind == .IPhoneOS) {788/// Saturating multiplication
533 switch (comp.target.cpu.arch) {789fn satMul(a: var, b: var) @TypeOf(a, b) {
534 .i386,790 const T = @TypeOf(a, b);
535 .x86_64,791 return std.math.mul(T, a, b) catch std.math.maxInt(T);
536 => result.kind = .IPhoneOSSimulator,792}
537 else => {},
538 }
539 }
540 return result;
541 }
542793
543 fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {794fn bswapAllFields(comptime S: type, ptr: *S) void {
544 if (self.major < major)795 @panic("TODO implement bswapAllFields");
545 return true;796}
546 if (self.major > major)
547 return false;
548 if (self.minor < minor)
549 return true;
550 return false;
551 }
552};
553797
554/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the798fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
555/// grouped values as integers. Numbers which are not provided are set to 0.799 return .{
556/// return true if the entire string was parsed (9.2), or all groups were800 .p_type = phdr.p_type,
557/// parsed (10.3.5extrastuff).801 .p_flags = phdr.p_flags,
558fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {802 .p_offset = @intCast(u32, phdr.p_offset),
559 major.* = 0;803 .p_vaddr = @intCast(u32, phdr.p_vaddr),
560 minor.* = 0;804 .p_paddr = @intCast(u32, phdr.p_paddr),
561 micro.* = 0;805 .p_filesz = @intCast(u32, phdr.p_filesz),
562 had_extra.* = false;806 .p_memsz = @intCast(u32, phdr.p_memsz),
563807 .p_align = @intCast(u32, phdr.p_align),
564 if (str.len == 0)808 };
565 return error.InvalidDarwinVersionString;809}
566810
567 var start_pos: usize = 0;811fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
568 for ([_]*u32{ major, minor, micro }) |v| {812 return .{
569 const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');813 .sh_name = shdr.sh_name,
570 const end_pos = dot_pos orelse str.len;814 .sh_type = shdr.sh_type,
571 v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;815 .sh_flags = @intCast(u32, shdr.sh_flags),
572 start_pos = (dot_pos orelse return) + 1;816 .sh_addr = @intCast(u32, shdr.sh_addr),
573 if (start_pos == str.len) return;817 .sh_offset = @intCast(u32, shdr.sh_offset),
574 }818 .sh_size = @intCast(u32, shdr.sh_size),
575 had_extra.* = true;819 .sh_link = shdr.sh_link,
820 .sh_info = shdr.sh_info,
821 .sh_addralign = @intCast(u32, shdr.sh_addralign),
822 .sh_entsize = @intCast(u32, shdr.sh_entsize),
823 };
576}824}
src-self-hosted/value.zig+50
...@@ -264,6 +264,56 @@ pub const Value = extern union {...@@ -264,6 +264,56 @@ pub const Value = extern union {
264 }264 }
265 }265 }
266266
267 /// Asserts the value is an integer and it fits in a u64
268 pub fn toUnsignedInt(self: Value) u64 {
269 switch (self.tag()) {
270 .ty,
271 .u8_type,
272 .i8_type,
273 .isize_type,
274 .usize_type,
275 .c_short_type,
276 .c_ushort_type,
277 .c_int_type,
278 .c_uint_type,
279 .c_long_type,
280 .c_ulong_type,
281 .c_longlong_type,
282 .c_ulonglong_type,
283 .c_longdouble_type,
284 .f16_type,
285 .f32_type,
286 .f64_type,
287 .f128_type,
288 .c_void_type,
289 .bool_type,
290 .void_type,
291 .type_type,
292 .anyerror_type,
293 .comptime_int_type,
294 .comptime_float_type,
295 .noreturn_type,
296 .fn_naked_noreturn_no_args_type,
297 .single_const_pointer_to_comptime_int_type,
298 .const_slice_u8_type,
299 .void_value,
300 .noreturn_value,
301 .bool_true,
302 .bool_false,
303 .function,
304 .ref,
305 .ref_val,
306 .bytes,
307 => unreachable,
308
309 .zero => return 0,
310
311 .int_u64 => return self.cast(Payload.Int_u64).?.int,
312 .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int),
313 .int_big => return self.cast(Payload.IntBig).?.big_int.to(u64) catch unreachable,
314 }
315 }
316
267 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.317 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
268 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {318 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
269 switch (self.tag()) {319 switch (self.tag()) {