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
signaturebadge-question-mark 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 {
13451345 mode: File.Mode = File.default_mode,
13461346 };
13471347
1348 /// `dest_path` must remain valid for the lifetime of `AtomicFile`.
1349 /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.
1348 /// Directly access the `.file` field, and then call `AtomicFile.finish`
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.
13501352 pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {
13511353 if (path.dirname(dest_path)) |dirname| {
13521354 const dir = try self.openDir(dirname, .{});
lib/std/fs/file.zig+1-1
......@@ -93,7 +93,7 @@ pub const File = struct {
9393 /// This means that a process that does not respect the locking API can still get access
9494 /// to the file, despite the lock.
9595 ///
96 /// Windows' file locks are mandatory, and any process attempting to access the file will
96 /// Windows's file locks are mandatory, and any process attempting to access the file will
9797 /// receive an error.
9898 ///
9999 /// [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" {
20272027/// Round an address up to the nearest aligned address
20282028/// The alignment must be a power of 2 and greater than 0.
20292029pub 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);
20312037}
20322038
20332039test "alignForward" {
......@@ -2048,8 +2054,14 @@ test "alignForward" {
20482054/// Round an address up to the previous aligned address
20492055/// The alignment must be a power of 2 and greater than 0.
20502056pub fn alignBackward(addr: usize, alignment: usize) usize {
2051 assert(@popCount(usize, alignment) == 1);
2052 // 000010000 // example addr
2057 return alignBackwardGeneric(usize, addr, alignment);
2058}
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
20532065 // 000001111 // subtract 1
20542066 // 111110000 // binary not
20552067 return addr & ~(alignment - 1);
src-self-hosted/codegen.zig+482-405
......@@ -1,447 +1,524 @@
11const std = @import("std");
2const Compilation = @import("compilation.zig").Compilation;
3const llvm = @import("llvm.zig");
4const c = @import("c.zig");
2const mem = std.mem;
3const assert = std.debug.assert;
54const ir = @import("ir.zig");
6const Value = @import("value.zig").Value;
75const Type = @import("type.zig").Type;
8const Scope = @import("scope.zig").Scope;
9const util = @import("util.zig");
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);
6const Value = @import("value.zig").Value;
7const Target = std.Target;
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;
29 defer llvm.DisposeModule(module);
14pub const Symbol = struct {
15 errors: []ErrorMsg,
3016
31 llvm.SetTarget(module, comp.llvm_triple.span());
32 llvm.SetDataLayout(module, comp.target_layout_str);
17 pub fn deinit(self: *Symbol, allocator: *mem.Allocator) void {
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) {
35 llvm.AddModuleCodeViewFlag(module);
36 } else {
37 llvm.AddModuleDebugInfoFlag(module);
26pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !Symbol {
27 switch (typed_value.ty.zigTypeTag()) {
28 .Fn => {
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"),
3856 }
57}
3958
40 const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;
41 defer llvm.DisposeBuilder(builder);
42
43 const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory;
44 defer llvm.DisposeDIBuilder(dibuilder);
45
46 // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes
47 // the git revision.
48 const producer = try std.fmt.allocPrintZ(&code.arena.allocator, "zig {}.{}.{}", .{
49 @as(u32, c.ZIG_VERSION_MAJOR),
50 @as(u32, c.ZIG_VERSION_MINOR),
51 @as(u32, c.ZIG_VERSION_PATCH),
52 });
53 const flags = "";
54 const runtime_version = 0;
55 const compile_unit_file = llvm.CreateFile(
56 dibuilder,
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,
59const Function = struct {
60 module: *const ir.Module,
61 mod_fn: *const ir.Module.Fn,
62 code: *std.ArrayList(u8),
63 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
64 errors: std.ArrayList(ErrorMsg),
65
66 const MCValue = union(enum) {
67 none,
68 unreach,
69 /// A pointer-sized integer that fits in a register.
70 immediate: u64,
71 /// The constant was emitted into the code, at this offset.
72 embedded_in_code: usize,
73 /// The value is in a target-specific register. The value can
74 /// be @intToEnum casted to the respective Reg enum.
75 register: usize,
8276 };
8377
84 try renderToLlvmModule(&ofile, fn_val, code);
85
86 // TODO module level assembly
87 //if (buf_len(&g->global_asm) != 0) {
88 // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));
89 //}
90
91 llvm.DIBuilderFinalize(dibuilder);
92
93 if (comp.verbose_llvm_ir) {
94 std.debug.warn("raw module:\n", .{});
95 llvm.DumpModule(ofile.module);
78 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
79 switch (inst.tag) {
80 .unreach => return self.genPanic(inst.src),
81 .constant => unreachable, // excluded from function bodies
82 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
83 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
84 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
85 }
9686 }
9787
98 // verify the llvm module when safety is on
99 if (std.debug.runtime_safety) {
100 var error_ptr: ?[*:0]u8 = null;
101 _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
88 fn genPanic(self: *Function, src: usize) !MCValue {
89 // TODO change this to call the panic function
90 switch (self.module.target.cpu.arch) {
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;
10297 }
10398
104 const is_small = comp.build_mode == .ReleaseSmall;
105 const is_debug = comp.build_mode == .Debug;
106
107 var err_msg: [*:0]u8 = undefined;
108 // TODO integrate this with evented I/O
109 if (llvm.TargetMachineEmitToFile(
110 comp.target_machine,
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 });
99 fn genRet(self: *Function, src: usize) !void {
100 // TODO change this to call the panic function
101 switch (self.module.target.cpu.arch) {
102 .i386, .x86_64 => {
103 try self.code.append(0xc3); // ret
104 },
105 else => return self.fail(src, "TODO implement ret for {}", .{self.module.target.cpu.arch}),
120106 }
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()});
131107 }
132}
133108
134pub const ObjectFile = struct {
135 comp: *Compilation,
136 module: *llvm.Module,
137 builder: *llvm.Builder,
138 dibuilder: *llvm.DIBuilder,
139 context: *llvm.Context,
140 lock: event.Lock,
141 arena: *std.mem.Allocator,
142
143 fn gpa(self: *ObjectFile) *std.mem.Allocator {
144 return self.comp.gpa();
109 fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void {
110 switch (self.module.target.cpu.arch) {
111 .i386, .x86_64 => {
112 if (amount <= std.math.maxInt(u8)) {
113 try self.code.resize(self.code.items.len + 2);
114 self.code.items[self.code.items.len - 2] = 0xeb;
115 self.code.items[self.code.items.len - 1] = @intCast(u8, amount);
116 } else {
117 try self.code.resize(self.code.items.len + 5);
118 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
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 }
145125 }
146};
147126
148pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
149 // TODO audit more of codegen.cpp:fn_llvm_value and port more logic
150 const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
151 const llvm_fn = llvm.AddFunction(
152 ofile.module,
153 fn_val.symbol_name.span(),
154 llvm_fn_type,
155 ) orelse return error.OutOfMemory;
156
157 const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp);
158 if (want_fn_safety and ofile.comp.haveLibC()) {
159 try addLLVMFnAttr(ofile, llvm_fn, "sspstrong");
160 try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4");
127 fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue {
128 // TODO convert to inline function
129 switch (self.module.target.cpu.arch) {
130 .arm => return self.genAsmArch(.arm, inst),
131 .armeb => return self.genAsmArch(.armeb, inst),
132 .aarch64 => return self.genAsmArch(.aarch64, inst),
133 .aarch64_be => return self.genAsmArch(.aarch64_be, inst),
134 .aarch64_32 => return self.genAsmArch(.aarch64_32, inst),
135 .arc => return self.genAsmArch(.arc, inst),
136 .avr => return self.genAsmArch(.avr, inst),
137 .bpfel => return self.genAsmArch(.bpfel, inst),
138 .bpfeb => return self.genAsmArch(.bpfeb, inst),
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 }
161182 }
162183
163 // TODO
164 //if (fn_val.align_stack) |align_stack| {
165 // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
166 //}
167
168 const fn_type = fn_val.base.typ.cast(Type.Fn).?;
169 const fn_type_normal = &fn_type.key.data.Normal;
170
171 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
172 //add_uwtable_attr(g, fn_table_entry->llvm_value);
173 try addLLVMFnAttr(ofile, llvm_fn, "nobuiltin");
174
175 //if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) {
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);
184 fn genAsmArch(self: *Function, comptime arch: Target.Cpu.Arch, inst: *ir.Inst.Assembly) !MCValue {
185 if (arch != .x86_64 and arch != .i386) {
186 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
187 }
188 for (inst.args.inputs) |input, i| {
189 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
190 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
191 }
192 const reg_name = input[1 .. input.len - 1];
193 const reg = parseRegName(arch, reg_name) orelse
194 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
195 const arg = try self.resolveInst(inst.args.args[i]);
196 try self.genSetReg(inst.base.src, arch, reg, arg);
266197 }
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.
278
279 // create debug variable declarations for parameters
280 // rely on the first variables in the variable_list being parameters.
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 );
199 if (mem.eql(u8, inst.args.asm_source, "syscall")) {
200 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
201 } else {
202 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
302203 }
303204
304 //if (variable->decl_node) {
305 // gen_var_debug_decl(g, variable);
306 //}
205 if (inst.args.output) |output| {
206 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
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 }
307216 }
308217
309 for (code.basic_block_list.span()) |current_block| {
310 llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block);
311 for (current_block.instruction_list.span()) |instruction| {
312 if (instruction.ref_count == 0 and !instruction.hasSideEffects()) continue;
313
314 instruction.llvm_value = try instruction.render(ofile, fn_val);
218 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) !void {
219 switch (arch) {
220 .x86_64 => switch (reg) {
221 .rax => switch (mcv) {
222 .none, .unreach => unreachable,
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", .{}),
315342 }
316 current_block.llvm_exit_block = llvm.GetInsertBlock(ofile.builder);
317343 }
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 {
362 return addLLVMAttr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name);
363}
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),
345 fn genPtrToInt(self: *Function, inst: *ir.Inst.PtrToInt) !MCValue {
346 // no-op
347 return self.resolveInst(inst.args.ptr);
384348 }
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 {
390 return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
391}
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;
350 fn genBitCast(self: *Function, inst: *ir.Inst.BitCast) !MCValue {
351 const operand = try self.resolveInst(inst.args.operand);
352 return operand;
397353 }
398 if (child_type.handleIsPtr()) {
399 return ptr;
354
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 }
400366 }
401 return try renderLoad(ofile, ptr, ptr_type, "");
402}
403367
404pub fn renderStoreUntyped(
405 ofile: *ObjectFile,
406 value: *llvm.Value,
407 ptr: *llvm.Value,
408 alignment: Type.Pointer.Align,
409 vol: Type.Pointer.Vol,
410) !*llvm.Value {
411 const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory;
412 switch (vol) {
413 .Non => {},
414 .Volatile => llvm.SetVolatile(result, 1),
368 fn genTypedValue(self: *Function, src: usize, typed_value: ir.TypedValue) !MCValue {
369 switch (typed_value.ty.zigTypeTag()) {
370 .Pointer => {
371 const ptr_elem_type = typed_value.ty.elemType();
372 switch (ptr_elem_type.zigTypeTag()) {
373 .Array => {
374 // TODO more checks to make sure this can be emitted as a string literal
375 const bytes = try typed_value.val.toAllocatedBytes(self.code.allocator);
376 defer self.code.allocator.free(bytes);
377 const smaller_len = std.math.cast(u32, bytes.len) catch
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 }
415401 }
416 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value)));
417 return result;
418}
419402
420pub fn renderStore(
421 ofile: *ObjectFile,
422 value: *llvm.Value,
423 ptr: *llvm.Value,
424 ptr_type: *Type.Pointer,
425) !*llvm.Value {
426 return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol);
427}
403 fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } {
404 @setCold(true);
405 const msg = try std.fmt.allocPrint(self.errors.allocator, format, args);
406 {
407 errdefer self.errors.allocator.free(msg);
408 (try self.errors.addOne()).* = .{
409 .byte_offset = src,
410 .msg = msg,
411 };
412 }
413 return error.CodegenFail;
414 }
415};
428416
429pub fn renderAlloca(
430 ofile: *ObjectFile,
431 var_type: *Type,
432 name: []const u8,
433 alignment: Type.Pointer.Align,
434) !*llvm.Value {
435 const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
436 const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
437 const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, @ptrCast([*:0]const u8, name_with_null.ptr)) orelse return error.OutOfMemory;
438 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));
439 return result;
417fn Reg(comptime arch: Target.Cpu.Arch) type {
418 return switch (arch) {
419 .i386 => enum {
420 eax,
421 ebx,
422 ecx,
423 edx,
424 ebp,
425 esp,
426 esi,
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 };
440520}
441521
442pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: *llvm.Type) u32 {
443 return switch (alignment) {
444 .Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type),
445 .Override => |a| a,
446 };
522fn parseRegName(comptime arch: Target.Cpu.Arch, name: []const u8) ?Reg(arch) {
523 return std.meta.stringToEnum(Reg(arch), name);
447524}
src-self-hosted/ir.zig+61-19
......@@ -24,6 +24,7 @@ pub const Inst = struct {
2424 constant,
2525 assembly,
2626 ptrtoint,
27 bitcast,
2728 };
2829
2930 pub fn cast(base: *Inst, comptime T: type) ?*T {
......@@ -45,6 +46,7 @@ pub const Inst = struct {
4546
4647 .assembly,
4748 .ptrtoint,
49 .bitcast,
4850 => null,
4951 };
5052 }
......@@ -84,6 +86,15 @@ pub const Inst = struct {
8486 ptr: *Inst,
8587 },
8688 };
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 };
8798};
8899
89100pub const TypedValue = struct {
......@@ -96,6 +107,7 @@ pub const Module = struct {
96107 errors: []ErrorMsg,
97108 arena: std.heap.ArenaAllocator,
98109 fns: []Fn,
110 target: Target,
99111
100112 pub const Export = struct {
101113 name: []const u8,
......@@ -122,9 +134,7 @@ pub const ErrorMsg = struct {
122134 msg: []const u8,
123135};
124136
125pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
126 const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
127
137pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !Module {
128138 var ctx = Analyze{
129139 .allocator = allocator,
130140 .arena = std.heap.ArenaAllocator.init(allocator),
......@@ -133,7 +143,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
133143 .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator),
134144 .exports = std.ArrayList(Module.Export).init(allocator),
135145 .fns = std.ArrayList(Module.Fn).init(allocator),
136 .target = native_info.target,
146 .target = target,
137147 };
138148 defer ctx.errors.deinit();
139149 defer ctx.decl_table.deinit();
......@@ -152,6 +162,7 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module) !Module {
152162 .errors = ctx.errors.toOwnedSlice(),
153163 .fns = ctx.fns.toOwnedSlice(),
154164 .arena = ctx.arena,
165 .target = target,
155166 };
156167}
157168
......@@ -234,7 +245,7 @@ const Analyze = struct {
234245 fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 {
235246 const new_inst = try self.resolveInst(func, old_inst);
236247 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);
238249 const val = try self.resolveConstValue(coerced_inst);
239250 return val.toAllocatedBytes(&self.arena.allocator);
240251 }
......@@ -242,7 +253,7 @@ const Analyze = struct {
242253 fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type {
243254 const new_inst = try self.resolveInst(func, old_inst);
244255 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);
246257 const val = try self.resolveConstValue(coerced_inst);
247258 return val.toType();
248259 }
......@@ -409,6 +420,7 @@ const Analyze = struct {
409420 .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?),
410421 .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?),
411422 .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?),
423 .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?),
412424 }
413425 }
414426
......@@ -472,7 +484,7 @@ const Analyze = struct {
472484 fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst {
473485 const dest_type = try self.resolveType(func, as.positionals.dest_type);
474486 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);
476488 }
477489
478490 fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst {
......@@ -545,12 +557,18 @@ const Analyze = struct {
545557 }
546558
547559 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);
549561 }
550562
551563 return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{});
552564 }
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
554572 fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst {
555573 const ptr = try self.resolveInst(func, deref.positionals.ptr);
556574 const elem_ty = switch (ptr.ty.zigTypeTag()) {
......@@ -583,7 +601,8 @@ const Analyze = struct {
583601 elem.* = try self.resolveConstString(func, assembly.kw_args.clobbers[i]);
584602 }
585603 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);
587606 }
588607
589608 const f = try self.requireFunctionBody(func, assembly.base.src);
......@@ -602,10 +621,14 @@ const Analyze = struct {
602621 return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {});
603622 }
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
606629 const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty);
607630 if (in_memory_result == .ok) {
608 return self.bitcast(dest_type, inst);
631 return self.bitcast(func, dest_type, inst);
609632 }
610633
611634 // *[N]T to []T
......@@ -634,12 +657,14 @@ const Analyze = struct {
634657 return self.fail(inst.src, "TODO implement type coercion", .{});
635658 }
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 {
638661 if (inst.value()) |val| {
639662 // Keep the comptime Value representation; take the new type.
640663 return self.constInst(inst.src, .{ .ty = dest_type, .val = val });
641664 }
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 });
643668 }
644669
645670 fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst {
......@@ -699,7 +724,9 @@ pub fn main() anyerror!void {
699724 std.process.exit(1);
700725 }
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);
703730 defer analyzed_module.deinit(allocator);
704731
705732 if (analyzed_module.errors.len != 0) {
......@@ -711,12 +738,27 @@ pub fn main() anyerror!void {
711738 std.process.exit(1);
712739 }
713740
714 var new_zir_module = try text.emit_zir(allocator, analyzed_module);
715 defer new_zir_module.deinit(allocator);
741 const output_zir = true;
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());
718 try new_zir_module.writeToStream(allocator, bos.outStream());
719 try bos.flush();
746 var bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
747 try new_zir_module.writeToStream(allocator, bos.outStream());
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 }
720762}
721763
722764fn 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 {
3131 primitive,
3232 fntype,
3333 intcast,
34 bitcast,
3435 };
3536
3637 pub fn TagToType(tag: Tag) type {
......@@ -48,6 +49,7 @@ pub const Inst = struct {
4849 .primitive => Primitive,
4950 .fntype => FnType,
5051 .intcast => IntCast,
52 .bitcast => BitCast,
5153 };
5254 }
5355
......@@ -258,6 +260,17 @@ pub const Inst = struct {
258260 },
259261 kw_args: struct {},
260262 };
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 };
261274};
262275
263276pub const ErrorMsg = struct {
......@@ -331,6 +344,7 @@ pub const Module = struct {
331344 .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table),
332345 .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table),
333346 .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table),
347 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table),
334348 }
335349 }
336350
......@@ -957,6 +971,19 @@ const EmitZIR = struct {
957971 };
958972 break :blk &new_inst.base;
959973 },
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 },
960987 };
961988 try instructions.append(new_inst);
962989 try inst_table.putNoClobber(inst, new_inst);
src-self-hosted/link.zig+748-500
......@@ -1,576 +1,824 @@
11const std = @import("std");
22const 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;
83const assert = std.debug.assert;
9const util = @import("util.zig");
10
11const Context = struct {
12 comp: *Compilation,
13 arena: std.heap.ArenaAllocator,
14 args: std.ArrayList([*:0]const u8),
15 link_in_crt: bool,
4const Allocator = std.mem.Allocator;
5const ir = @import("ir.zig");
6const fs = std.fs;
7const elf = std.elf;
8const codegen = @import("codegen.zig");
9
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,
18 link_msg: std.ArrayListSentineled(u8, 0),
22pub const Result = struct {
23 errors: []ErrorMsg,
1924
20 libc: *LibCInstallation,
21 out_file_path: std.ArrayListSentineled(u8, 0),
25 pub fn deinit(self: *Result, allocator: *mem.Allocator) void {
26 for (self.errors) |err| {
27 allocator.free(err.msg);
28 }
29 allocator.free(self.errors);
30 self.* = undefined;
31 }
2232};
2333
24pub fn link(comp: *Compilation) !void {
25 var ctx = Context{
26 .comp = comp,
27 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
28 .args = undefined,
29 .link_in_crt = comp.haveLibC() and comp.kind == .Exe,
30 .link_err = {},
31 .link_msg = undefined,
32 .libc = undefined,
33 .out_file_path = undefined,
34 };
35 defer ctx.arena.deinit();
36 ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator);
37 ctx.link_msg = std.ArrayListSentineled(u8, 0).initNull(&ctx.arena.allocator);
38
39 ctx.out_file_path = try std.ArrayListSentineled(u8, 0).init(&ctx.arena.allocator, comp.name.span());
40 switch (comp.kind) {
41 .Exe => {
42 try ctx.out_file_path.append(comp.target.exeFileExt());
43 },
44 .Lib => {
45 try ctx.out_file_path.append(if (comp.is_static) comp.target.staticLibSuffix() else comp.target.dynamicLibSuffix());
46 },
47 .Obj => {
48 try ctx.out_file_path.append(comp.target.oFileExt());
34/// Attempts incremental linking, if the file already exists.
35/// If incremental linking fails, falls back to truncating the file and rewriting it.
36/// A malicious file is detected as incremental link failure and does not cause Illegal Behavior.
37/// This operation is not atomic.
38pub fn updateExecutableFilePath(
39 allocator: *Allocator,
40 module: ir.Module,
41 dir: fs.Dir,
42 sub_path: []const u8,
43) !Result {
44 const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode });
45 defer file.close();
46
47 return updateExecutableFile(allocator, module, file);
48}
49
50/// Atomically overwrites the old file, if present.
51pub fn writeExecutableFilePath(
52 allocator: *Allocator,
53 module: ir.Module,
54 dir: fs.Dir,
55 sub_path: []const u8,
56) !Result {
57 const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode });
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);
4974 },
50 }
75 else => |e| return e,
76 };
77}
5178
52 // even though we're calling LLD as a library it thinks the first
53 // argument is its own exe name
54 try ctx.args.append("lld");
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 }
79const Update = struct {
80 file: fs.File,
81 module: *const ir.Module,
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) {
73 for (ctx.args.span()) |arg, i| {
74 const space = if (i == 0) "" else " ";
75 std.debug.warn("{}{s}", .{ space, arg });
76 }
77 std.debug.warn("\n", .{});
88 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
89 /// Same order as in the file.
90 program_headers: std.ArrayList(elf.Elf64_Phdr),
91 phdr_table_offset: ?u64,
92 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
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();
78113 }
79114
80 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
81 const args_slice = ctx.args.span();
82
83 {
84 // LLD is not thread-safe, so we grab a global lock.
85 const held = comp.zig_compiler.lld_lock.acquire();
86 defer held.release();
87
88 // Not evented I/O. LLD does its own multithreading internally.
89 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
90 if (!ctx.link_msg.isNull()) {
91 // TODO capture these messages and pass them through the system, reporting them through the
92 // event system instead of printing them directly here.
93 // perhaps try to parse and understand them.
94 std.debug.warn("{}\n", .{ctx.link_msg.span()});
115 // `expand_num / expand_den` is the factor of padding when allocation
116 const alloc_num = 4;
117 const alloc_den = 3;
118
119 /// Returns end pos of collision, if any.
120 fn detectAllocCollision(self: *Update, start: u64, size: u64) ?u64 {
121 const small_ptr = self.module.target.cpu.arch.ptrBitWidth() == 32;
122 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
123 if (start < ehdr_size)
124 return ehdr_size;
125
126 const end = start + satMul(size, alloc_num) / alloc_den;
127
128 if (self.shdr_table_offset) |off| {
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;
95135 }
96 return error.LinkFailed;
97136 }
98 }
99}
100137
101extern fn ZigLLDLink(
102 oformat: c.ZigLLVM_ObjectFormatType,
103 args: [*]const [*]const u8,
104 arg_count: usize,
105 append_diagnostic: extern fn (*c_void, [*]const u8, usize) void,
106 context: *c_void,
107) bool;
108
109fn linkDiagCallback(context: *c_void, ptr: [*]const u8, len: usize) callconv(.C) void {
110 const ctx = @ptrCast(*Context, @alignCast(@alignOf(Context), context));
111 ctx.link_err = linkDiagCallbackErrorable(ctx, ptr[0..len]);
112}
138 if (self.phdr_table_offset) |off| {
139 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
140 const tight_size = self.sections.items.len * phdr_size;
141 const increased_size = satMul(tight_size, alloc_num) / alloc_den;
142 const test_end = off + increased_size;
143 if (end > off and start < test_end) {
144 return test_end;
145 }
146 }
113147
114fn linkDiagCallbackErrorable(ctx: *Context, msg: []const u8) !void {
115 if (ctx.link_msg.isNull()) {
116 try ctx.link_msg.resize(0);
148 for (self.sections.items) |section| {
149 const increased_size = satMul(section.sh_size, alloc_num) / alloc_den;
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;
117163 }
118 try ctx.link_msg.append(msg);
119}
120164
121fn toExternObjectFormatType(ofmt: ObjectFormat) c.ZigLLVM_ObjectFormatType {
122 return switch (ofmt) {
123 .unknown => .ZigLLVM_UnknownObjectFormat,
124 .coff => .ZigLLVM_COFF,
125 .elf => .ZigLLVM_ELF,
126 .macho => .ZigLLVM_MachO,
127 .wasm => .ZigLLVM_Wasm,
128 };
129}
130
131fn constructLinkerArgs(ctx: *Context) !void {
132 switch (ctx.comp.target.getObjectFormat()) {
133 .unknown => unreachable,
134 .coff => return constructLinkerArgsCoff(ctx),
135 .elf => return constructLinkerArgsElf(ctx),
136 .macho => return constructLinkerArgsMachO(ctx),
137 .wasm => return constructLinkerArgsWasm(ctx),
165 fn allocatedSize(self: *Update, start: u64) u64 {
166 var min_pos: u64 = std.math.maxInt(u64);
167 if (self.shdr_table_offset) |off| {
168 if (off > start and off < min_pos) min_pos = off;
169 }
170 if (self.phdr_table_offset) |off| {
171 if (off > start and off < min_pos) min_pos = off;
172 }
173 for (self.sections.items) |section| {
174 if (section.sh_offset <= start) continue;
175 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
176 }
177 for (self.program_headers.items) |program_header| {
178 if (program_header.p_offset <= start) continue;
179 if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
180 }
181 return min_pos - start;
138182 }
139}
140183
141fn constructLinkerArgsElf(ctx: *Context) !void {
142 // TODO commented out code in this function
143 //if (g->linker_script) {
144 // lj->args.append("-T");
145 // lj->args.append(g->linker_script);
146 //}
147 try ctx.args.append("--gc-sections");
148 if (ctx.comp.link_eh_frame_hdr) {
149 try ctx.args.append("--eh-frame-hdr");
184 fn findFreeSpace(self: *Update, object_size: u64, min_alignment: u16) u64 {
185 var start: u64 = 0;
186 while (self.detectAllocCollision(start, object_size)) |item_end| {
187 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
188 }
189 return start;
150190 }
151191
152 //lj->args.append("-m");
153 //lj->args.append(getLDMOption(&g->zig_target));
154
155 //bool is_lib = g->out_type == OutTypeLib;
156 //bool shared = !g->is_static && is_lib;
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");
192 fn makeString(self: *Update, bytes: []const u8) !u32 {
193 const result = self.shstrtab.items.len;
194 try self.shstrtab.appendSlice(bytes);
195 try self.shstrtab.append(0);
196 return @intCast(u32, result);
182197 }
183198
184 if (ctx.comp.haveLibC()) {
185 try ctx.args.append("-L");
186 // TODO addNullByte should probably return [:0]u8
187 try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.crt_dir.?)).ptr));
188
189 //if (!ctx.comp.is_static) {
190 // const dl = blk: {
191 // //if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
192 // //if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl;
193 // return error.LibCMissingDynamicLinker;
194 // };
195 // try ctx.args.append("-dynamic-linker");
196 // try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
197 //}
198 }
199 fn perform(self: *Update) !void {
200 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
201 32 => .p32,
202 64 => .p64,
203 else => return error.UnsupportedArchitecture,
204 };
205 const small_ptr = switch (ptr_width) {
206 .p32 => true,
207 .p64 => false,
208 };
209 // This means the entire read-only executable program code needs to be rewritten.
210 var phdr_load_re_dirty = false;
211 var phdr_table_dirty = false;
212 var shdr_table_dirty = false;
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) {
201 // lj->args.append("-soname");
202 // lj->args.append(buf_ptr(soname));
203 //}
341 try self.writeCodeAndSymbols(phdr_table_dirty, shdr_table_dirty);
204342
205 // .o files
206 for (ctx.comp.link_objects) |link_object| {
207 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
208 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
209 }
210 try addFnObjects(ctx);
211
212 //if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
213 // if (g->libc_link_lib == nullptr) {
214 // Buf *builtin_o_path = build_o(g, "builtin");
215 // lj->args.append(buf_ptr(builtin_o_path));
216 // }
217
218 // // sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
219 // Buf *compiler_rt_o_path = build_compiler_rt(g);
220 // lj->args.append(buf_ptr(compiler_rt_o_path));
221 //}
222
223 //for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
224 // LinkLib *link_lib = g->link_libs_list.at(i);
225 // if (buf_eql_str(link_lib->name, "c")) {
226 // continue;
227 // }
228 // Buf *arg;
229 // if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") ||
230 // buf_ends_with_str(link_lib->name, ".so"))
231 // {
232 // arg = link_lib->name;
233 // } else {
234 // arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
235 // }
236 // lj->args.append(buf_ptr(arg));
237 //}
238
239 // libc dep
240 if (ctx.comp.haveLibC()) {
241 if (ctx.comp.is_static) {
242 try ctx.args.append("--start-group");
243 try ctx.args.append("-lgcc");
244 try ctx.args.append("-lgcc_eh");
245 try ctx.args.append("-lc");
246 try ctx.args.append("-lm");
247 try ctx.args.append("--end-group");
343 if (phdr_table_dirty) {
344 const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
345 const needed_size = self.program_headers.items.len * phsize;
346
347 if (needed_size > allocated_size) {
348 self.phdr_table_offset = null; // free the space
349 self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
350 }
351
352 const allocator = self.program_headers.allocator;
353 switch (ptr_width) {
354 .p32 => {
355 const buf = try allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
356 defer allocator.free(buf);
357
358 for (buf) |*phdr, i| {
359 phdr.* = progHeaderTo32(self.program_headers.items[i]);
360 if (foreign_endian) {
361 bswapAllFields(elf.Elf32_Phdr, phdr);
362 }
363 }
364 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
365 },
366 .p64 => {
367 const buf = try allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
368 defer allocator.free(buf);
369
370 for (buf) |*phdr, i| {
371 phdr.* = self.program_headers.items[i];
372 if (foreign_endian) {
373 bswapAllFields(elf.Elf64_Phdr, phdr);
374 }
375 }
376 try self.file.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
377 },
378 }
379 }
380
381 {
382 const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
383 if (shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
384 const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
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 });
248446 } else {
249 try ctx.args.append("-lgcc");
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");
447 try self.writeElfHeader();
259448 }
449 // TODO find end pos and truncate
260450 }
261451
262 // crt end
263 if (ctx.link_in_crt) {
264 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crtn.o");
265 }
452 fn writeElfHeader(self: *Update) !void {
453 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
266454
267 //if (ctx.comp.target != Target.Native) {
268 // try ctx.args.append("--allow-shlib-undefined");
269 //}
270}
455 var index: usize = 0;
456 hdr_buf[0..4].* = "\x7fELF".*;
457 index += 4;
271458
272fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
273 const full_path = try std.fs.path.join(&ctx.arena.allocator, &[_][]const u8{ dirname, basename });
274 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
275 try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr));
276}
459 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
460 32 => .p32,
461 64 => .p64,
462 else => return error.UnsupportedArchitecture,
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 {
279 try ctx.args.append("-NOLOGO");
470 const endian = self.module.target.cpu.arch.endian();
471 hdr_buf[index] = switch (endian) {
472 .Little => elf.ELFDATA2LSB,
473 .Big => elf.ELFDATA2MSB,
474 };
475 index += 1;
280476
281 if (!ctx.comp.strip) {
282 try ctx.args.append("-DEBUG");
283 }
477 hdr_buf[index] = 1; // ELF version
478 index += 1;
284479
285 switch (ctx.comp.target.cpu.arch) {
286 .i386 => try ctx.args.append("-MACHINE:X86"),
287 .x86_64 => try ctx.args.append("-MACHINE:X64"),
288 .aarch64 => try ctx.args.append("-MACHINE:ARM"),
289 else => return error.UnsupportedLinkArchitecture,
290 }
480 // OS ABI, often set to 0 regardless of target platform
481 // ABI Version, possibly used by glibc but not by static executables
482 // padding
483 mem.set(u8, hdr_buf[index..][0..9], 0);
484 index += 9;
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()});
295 try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr));
488 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian);
489 index += 2;
296490
297 if (ctx.comp.haveLibC()) {
298 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr));
299 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr));
300 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.crt_dir.?})).ptr));
301 }
491 const machine = self.module.target.cpu.arch.toElfMachine();
492 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
493 index += 2;
302494
303 if (ctx.link_in_crt) {
304 const lib_str = if (ctx.comp.is_static) "lib" else "";
305 const d_str = if (ctx.comp.build_mode == .Debug) "d" else "";
495 // ELF Version, again
496 mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
497 index += 4;
306498
307 if (ctx.comp.is_static) {
308 const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", .{d_str});
309 try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr));
310 } else {
311 const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", .{d_str});
312 try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr));
313 }
499 switch (ptr_width) {
500 .p32 => {
501 // e_entry
502 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian);
503 index += 4;
504
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", .{
316 lib_str,
317 d_str,
318 });
319 try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr));
320
321 const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", .{ lib_str, d_str });
322 try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr));
323
324 // Visual C++ 2015 Conformance Changes
325 // https://msdn.microsoft.com/en-us/library/bb531344.aspx
326 try ctx.args.append("legacy_stdio_definitions.lib");
327
328 // msvcrt depends on kernel32
329 try ctx.args.append("kernel32.lib");
330 } else {
331 try ctx.args.append("-NODEFAULTLIB");
332 if (!is_library) {
333 try ctx.args.append("-ENTRY:WinMainCRTStartup");
509 // e_shoff
510 mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
511 index += 4;
512 },
513 .p64 => {
514 // e_entry
515 mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian);
516 index += 8;
517
518 // e_phoff
519 mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
520 index += 8;
521
522 // e_shoff
523 mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
524 index += 8;
525 },
334526 }
335 }
336527
337 if (is_library and !ctx.comp.is_static) {
338 try ctx.args.append("-DLL");
339 }
528 const e_flags = 0;
529 mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
530 index += 4;
340531
341 for (ctx.comp.link_objects) |link_object| {
342 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
343 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
344 }
345 try addFnObjects(ctx);
532 const e_ehsize: u16 = switch (ptr_width) {
533 .p32 => @sizeOf(elf.Elf32_Ehdr),
534 .p64 => @sizeOf(elf.Elf64_Ehdr),
535 };
536 mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
537 index += 2;
346538
347 switch (ctx.comp.kind) {
348 .Exe, .Lib => {
349 if (!ctx.comp.haveLibC()) {
350 @panic("TODO");
351 }
352 },
353 .Obj => {},
354 }
355}
539 const e_phentsize: u16 = switch (ptr_width) {
540 .p32 => @sizeOf(elf.Elf32_Phdr),
541 .p64 => @sizeOf(elf.Elf64_Phdr),
542 };
543 mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
544 index += 2;
356545
357fn constructLinkerArgsMachO(ctx: *Context) !void {
358 try ctx.args.append("-demangle");
546 const e_phnum = @intCast(u16, self.program_headers.items.len);
547 mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
548 index += 2;
359549
360 if (ctx.comp.linker_rdynamic) {
361 try ctx.args.append("-export_dynamic");
362 }
550 const e_shentsize: u16 = switch (ptr_width) {
551 .p32 => @sizeOf(elf.Elf32_Shdr),
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;
365 const shared = !ctx.comp.is_static and is_lib;
366 if (ctx.comp.is_static) {
367 try ctx.args.append("-static");
368 } else {
369 try ctx.args.append("-dynamic");
370 }
557 const e_shnum = @intCast(u16, self.sections.items.len);
558 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
559 index += 2;
371560
372 try ctx.args.append("-arch");
373 try ctx.args.append(util.getDarwinArchString(ctx.comp.target));
561 mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
562 index += 2;
374563
375 const platform = try DarwinPlatform.get(ctx.comp);
376 switch (platform.kind) {
377 .MacOS => try ctx.args.append("-macosx_version_min"),
378 .IPhoneOS => try ctx.args.append("-iphoneos_version_min"),
379 .IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"),
564 assert(index == e_ehsize);
565
566 try self.file.pwriteAll(hdr_buf[0..index], 0);
380567 }
381 const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", .{
382 platform.major,
383 platform.minor,
384 platform.micro,
385 });
386 try ctx.args.append(@ptrCast([*:0]const u8, ver_str.ptr));
387
388 if (ctx.comp.kind == .Exe) {
389 if (ctx.comp.is_static) {
390 try ctx.args.append("-no_pie");
391 } else {
392 try ctx.args.append("-pie");
568
569 fn writeCodeAndSymbols(self: *Update, phdr_table_dirty: bool, shdr_table_dirty: bool) !void {
570 // index 0 is always a null symbol
571 try self.symbols.resize(1);
572 self.symbols.items[0] = .{
573 .st_name = 0,
574 .st_info = 0,
575 .st_other = 0,
576 .st_shndx = 0,
577 .st_value = 0,
578 .st_size = 0,
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 }
393635 }
636
637 return self.writeSymbols();
394638 }
395639
396 try ctx.args.append("-o");
397 try ctx.args.append(ctx.out_file_path.span());
398
399 if (shared) {
400 try ctx.args.append("-headerpad_max_install_names");
401 } else if (ctx.comp.is_static) {
402 try ctx.args.append("-lcrt0.o");
403 } else {
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");
640 fn writeProgHeader(self: *Update, index: usize) !void {
641 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
642 const offset = self.program_headers.items[index].p_offset;
643 switch (self.module.target.cpu.arch.ptrBitWidth()) {
644 32 => {
645 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
646 if (foreign_endian) {
647 bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
412648 }
649 return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset);
413650 },
414 .IPhoneOS => {
415 if (ctx.comp.target.cpu.arch == .aarch64) {
416 // iOS does not need any crt1 files for arm64
417 } else if (platform.versionLessThan(3, 1)) {
418 try ctx.args.append("-lcrt1.o");
419 } else if (platform.versionLessThan(6, 0)) {
420 try ctx.args.append("-lcrt1.3.1.o");
651 64 => {
652 var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
653 if (foreign_endian) {
654 bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
421655 }
656 return self.file.pwriteAll(mem.sliceAsBytes(&phdr), offset);
422657 },
423 .IPhoneOSSimulator => {}, // no crt1.o needed
658 else => return error.UnsupportedArchitecture,
424659 }
425660 }
426661
427 for (ctx.comp.link_objects) |link_object| {
428 const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
429 try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
430 }
431 try addFnObjects(ctx);
432
433 // TODO
434 //if (ctx.comp.target == Target.Native) {
435 // for (ctx.comp.link_libs_list.span()) |lib| {
436 // if (mem.eql(u8, lib.name, "c")) {
437 // // on Darwin, libSystem has libc in it, but also you have to use it
438 // // to make syscalls because the syscall numbers are not documented
439 // // and change between versions.
440 // // so we always link against libSystem
441 // try ctx.args.append("-lSystem");
442 // } else {
443 // if (mem.indexOfScalar(u8, lib.name, '/') == null) {
444 // const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name});
445 // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
446 // } else {
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");
662 fn writeSectHeader(self: *Update, index: usize) !void {
663 const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
664 const offset = self.sections.items[index].sh_offset;
665 switch (self.module.target.cpu.arch.ptrBitWidth()) {
666 32 => {
667 var shdr: [1]elf.Elf32_Shdr = undefined;
668 shdr[0] = sectHeaderTo32(self.sections.items[index]);
669 if (foreign_endian) {
670 bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
671 }
672 return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset);
673 },
674 64 => {
675 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
676 if (foreign_endian) {
677 bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
678 }
679 return self.file.pwriteAll(mem.sliceAsBytes(&shdr), offset);
680 },
681 else => return error.UnsupportedArchitecture,
462682 }
463 } else {
464 @panic("TODO");
465683 }
466}
467
468fn constructLinkerArgsWasm(ctx: *Context) void {
469 @panic("TODO");
470}
471684
472fn addFnObjects(ctx: *Context) !void {
473 const held = ctx.comp.fn_link_set.acquire();
474 defer held.release();
475
476 var it = held.value.first;
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;
685 fn writeSymbols(self: *Update) !void {
686 const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) {
687 32 => .p32,
688 64 => .p64,
689 else => return error.UnsupportedArchitecture,
484690 };
485 try ctx.args.append(fn_val.containing_object.span());
486 it = node.next;
691 const small_ptr = ptr_width == .p32;
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 }
487748 }
488}
749};
489750
490const DarwinPlatform = struct {
491 kind: Kind,
492 major: u32,
493 minor: u32,
494 micro: u32,
751/// Truncates the existing file contents and overwrites the contents.
752/// Returns an error if `file` is not already open with +read +write +seek abilities.
753pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
754 var update = Update{
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 {
497 MacOS,
498 IPhoneOS,
499 IPhoneOSSimulator,
774 try update.perform();
775 return Result{
776 .errors = update.errors.toOwnedSlice(),
500777 };
778}
501779
502 fn get(comp: *Compilation) !DarwinPlatform {
503 var result: DarwinPlatform = undefined;
504 const ver_str = switch (comp.darwin_version_min) {
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 };
780/// Returns error.IncrFailed if incremental update could not be performed.
781fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result {
782 //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
519783
520 var had_extra: bool = undefined;
521 try darwinGetReleaseVersion(
522 ver_str,
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 }
784 // TODO implement incremental linking
785 return error.IncrFailed;
786}
531787
532 if (result.kind == .IPhoneOS) {
533 switch (comp.target.cpu.arch) {
534 .i386,
535 .x86_64,
536 => result.kind = .IPhoneOSSimulator,
537 else => {},
538 }
539 }
540 return result;
541 }
788/// Saturating multiplication
789fn satMul(a: var, b: var) @TypeOf(a, b) {
790 const T = @TypeOf(a, b);
791 return std.math.mul(T, a, b) catch std.math.maxInt(T);
792}
542793
543 fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {
544 if (self.major < major)
545 return true;
546 if (self.major > major)
547 return false;
548 if (self.minor < minor)
549 return true;
550 return false;
551 }
552};
794fn bswapAllFields(comptime S: type, ptr: *S) void {
795 @panic("TODO implement bswapAllFields");
796}
553797
554/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
555/// grouped values as integers. Numbers which are not provided are set to 0.
556/// return true if the entire string was parsed (9.2), or all groups were
557/// parsed (10.3.5extrastuff).
558fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {
559 major.* = 0;
560 minor.* = 0;
561 micro.* = 0;
562 had_extra.* = false;
563
564 if (str.len == 0)
565 return error.InvalidDarwinVersionString;
566
567 var start_pos: usize = 0;
568 for ([_]*u32{ major, minor, micro }) |v| {
569 const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
570 const end_pos = dot_pos orelse str.len;
571 v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
572 start_pos = (dot_pos orelse return) + 1;
573 if (start_pos == str.len) return;
574 }
575 had_extra.* = true;
798fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
799 return .{
800 .p_type = phdr.p_type,
801 .p_flags = phdr.p_flags,
802 .p_offset = @intCast(u32, phdr.p_offset),
803 .p_vaddr = @intCast(u32, phdr.p_vaddr),
804 .p_paddr = @intCast(u32, phdr.p_paddr),
805 .p_filesz = @intCast(u32, phdr.p_filesz),
806 .p_memsz = @intCast(u32, phdr.p_memsz),
807 .p_align = @intCast(u32, phdr.p_align),
808 };
809}
810
811fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
812 return .{
813 .sh_name = shdr.sh_name,
814 .sh_type = shdr.sh_type,
815 .sh_flags = @intCast(u32, shdr.sh_flags),
816 .sh_addr = @intCast(u32, shdr.sh_addr),
817 .sh_offset = @intCast(u32, shdr.sh_offset),
818 .sh_size = @intCast(u32, shdr.sh_size),
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 };
576824}
src-self-hosted/value.zig+50
......@@ -264,6 +264,56 @@ pub const Value = extern union {
264264 }
265265 }
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
267317 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
268318 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
269319 switch (self.tag()) {