| author | |
| committer | |
| log | 24a01eed90b60a1e57172ae5a5305bc437bfeaba |
| tree | e7670e50bd61bcdeed198ec6f95b6ecdec33308d |
| parent | a3dfe36ca1dac946f507c8b69241a93891bf7da5 |
4 files changed, 357 insertions(+), 445 deletions(-)
src-self-hosted/codegen.zig+111-421| ... | @@ -1,447 +1,137 @@ | ... | @@ -1,447 +1,137 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const Compilation = @import("compilation.zig").Compilation; | 2 | const mem = std.mem; |
| 3 | const llvm = @import("llvm.zig"); | 3 | const assert = std.debug.assert; |
| 4 | const c = @import("c.zig"); | ||
| 5 | const ir = @import("ir.zig"); | 4 | const ir = @import("ir.zig"); |
| 6 | const Value = @import("value.zig").Value; | ||
| 7 | const Type = @import("type.zig").Type; | 5 | const Type = @import("type.zig").Type; |
| 8 | const Scope = @import("scope.zig").Scope; | 6 | const Value = @import("value.zig").Value; |
| 9 | const util = @import("util.zig"); | ||
| 10 | const event = std.event; | ||
| 11 | const assert = std.debug.assert; | ||
| 12 | const DW = std.dwarf; | ||
| 13 | const maxInt = std.math.maxInt; | ||
| 14 | |||
| 15 | pub 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); | ||
| 25 | |||
| 26 | const context = llvm_handle.node.data; | ||
| 27 | |||
| 28 | const module = llvm.ModuleCreateWithNameInContext(comp.name.span(), context) orelse return error.OutOfMemory; | ||
| 29 | defer llvm.DisposeModule(module); | ||
| 30 | |||
| 31 | llvm.SetTarget(module, comp.llvm_triple.span()); | ||
| 32 | llvm.SetDataLayout(module, comp.target_layout_str); | ||
| 33 | |||
| 34 | if (comp.target.getObjectFormat() == .coff) { | ||
| 35 | llvm.AddModuleCodeViewFlag(module); | ||
| 36 | } else { | ||
| 37 | llvm.AddModuleDebugInfoFlag(module); | ||
| 38 | } | ||
| 39 | |||
| 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, | ||
| 82 | }; | ||
| 83 | |||
| 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); | ||
| 96 | } | ||
| 97 | 7 | ||
| 98 | // verify the llvm module when safety is on | 8 | pub const ErrorMsg = struct { |
| 99 | if (std.debug.runtime_safety) { | 9 | byte_offset: usize, |
| 100 | var error_ptr: ?[*:0]u8 = null; | 10 | msg: []const u8, |
| 101 | _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); | 11 | }; |
| 102 | } | ||
| 103 | 12 | ||
| 104 | const is_small = comp.build_mode == .ReleaseSmall; | 13 | pub const Symbol = struct { |
| 105 | const is_debug = comp.build_mode == .Debug; | 14 | errors: []ErrorMsg, |
| 106 | 15 | ||
| 107 | var err_msg: [*:0]u8 = undefined; | 16 | pub fn deinit(self: *Symbol, allocator: *mem.Allocator) void { |
| 108 | // TODO integrate this with evented I/O | 17 | for (self.errors) |err| { |
| 109 | if (llvm.TargetMachineEmitToFile( | 18 | allocator.free(err.msg); |
| 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 }); | ||
| 120 | } | 19 | } |
| 121 | return error.WritingObjectFileFailed; | 20 | allocator.free(self.errors); |
| 122 | } | 21 | self.* = undefined; |
| 123 | //validate_inline_fns(g); TODO | ||
| 124 | fn_val.containing_object = output_path; | ||
| 125 | if (comp.verbose_llvm_ir) { | ||
| 126 | std.debug.warn("optimized module:\n", .{}); | ||
| 127 | llvm.DumpModule(ofile.module); | ||
| 128 | } | ||
| 129 | if (comp.verbose_link) { | ||
| 130 | std.debug.warn("created {}\n", .{output_path.span()}); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | pub 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(); | ||
| 145 | } | 22 | } |
| 146 | }; | 23 | }; |
| 147 | 24 | ||
| 148 | pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void { | 25 | pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !Symbol { |
| 149 | // TODO audit more of codegen.cpp:fn_llvm_value and port more logic | 26 | switch (typed_value.ty.zigTypeTag()) { |
| 150 | const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context); | 27 | .Fn => { |
| 151 | const llvm_fn = llvm.AddFunction( | 28 | const index = typed_value.val.cast(Value.Payload.Function).?.index; |
| 152 | ofile.module, | 29 | const module_fn = module.fns[index]; |
| 153 | fn_val.symbol_name.span(), | 30 | |
| 154 | llvm_fn_type, | 31 | var function = Function{ |
| 155 | ) orelse return error.OutOfMemory; | 32 | .module = &module, |
| 156 | 33 | .mod_fn = &module_fn, | |
| 157 | const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp); | 34 | .code = code, |
| 158 | if (want_fn_safety and ofile.comp.haveLibC()) { | 35 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator), |
| 159 | try addLLVMFnAttr(ofile, llvm_fn, "sspstrong"); | 36 | .errors = std.ArrayList(ErrorMsg).init(code.allocator), |
| 160 | try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4"); | 37 | .constants = std.ArrayList(ir.TypedValue).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 | return Symbol{ .errors = function.errors.toOwnedSlice() }; | ||
| 53 | }, | ||
| 54 | else => @panic("TODO implement generateSymbol for non-function types"), | ||
| 161 | } | 55 | } |
| 56 | } | ||
| 162 | 57 | ||
| 163 | // TODO | 58 | const Function = struct { |
| 164 | //if (fn_val.align_stack) |align_stack| { | 59 | module: *const ir.Module, |
| 165 | // try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack); | 60 | mod_fn: *const ir.Module.Fn, |
| 166 | //} | 61 | code: *std.ArrayList(u8), |
| 167 | 62 | inst_table: std.AutoHashMap(*ir.Inst, MCValue), | |
| 168 | const fn_type = fn_val.base.typ.cast(Type.Fn).?; | 63 | /// Constants are embedded within functions (at the end, after `ret`) |
| 169 | const fn_type_normal = &fn_type.key.data.Normal; | 64 | /// so that they are independently updateable. |
| 170 | 65 | /// This is a list of constants that must be appended to the symbol after `ret`. | |
| 171 | try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); | 66 | constants: std.ArrayList(ir.TypedValue), |
| 172 | //add_uwtable_attr(g, fn_table_entry->llvm_value); | 67 | errors: std.ArrayList(ErrorMsg), |
| 173 | try addLLVMFnAttr(ofile, llvm_fn, "nobuiltin"); | 68 | |
| 174 | 69 | const MCValue = union(enum) { | |
| 175 | //if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) { | 70 | none, |
| 176 | // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true"); | 71 | unreach, |
| 177 | // ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim-non-leaf", nullptr); | 72 | /// A pointer-sized integer that fits in a register. |
| 178 | //} | 73 | immediate: u64, |
| 179 | 74 | /// Refers to the index into `constants` field of `Function`. | |
| 180 | //if (fn_table_entry->section_name) { | 75 | local_const_ptr: usize, |
| 181 | // LLVMSetSection(fn_table_entry->llvm_value, buf_ptr(fn_table_entry->section_name)); | 76 | }; |
| 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 | 77 | ||
| 256 | if (var_type.handleIsPtr()) { | 78 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { |
| 257 | // if (gen_info->is_byval) { | 79 | switch (inst.tag) { |
| 258 | // gen_type = var->value->type; | 80 | .unreach => return self.genPanic(inst.src), |
| 259 | // } else { | 81 | .constant => unreachable, // excluded from function bodies |
| 260 | // gen_type = gen_info->type; | 82 | .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?), |
| 261 | // } | 83 | .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), |
| 262 | var_scope.data.Param.llvm_value = llvm.GetParam(llvm_fn, @intCast(c_uint, i)); | ||
| 263 | } else { | ||
| 264 | // gen_type = var->value->type; | ||
| 265 | var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, .Abi); | ||
| 266 | } | 84 | } |
| 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 | } | 85 | } |
| 276 | 86 | ||
| 277 | // TODO finishing error return trace setup. we have to do this after all the allocas. | 87 | fn genPanic(self: *Function, src: usize) !MCValue { |
| 278 | 88 | // TODO change this to call the panic function | |
| 279 | // create debug variable declarations for parameters | 89 | switch (self.module.target.cpu.arch) { |
| 280 | // rely on the first variables in the variable_list being parameters. | 90 | .i386, .x86_64 => { |
| 281 | //size_t next_var_i = 0; | 91 | try self.code.append(0xcc); // x86 int3 |
| 282 | for (fn_type.key.data.Normal.params) |param, i| { | 92 | }, |
| 283 | //FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; | 93 | else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}), |
| 284 | //if (info->gen_index == SIZE_MAX) | ||
| 285 | // continue; | ||
| 286 | const scope_var = var_list[i]; | ||
| 287 | //assert(variable->src_arg_index != SIZE_MAX); | ||
| 288 | //next_var_i += 1; | ||
| 289 | //assert(variable); | ||
| 290 | //assert(variable->value_ref); | ||
| 291 | |||
| 292 | if (!param.typ.handleIsPtr()) { | ||
| 293 | //clear_debug_source_node(g); | ||
| 294 | const llvm_param = llvm.GetParam(llvm_fn, @intCast(c_uint, i)); | ||
| 295 | _ = try renderStoreUntyped( | ||
| 296 | ofile, | ||
| 297 | llvm_param, | ||
| 298 | scope_var.data.Param.llvm_value, | ||
| 299 | .Abi, | ||
| 300 | .Non, | ||
| 301 | ); | ||
| 302 | } | 94 | } |
| 303 | 95 | return .unreach; | |
| 304 | //if (variable->decl_node) { | ||
| 305 | // gen_var_debug_decl(g, variable); | ||
| 306 | //} | ||
| 307 | } | 96 | } |
| 308 | 97 | ||
| 309 | for (code.basic_block_list.span()) |current_block| { | 98 | fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue { |
| 310 | llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block); | 99 | return self.fail(inst.base.src, "TODO machine code gen assembly", .{}); |
| 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); | ||
| 315 | } | ||
| 316 | current_block.llvm_exit_block = llvm.GetInsertBlock(ofile.builder); | ||
| 317 | } | 100 | } |
| 318 | } | ||
| 319 | |||
| 320 | fn 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 | |||
| 332 | fn 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 | |||
| 349 | fn 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 | } | ||
| 360 | |||
| 361 | fn 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 | 101 | ||
| 365 | fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: *llvm.Value, attr_name: []const u8, attr_val: []const u8) !void { | 102 | fn genPtrToInt(self: *Function, inst: *ir.Inst.PtrToInt) !MCValue { |
| 366 | return addLLVMAttrStr(ofile, fn_val, maxInt(llvm.AttributeIndex), attr_name, attr_val); | 103 | // no-op |
| 367 | } | 104 | return self.resolveInst(inst.args.ptr); |
| 368 | |||
| 369 | fn 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 | |||
| 373 | fn renderLoadUntyped( | ||
| 374 | ofile: *ObjectFile, | ||
| 375 | ptr: *llvm.Value, | ||
| 376 | alignment: Type.Pointer.Align, | ||
| 377 | vol: Type.Pointer.Vol, | ||
| 378 | name: [*:0]const u8, | ||
| 379 | ) !*llvm.Value { | ||
| 380 | const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory; | ||
| 381 | switch (vol) { | ||
| 382 | .Non => {}, | ||
| 383 | .Volatile => llvm.SetVolatile(result, 1), | ||
| 384 | } | 105 | } |
| 385 | llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.GetElementType(llvm.TypeOf(ptr)))); | ||
| 386 | return result; | ||
| 387 | } | ||
| 388 | 106 | ||
| 389 | fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*:0]const u8) !*llvm.Value { | 107 | fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue { |
| 390 | return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name); | 108 | if (inst.cast(ir.Inst.Constant)) |const_inst| { |
| 391 | } | 109 | switch (inst.ty.zigTypeTag()) { |
| 392 | 110 | .Int => { | |
| 393 | pub fn getHandleValue(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer) !?*llvm.Value { | 111 | const info = inst.ty.intInfo(self.module.target); |
| 394 | const child_type = ptr_type.key.child_type; | 112 | const ptr_bits = self.module.target.cpu.arch.ptrBitWidth(); |
| 395 | if (!child_type.hasBits()) { | 113 | if (info.bits > ptr_bits or info.signed) { |
| 396 | return null; | 114 | return self.fail(inst.src, "TODO const int bigger than ptr and signed int", .{}); |
| 397 | } | 115 | } |
| 398 | if (child_type.handleIsPtr()) { | 116 | return MCValue{ .immediate = const_inst.val.toUnsignedInt() }; |
| 399 | return ptr; | 117 | }, |
| 118 | else => return self.fail(inst.src, "TODO implement const of type '{}'", .{inst.ty}), | ||
| 119 | } | ||
| 120 | } else { | ||
| 121 | return self.inst_table.getValue(inst).?; | ||
| 122 | } | ||
| 400 | } | 123 | } |
| 401 | return try renderLoad(ofile, ptr, ptr_type, ""); | ||
| 402 | } | ||
| 403 | 124 | ||
| 404 | pub fn renderStoreUntyped( | 125 | fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } { |
| 405 | ofile: *ObjectFile, | 126 | @setCold(true); |
| 406 | value: *llvm.Value, | 127 | const msg = try std.fmt.allocPrint(self.errors.allocator, format, args); |
| 407 | ptr: *llvm.Value, | 128 | { |
| 408 | alignment: Type.Pointer.Align, | 129 | errdefer self.errors.allocator.free(msg); |
| 409 | vol: Type.Pointer.Vol, | 130 | (try self.errors.addOne()).* = .{ |
| 410 | ) !*llvm.Value { | 131 | .byte_offset = src, |
| 411 | const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory; | 132 | .msg = msg, |
| 412 | switch (vol) { | 133 | }; |
| 413 | .Non => {}, | 134 | } |
| 414 | .Volatile => llvm.SetVolatile(result, 1), | 135 | return error.CodegenFail; |
| 415 | } | 136 | } |
| 416 | llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value))); | 137 | }; |
| 417 | return result; | ||
| 418 | } | ||
| 419 | |||
| 420 | pub 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 | } | ||
| 428 | |||
| 429 | pub 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; | ||
| 440 | } | ||
| 441 | |||
| 442 | pub 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 | }; | ||
| 447 | } |
src-self-hosted/ir.zig+10-1| ... | @@ -724,7 +724,16 @@ pub fn main() anyerror!void { | ... | @@ -724,7 +724,16 @@ pub fn main() anyerror!void { |
| 724 | } | 724 | } |
| 725 | 725 | ||
| 726 | const link = @import("link.zig"); | 726 | const link = @import("link.zig"); |
| 727 | try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); | 727 | var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); |
| 728 | defer result.deinit(allocator); | ||
| 729 | if (result.errors.len != 0) { | ||
| 730 | for (result.errors) |err_msg| { | ||
| 731 | const loc = findLineColumn(source, err_msg.byte_offset); | ||
| 732 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); | ||
| 733 | } | ||
| 734 | if (debug_error_trace) return error.ParseFailure; | ||
| 735 | std.process.exit(1); | ||
| 736 | } | ||
| 728 | } | 737 | } |
| 729 | 738 | ||
| 730 | fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | 739 | fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { |
src-self-hosted/link.zig+186-23| ... | @@ -5,15 +5,38 @@ const Allocator = std.mem.Allocator; | ... | @@ -5,15 +5,38 @@ const Allocator = std.mem.Allocator; |
| 5 | const ir = @import("ir.zig"); | 5 | const ir = @import("ir.zig"); |
| 6 | const fs = std.fs; | 6 | const fs = std.fs; |
| 7 | const elf = std.elf; | 7 | const elf = std.elf; |
| 8 | const codegen = @import("codegen.zig"); | ||
| 8 | 9 | ||
| 9 | const executable_mode = 0o755; | 10 | const executable_mode = 0o755; |
| 10 | const default_entry_addr = 0x8000000; | 11 | const default_entry_addr = 0x8000000; |
| 11 | 12 | ||
| 13 | pub const ErrorMsg = struct { | ||
| 14 | byte_offset: usize, | ||
| 15 | msg: []const u8, | ||
| 16 | }; | ||
| 17 | |||
| 18 | pub const Result = struct { | ||
| 19 | errors: []ErrorMsg, | ||
| 20 | |||
| 21 | pub fn deinit(self: *Result, allocator: *mem.Allocator) void { | ||
| 22 | for (self.errors) |err| { | ||
| 23 | allocator.free(err.msg); | ||
| 24 | } | ||
| 25 | allocator.free(self.errors); | ||
| 26 | self.* = undefined; | ||
| 27 | } | ||
| 28 | }; | ||
| 29 | |||
| 12 | /// Attempts incremental linking, if the file already exists. | 30 | /// Attempts incremental linking, if the file already exists. |
| 13 | /// If incremental linking fails, falls back to truncating the file and rewriting it. | 31 | /// If incremental linking fails, falls back to truncating the file and rewriting it. |
| 14 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. | 32 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. |
| 15 | /// This operation is not atomic. | 33 | /// This operation is not atomic. |
| 16 | pub fn updateExecutableFilePath(allocator: *Allocator, module: ir.Module, dir: fs.Dir, sub_path: []const u8) !void { | 34 | pub fn updateExecutableFilePath( |
| 35 | allocator: *Allocator, | ||
| 36 | module: ir.Module, | ||
| 37 | dir: fs.Dir, | ||
| 38 | sub_path: []const u8, | ||
| 39 | ) !Result { | ||
| 17 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode }); | 40 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode }); |
| 18 | defer file.close(); | 41 | defer file.close(); |
| 19 | 42 | ||
| ... | @@ -21,12 +44,18 @@ pub fn updateExecutableFilePath(allocator: *Allocator, module: ir.Module, dir: f | ... | @@ -21,12 +44,18 @@ pub fn updateExecutableFilePath(allocator: *Allocator, module: ir.Module, dir: f |
| 21 | } | 44 | } |
| 22 | 45 | ||
| 23 | /// Atomically overwrites the old file, if present. | 46 | /// Atomically overwrites the old file, if present. |
| 24 | pub fn writeExecutableFilePath(allocator: *Allocator, module: ir.Module, dir: fs.Dir, sub_path: []const u8) !void { | 47 | pub fn writeExecutableFilePath( |
| 48 | allocator: *Allocator, | ||
| 49 | module: ir.Module, | ||
| 50 | dir: fs.Dir, | ||
| 51 | sub_path: []const u8, | ||
| 52 | ) !Result { | ||
| 25 | const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode }); | 53 | const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode }); |
| 26 | defer af.deinit(); | 54 | defer af.deinit(); |
| 27 | 55 | ||
| 28 | try writeExecutableFile(allocator, module, af.file); | 56 | const result = try writeExecutableFile(allocator, module, af.file); |
| 29 | try af.finish(); | 57 | try af.finish(); |
| 58 | return result; | ||
| 30 | } | 59 | } |
| 31 | 60 | ||
| 32 | /// Attempts incremental linking, if the file already exists. | 61 | /// Attempts incremental linking, if the file already exists. |
| ... | @@ -34,8 +63,8 @@ pub fn writeExecutableFilePath(allocator: *Allocator, module: ir.Module, dir: fs | ... | @@ -34,8 +63,8 @@ pub fn writeExecutableFilePath(allocator: *Allocator, module: ir.Module, dir: fs |
| 34 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | 63 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 35 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. | 64 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. |
| 36 | /// This operation is not atomic. | 65 | /// This operation is not atomic. |
| 37 | pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !void { | 66 | pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { |
| 38 | updateExecutableFileInner(allocator, module, file) catch |err| switch (err) { | 67 | return updateExecutableFileInner(allocator, module, file) catch |err| switch (err) { |
| 39 | error.IncrFailed => { | 68 | error.IncrFailed => { |
| 40 | return writeExecutableFile(allocator, module, file); | 69 | return writeExecutableFile(allocator, module, file); |
| 41 | }, | 70 | }, |
| ... | @@ -66,20 +95,17 @@ const Update = struct { | ... | @@ -66,20 +95,17 @@ const Update = struct { |
| 66 | text_section_index: ?u16, | 95 | text_section_index: ?u16, |
| 67 | symtab_section_index: ?u16, | 96 | symtab_section_index: ?u16, |
| 68 | 97 | ||
| 69 | /// Key: index into strtab. Value: index into symbols. | ||
| 70 | symbol_table: std.AutoHashMap(usize, usize), | ||
| 71 | /// The same order as in the file | 98 | /// The same order as in the file |
| 72 | symbols: std.ArrayList(elf.Elf64_Sym), | 99 | symbols: std.ArrayList(elf.Elf64_Sym), |
| 73 | /// Sorted by address, index into symbols | 100 | |
| 74 | symbols_by_addr: std.ArrayList(usize), | 101 | errors: std.ArrayList(ErrorMsg), |
| 75 | 102 | ||
| 76 | fn deinit(self: *Update) void { | 103 | fn deinit(self: *Update) void { |
| 77 | self.sections.deinit(); | 104 | self.sections.deinit(); |
| 78 | self.program_headers.deinit(); | 105 | self.program_headers.deinit(); |
| 79 | self.shstrtab.deinit(); | 106 | self.shstrtab.deinit(); |
| 80 | self.symbol_table.deinit(); | ||
| 81 | self.symbols.deinit(); | 107 | self.symbols.deinit(); |
| 82 | self.symbols_by_addr.deinit(); | 108 | self.errors.deinit(); |
| 83 | } | 109 | } |
| 84 | 110 | ||
| 85 | // `expand_num / expand_den` is the factor of padding when allocation | 111 | // `expand_num / expand_den` is the factor of padding when allocation |
| ... | @@ -162,6 +188,7 @@ const Update = struct { | ... | @@ -162,6 +188,7 @@ const Update = struct { |
| 162 | fn makeString(self: *Update, bytes: []const u8) !u32 { | 188 | fn makeString(self: *Update, bytes: []const u8) !u32 { |
| 163 | const result = self.shstrtab.items.len; | 189 | const result = self.shstrtab.items.len; |
| 164 | try self.shstrtab.appendSlice(bytes); | 190 | try self.shstrtab.appendSlice(bytes); |
| 191 | try self.shstrtab.append(0); | ||
| 165 | return @intCast(u32, result); | 192 | return @intCast(u32, result); |
| 166 | } | 193 | } |
| 167 | 194 | ||
| ... | @@ -187,6 +214,7 @@ const Update = struct { | ... | @@ -187,6 +214,7 @@ const Update = struct { |
| 187 | const file_size = 256 * 1024; | 214 | const file_size = 256 * 1024; |
| 188 | const p_align = 0x1000; | 215 | const p_align = 0x1000; |
| 189 | const off = self.findFreeSpace(file_size, p_align); | 216 | const off = self.findFreeSpace(file_size, p_align); |
| 217 | //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | ||
| 190 | try self.program_headers.append(.{ | 218 | try self.program_headers.append(.{ |
| 191 | .p_type = elf.PT_LOAD, | 219 | .p_type = elf.PT_LOAD, |
| 192 | .p_offset = off, | 220 | .p_offset = off, |
| ... | @@ -194,10 +222,10 @@ const Update = struct { | ... | @@ -194,10 +222,10 @@ const Update = struct { |
| 194 | .p_vaddr = default_entry_addr, | 222 | .p_vaddr = default_entry_addr, |
| 195 | .p_paddr = default_entry_addr, | 223 | .p_paddr = default_entry_addr, |
| 196 | .p_memsz = 0, | 224 | .p_memsz = 0, |
| 197 | .p_align = 0x1000, | 225 | .p_align = p_align, |
| 198 | .p_flags = elf.PF_X | elf.PF_R, | 226 | .p_flags = elf.PF_X | elf.PF_R, |
| 199 | }); | 227 | }); |
| 200 | self.entry_addr = default_entry_addr; | 228 | self.entry_addr = null; |
| 201 | phdr_load_re_dirty = true; | 229 | phdr_load_re_dirty = true; |
| 202 | phdr_table_dirty = true; | 230 | phdr_table_dirty = true; |
| 203 | } | 231 | } |
| ... | @@ -220,6 +248,7 @@ const Update = struct { | ... | @@ -220,6 +248,7 @@ const Update = struct { |
| 220 | if (self.shstrtab_index == null) { | 248 | if (self.shstrtab_index == null) { |
| 221 | self.shstrtab_index = @intCast(u16, self.sections.items.len); | 249 | self.shstrtab_index = @intCast(u16, self.sections.items.len); |
| 222 | const off = self.findFreeSpace(self.shstrtab.items.len, 1); | 250 | const off = self.findFreeSpace(self.shstrtab.items.len, 1); |
| 251 | //std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len }); | ||
| 223 | try self.sections.append(.{ | 252 | try self.sections.append(.{ |
| 224 | .sh_name = try self.makeString(".shstrtab"), | 253 | .sh_name = try self.makeString(".shstrtab"), |
| 225 | .sh_type = elf.SHT_STRTAB, | 254 | .sh_type = elf.SHT_STRTAB, |
| ... | @@ -259,6 +288,7 @@ const Update = struct { | ... | @@ -259,6 +288,7 @@ const Update = struct { |
| 259 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); | 288 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); |
| 260 | const file_size = self.module.exports.len * each_size; | 289 | const file_size = self.module.exports.len * each_size; |
| 261 | const off = self.findFreeSpace(file_size, min_align); | 290 | const off = self.findFreeSpace(file_size, min_align); |
| 291 | //std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); | ||
| 262 | 292 | ||
| 263 | try self.sections.append(.{ | 293 | try self.sections.append(.{ |
| 264 | .sh_name = try self.makeString(".symtab"), | 294 | .sh_name = try self.makeString(".symtab"), |
| ... | @@ -307,6 +337,7 @@ const Update = struct { | ... | @@ -307,6 +337,7 @@ const Update = struct { |
| 307 | const needed_size = self.program_headers.items.len * phsize; | 337 | const needed_size = self.program_headers.items.len * phsize; |
| 308 | 338 | ||
| 309 | if (needed_size > allocated_size) { | 339 | if (needed_size > allocated_size) { |
| 340 | self.phdr_table_offset = null; // free the space | ||
| 310 | self.phdr_table_offset = self.findFreeSpace(needed_size, phalign); | 341 | self.phdr_table_offset = self.findFreeSpace(needed_size, phalign); |
| 311 | } | 342 | } |
| 312 | 343 | ||
| ... | @@ -361,6 +392,7 @@ const Update = struct { | ... | @@ -361,6 +392,7 @@ const Update = struct { |
| 361 | const needed_size = self.sections.items.len * phsize; | 392 | const needed_size = self.sections.items.len * phsize; |
| 362 | 393 | ||
| 363 | if (needed_size > allocated_size) { | 394 | if (needed_size > allocated_size) { |
| 395 | self.shdr_table_offset = null; // free the space | ||
| 364 | self.shdr_table_offset = self.findFreeSpace(needed_size, phalign); | 396 | self.shdr_table_offset = self.findFreeSpace(needed_size, phalign); |
| 365 | } | 397 | } |
| 366 | 398 | ||
| ... | @@ -414,11 +446,30 @@ const Update = struct { | ... | @@ -414,11 +446,30 @@ const Update = struct { |
| 414 | }, | 446 | }, |
| 415 | } | 447 | } |
| 416 | } | 448 | } |
| 417 | if (shstrtab_dirty) { | ||
| 418 | try self.file.pwriteAll(self.shstrtab.items, self.sections.items[self.shstrtab_index.?].sh_offset); | ||
| 419 | } | ||
| 420 | try self.writeCodeAndSymbols(); | 449 | try self.writeCodeAndSymbols(); |
| 421 | try self.writeElfHeader(); | 450 | |
| 451 | const shstrtab_sect = &self.sections.items[self.shstrtab_index.?]; | ||
| 452 | if (shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) { | ||
| 453 | const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset); | ||
| 454 | const needed_size = self.shstrtab.items.len; | ||
| 455 | |||
| 456 | if (needed_size > allocated_size) { | ||
| 457 | shstrtab_sect.sh_size = 0; // free the space | ||
| 458 | shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1); | ||
| 459 | shstrtab_sect.sh_size = needed_size; | ||
| 460 | } | ||
| 461 | try self.file.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); | ||
| 462 | } | ||
| 463 | if (self.entry_addr == null) { | ||
| 464 | const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{}); | ||
| 465 | errdefer self.errors.allocator.free(msg); | ||
| 466 | try self.errors.append(.{ | ||
| 467 | .byte_offset = 0, | ||
| 468 | .msg = msg, | ||
| 469 | }); | ||
| 470 | } else { | ||
| 471 | try self.writeElfHeader(); | ||
| 472 | } | ||
| 422 | // TODO find end pos and truncate | 473 | // TODO find end pos and truncate |
| 423 | } | 474 | } |
| 424 | 475 | ||
| ... | @@ -540,13 +591,122 @@ const Update = struct { | ... | @@ -540,13 +591,122 @@ const Update = struct { |
| 540 | } | 591 | } |
| 541 | 592 | ||
| 542 | fn writeCodeAndSymbols(self: *Update) !void { | 593 | fn writeCodeAndSymbols(self: *Update) !void { |
| 543 | @panic("TODO writeCodeAndSymbols"); | 594 | // index 0 is always a null symbol |
| 595 | try self.symbols.resize(1); | ||
| 596 | self.symbols.items[0] = .{ | ||
| 597 | .st_name = 0, | ||
| 598 | .st_info = 0, | ||
| 599 | .st_other = 0, | ||
| 600 | .st_shndx = 0, | ||
| 601 | .st_value = 0, | ||
| 602 | .st_size = 0, | ||
| 603 | }; | ||
| 604 | |||
| 605 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | ||
| 606 | var vaddr: u64 = phdr.p_vaddr; | ||
| 607 | |||
| 608 | var code = std.ArrayList(u8).init(self.sections.allocator); | ||
| 609 | defer code.deinit(); | ||
| 610 | |||
| 611 | for (self.module.exports) |exp| { | ||
| 612 | code.shrink(0); | ||
| 613 | var symbol = try codegen.generateSymbol(exp.typed_value, self.module.*, &code); | ||
| 614 | defer symbol.deinit(code.allocator); | ||
| 615 | if (symbol.errors.len != 0) { | ||
| 616 | for (symbol.errors) |err| { | ||
| 617 | const msg = try mem.dupe(self.errors.allocator, u8, err.msg); | ||
| 618 | errdefer self.errors.allocator.free(msg); | ||
| 619 | try self.errors.append(.{ | ||
| 620 | .byte_offset = err.byte_offset, | ||
| 621 | .msg = msg, | ||
| 622 | }); | ||
| 623 | } | ||
| 624 | continue; | ||
| 625 | } | ||
| 626 | |||
| 627 | if (mem.eql(u8, exp.name, "_start")) { | ||
| 628 | self.entry_addr = vaddr; | ||
| 629 | } | ||
| 630 | (try self.symbols.addOne()).* = .{ | ||
| 631 | .st_name = try self.makeString(exp.name), | ||
| 632 | .st_info = (elf.STB_LOCAL << 4) | elf.STT_FUNC, | ||
| 633 | .st_other = 0, | ||
| 634 | .st_shndx = self.text_section_index.?, | ||
| 635 | .st_value = vaddr, | ||
| 636 | .st_size = code.items.len, | ||
| 637 | }; | ||
| 638 | vaddr += code.items.len; | ||
| 639 | } | ||
| 640 | |||
| 641 | return self.writeSymbols(); | ||
| 642 | } | ||
| 643 | |||
| 644 | fn writeSymbols(self: *Update) !void { | ||
| 645 | const ptr_width: enum { p32, p64 } = switch (self.module.target.cpu.arch.ptrBitWidth()) { | ||
| 646 | 32 => .p32, | ||
| 647 | 64 => .p64, | ||
| 648 | else => return error.UnsupportedArchitecture, | ||
| 649 | }; | ||
| 650 | const small_ptr = ptr_width == .p32; | ||
| 651 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | ||
| 652 | const sym_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); | ||
| 653 | const sym_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); | ||
| 654 | |||
| 655 | const allocated_size = self.allocatedSize(syms_sect.sh_offset); | ||
| 656 | const needed_size = self.symbols.items.len * sym_size; | ||
| 657 | if (needed_size > allocated_size) { | ||
| 658 | syms_sect.sh_size = 0; // free the space | ||
| 659 | syms_sect.sh_offset = self.findFreeSpace(needed_size, sym_align); | ||
| 660 | syms_sect.sh_size = needed_size; | ||
| 661 | } | ||
| 662 | const allocator = self.symbols.allocator; | ||
| 663 | const foreign_endian = self.module.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | ||
| 664 | switch (ptr_width) { | ||
| 665 | .p32 => { | ||
| 666 | const buf = try allocator.alloc(elf.Elf32_Sym, self.symbols.items.len); | ||
| 667 | defer allocator.free(buf); | ||
| 668 | |||
| 669 | for (buf) |*sym, i| { | ||
| 670 | sym.* = .{ | ||
| 671 | .st_name = self.symbols.items[i].st_name, | ||
| 672 | .st_value = @intCast(u32, self.symbols.items[i].st_value), | ||
| 673 | .st_size = @intCast(u32, self.symbols.items[i].st_size), | ||
| 674 | .st_info = self.symbols.items[i].st_info, | ||
| 675 | .st_other = self.symbols.items[i].st_other, | ||
| 676 | .st_shndx = self.symbols.items[i].st_shndx, | ||
| 677 | }; | ||
| 678 | if (foreign_endian) { | ||
| 679 | bswapAllFields(elf.Elf32_Sym, sym); | ||
| 680 | } | ||
| 681 | } | ||
| 682 | try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset); | ||
| 683 | }, | ||
| 684 | .p64 => { | ||
| 685 | const buf = try allocator.alloc(elf.Elf64_Sym, self.symbols.items.len); | ||
| 686 | defer allocator.free(buf); | ||
| 687 | |||
| 688 | for (buf) |*sym, i| { | ||
| 689 | sym.* = .{ | ||
| 690 | .st_name = self.symbols.items[i].st_name, | ||
| 691 | .st_value = self.symbols.items[i].st_value, | ||
| 692 | .st_size = self.symbols.items[i].st_size, | ||
| 693 | .st_info = self.symbols.items[i].st_info, | ||
| 694 | .st_other = self.symbols.items[i].st_other, | ||
| 695 | .st_shndx = self.symbols.items[i].st_shndx, | ||
| 696 | }; | ||
| 697 | if (foreign_endian) { | ||
| 698 | bswapAllFields(elf.Elf64_Sym, sym); | ||
| 699 | } | ||
| 700 | } | ||
| 701 | try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset); | ||
| 702 | }, | ||
| 703 | } | ||
| 544 | } | 704 | } |
| 545 | }; | 705 | }; |
| 546 | 706 | ||
| 547 | /// Truncates the existing file contents and overwrites the contents. | 707 | /// Truncates the existing file contents and overwrites the contents. |
| 548 | /// Returns an error if `file` is not already open with +read +write +seek abilities. | 708 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 549 | pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !void { | 709 | pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { |
| 550 | var update = Update{ | 710 | var update = Update{ |
| 551 | .file = file, | 711 | .file = file, |
| 552 | .module = &module, | 712 | .module = &module, |
| ... | @@ -561,17 +721,20 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi | ... | @@ -561,17 +721,20 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi |
| 561 | .text_section_index = null, | 721 | .text_section_index = null, |
| 562 | .symtab_section_index = null, | 722 | .symtab_section_index = null, |
| 563 | 723 | ||
| 564 | .symbol_table = std.AutoHashMap(usize, usize).init(allocator), | ||
| 565 | .symbols = std.ArrayList(elf.Elf64_Sym).init(allocator), | 724 | .symbols = std.ArrayList(elf.Elf64_Sym).init(allocator), |
| 566 | .symbols_by_addr = std.ArrayList(usize).init(allocator), | 725 | |
| 726 | .errors = std.ArrayList(ErrorMsg).init(allocator), | ||
| 567 | }; | 727 | }; |
| 568 | defer update.deinit(); | 728 | defer update.deinit(); |
| 569 | 729 | ||
| 570 | return update.perform(); | 730 | try update.perform(); |
| 731 | return Result{ | ||
| 732 | .errors = update.errors.toOwnedSlice(), | ||
| 733 | }; | ||
| 571 | } | 734 | } |
| 572 | 735 | ||
| 573 | /// Returns error.IncrFailed if incremental update could not be performed. | 736 | /// Returns error.IncrFailed if incremental update could not be performed. |
| 574 | fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !void { | 737 | fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { |
| 575 | //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; | 738 | //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; |
| 576 | 739 | ||
| 577 | // TODO implement incremental linking | 740 | // TODO implement incremental linking |
src-self-hosted/value.zig+50| ... | @@ -264,6 +264,56 @@ pub const Value = extern union { | ... | @@ -264,6 +264,56 @@ pub const Value = extern union { |
| 264 | } | 264 | } |
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | /// Asserts the value is an integer and it fits in a u64 | ||
| 268 | pub fn toUnsignedInt(self: Value) u64 { | ||
| 269 | switch (self.tag()) { | ||
| 270 | .ty, | ||
| 271 | .u8_type, | ||
| 272 | .i8_type, | ||
| 273 | .isize_type, | ||
| 274 | .usize_type, | ||
| 275 | .c_short_type, | ||
| 276 | .c_ushort_type, | ||
| 277 | .c_int_type, | ||
| 278 | .c_uint_type, | ||
| 279 | .c_long_type, | ||
| 280 | .c_ulong_type, | ||
| 281 | .c_longlong_type, | ||
| 282 | .c_ulonglong_type, | ||
| 283 | .c_longdouble_type, | ||
| 284 | .f16_type, | ||
| 285 | .f32_type, | ||
| 286 | .f64_type, | ||
| 287 | .f128_type, | ||
| 288 | .c_void_type, | ||
| 289 | .bool_type, | ||
| 290 | .void_type, | ||
| 291 | .type_type, | ||
| 292 | .anyerror_type, | ||
| 293 | .comptime_int_type, | ||
| 294 | .comptime_float_type, | ||
| 295 | .noreturn_type, | ||
| 296 | .fn_naked_noreturn_no_args_type, | ||
| 297 | .single_const_pointer_to_comptime_int_type, | ||
| 298 | .const_slice_u8_type, | ||
| 299 | .void_value, | ||
| 300 | .noreturn_value, | ||
| 301 | .bool_true, | ||
| 302 | .bool_false, | ||
| 303 | .function, | ||
| 304 | .ref, | ||
| 305 | .ref_val, | ||
| 306 | .bytes, | ||
| 307 | => unreachable, | ||
| 308 | |||
| 309 | .zero => return 0, | ||
| 310 | |||
| 311 | .int_u64 => return self.cast(Payload.Int_u64).?.int, | ||
| 312 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int), | ||
| 313 | .int_big => return self.cast(Payload.IntBig).?.big_int.to(u64) catch unreachable, | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 267 | /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. | 317 | /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. |
| 268 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { | 318 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { |
| 269 | switch (self.tag()) { | 319 | switch (self.tag()) { |