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