| author | |
| committer | |
| log | c0df70706695a67089d4e691d3d3a0f77b90298f |
| tree | cc05dafeb6b079455c95be905761f08982a1c6fa |
| parent | 5ab307cf47b1f0418d9ed4ab56df6fb798305c20 |
| signature |
My original goal here was just to get the self-hosted Wasm backend
compiling again after the pipeline change, but it turned out that from
there it was pretty simple to entirely eliminate the shared state
between `codegen.wasm` and `link.Wasm`. As such, this commit not only
fixes the backend, but makes it the second backend (after CBE) to
support the new 1:N:1 threading model.10 files changed, 404 insertions(+), 311 deletions(-)
lib/std/multi_array_list.zig+16| ... | @@ -135,6 +135,22 @@ pub fn MultiArrayList(comptime T: type) type { | ... | @@ -135,6 +135,22 @@ pub fn MultiArrayList(comptime T: type) type { |
| 135 | self.* = undefined; | 135 | self.* = undefined; |
| 136 | } | 136 | } |
| 137 | 137 | ||
| 138 | /// Returns a `Slice` representing a range of elements in `s`, analagous to `arr[off..len]`. | ||
| 139 | /// It is illegal to call `deinit` or `toMultiArrayList` on the returned `Slice`. | ||
| 140 | /// Asserts that `off + len <= s.len`. | ||
| 141 | pub fn subslice(s: Slice, off: usize, len: usize) Slice { | ||
| 142 | assert(off + len <= s.len); | ||
| 143 | var ptrs: [fields.len][*]u8 = undefined; | ||
| 144 | inline for (s.ptrs, &ptrs, fields) |in, *out, field| { | ||
| 145 | out.* = in + (off * @sizeOf(field.type)); | ||
| 146 | } | ||
| 147 | return .{ | ||
| 148 | .ptrs = ptrs, | ||
| 149 | .len = len, | ||
| 150 | .capacity = len, | ||
| 151 | }; | ||
| 152 | } | ||
| 153 | |||
| 138 | /// This function is used in the debugger pretty formatters in tools/ to fetch the | 154 | /// This function is used in the debugger pretty formatters in tools/ to fetch the |
| 139 | /// child field order and entry type to facilitate fancy debug printing for this type. | 155 | /// child field order and entry type to facilitate fancy debug printing for this type. |
| 140 | fn dbHelper(self: *Slice, child: *Elem, field: *Field, entry: *Entry) void { | 156 | fn dbHelper(self: *Slice, child: *Elem, field: *Field, entry: *Entry) void { |
src/Compilation.zig+1-1| ... | @@ -3500,7 +3500,7 @@ pub fn saveState(comp: *Compilation) !void { | ... | @@ -3500,7 +3500,7 @@ pub fn saveState(comp: *Compilation) !void { |
| 3500 | // TODO handle the union safety field | 3500 | // TODO handle the union safety field |
| 3501 | //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data))); | 3501 | //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data))); |
| 3502 | addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items)); | 3502 | addBuf(&bufs, mem.sliceAsBytes(wasm.mir_extra.items)); |
| 3503 | addBuf(&bufs, mem.sliceAsBytes(wasm.all_zcu_locals.items)); | 3503 | addBuf(&bufs, mem.sliceAsBytes(wasm.mir_locals.items)); |
| 3504 | addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items)); | 3504 | addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items)); |
| 3505 | addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items)); | 3505 | addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_offs.items)); |
| 3506 | 3506 |
src/arch/wasm/CodeGen.zig+103-198| ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const testing = std.testing; | 5 | const testing = std.testing; |
| 6 | const leb = std.leb; | ||
| 7 | const mem = std.mem; | 6 | const mem = std.mem; |
| 8 | const log = std.log.scoped(.codegen); | 7 | const log = std.log.scoped(.codegen); |
| 9 | 8 | ||
| ... | @@ -18,12 +17,10 @@ const Compilation = @import("../../Compilation.zig"); | ... | @@ -18,12 +17,10 @@ const Compilation = @import("../../Compilation.zig"); |
| 18 | const link = @import("../../link.zig"); | 17 | const link = @import("../../link.zig"); |
| 19 | const Air = @import("../../Air.zig"); | 18 | const Air = @import("../../Air.zig"); |
| 20 | const Mir = @import("Mir.zig"); | 19 | const Mir = @import("Mir.zig"); |
| 21 | const Emit = @import("Emit.zig"); | ||
| 22 | const abi = @import("abi.zig"); | 20 | const abi = @import("abi.zig"); |
| 23 | const Alignment = InternPool.Alignment; | 21 | const Alignment = InternPool.Alignment; |
| 24 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; | 22 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 25 | const errUnionErrorOffset = codegen.errUnionErrorOffset; | 23 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 26 | const Wasm = link.File.Wasm; | ||
| 27 | 24 | ||
| 28 | const target_util = @import("../../target.zig"); | 25 | const target_util = @import("../../target.zig"); |
| 29 | const libcFloatPrefix = target_util.libcFloatPrefix; | 26 | const libcFloatPrefix = target_util.libcFloatPrefix; |
| ... | @@ -78,17 +75,24 @@ simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty, | ... | @@ -78,17 +75,24 @@ simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty, |
| 78 | /// The Target we're emitting (used to call intInfo) | 75 | /// The Target we're emitting (used to call intInfo) |
| 79 | target: *const std.Target, | 76 | target: *const std.Target, |
| 80 | ptr_size: enum { wasm32, wasm64 }, | 77 | ptr_size: enum { wasm32, wasm64 }, |
| 81 | wasm: *link.File.Wasm, | ||
| 82 | pt: Zcu.PerThread, | 78 | pt: Zcu.PerThread, |
| 83 | /// List of MIR Instructions | 79 | /// List of MIR Instructions |
| 84 | mir_instructions: *std.MultiArrayList(Mir.Inst), | 80 | mir_instructions: std.MultiArrayList(Mir.Inst), |
| 85 | /// Contains extra data for MIR | 81 | /// Contains extra data for MIR |
| 86 | mir_extra: *std.ArrayListUnmanaged(u32), | 82 | mir_extra: std.ArrayListUnmanaged(u32), |
| 87 | start_mir_extra_off: u32, | ||
| 88 | start_locals_off: u32, | ||
| 89 | /// List of all locals' types generated throughout this declaration | 83 | /// List of all locals' types generated throughout this declaration |
| 90 | /// used to emit locals count at start of 'code' section. | 84 | /// used to emit locals count at start of 'code' section. |
| 91 | locals: *std.ArrayListUnmanaged(std.wasm.Valtype), | 85 | mir_locals: std.ArrayListUnmanaged(std.wasm.Valtype), |
| 86 | /// Set of all UAVs referenced by this function. Key is the UAV value, value is the alignment. | ||
| 87 | /// `.none` means naturally aligned. An explicit alignment is never less than the natural alignment. | ||
| 88 | mir_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), | ||
| 89 | /// Set of all functions whose address this function has taken and which therefore might be called | ||
| 90 | /// via a `call_indirect` function. | ||
| 91 | mir_indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), | ||
| 92 | /// Set of all function types used by this function. These must be interned by the linker. | ||
| 93 | mir_func_tys: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), | ||
| 94 | /// The number of `error_name_table_ref` instructions emitted. | ||
| 95 | error_name_table_ref_count: u32, | ||
| 92 | /// When a function is executing, we store the the current stack pointer's value within this local. | 96 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| 93 | /// This value is then used to restore the stack pointer to the original value at the return of the function. | 97 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| 94 | initial_stack_value: WValue = .none, | 98 | initial_stack_value: WValue = .none, |
| ... | @@ -219,7 +223,7 @@ const WValue = union(enum) { | ... | @@ -219,7 +223,7 @@ const WValue = union(enum) { |
| 219 | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. | 223 | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. |
| 220 | 224 | ||
| 221 | const index = local_value - reserved; | 225 | const index = local_value - reserved; |
| 222 | const valtype = gen.locals.items[gen.start_locals_off + index]; | 226 | const valtype = gen.mir_locals.items[index]; |
| 223 | switch (valtype) { | 227 | switch (valtype) { |
| 224 | .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead | 228 | .i32 => gen.free_locals_i32.append(gen.gpa, local_value) catch return, // It's ok to fail any of those, a new local can be allocated instead |
| 225 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, | 229 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, |
| ... | @@ -716,6 +720,12 @@ pub fn deinit(cg: *CodeGen) void { | ... | @@ -716,6 +720,12 @@ pub fn deinit(cg: *CodeGen) void { |
| 716 | cg.free_locals_f32.deinit(gpa); | 720 | cg.free_locals_f32.deinit(gpa); |
| 717 | cg.free_locals_f64.deinit(gpa); | 721 | cg.free_locals_f64.deinit(gpa); |
| 718 | cg.free_locals_v128.deinit(gpa); | 722 | cg.free_locals_v128.deinit(gpa); |
| 723 | cg.mir_instructions.deinit(gpa); | ||
| 724 | cg.mir_extra.deinit(gpa); | ||
| 725 | cg.mir_locals.deinit(gpa); | ||
| 726 | cg.mir_uavs.deinit(gpa); | ||
| 727 | cg.mir_indirect_function_set.deinit(gpa); | ||
| 728 | cg.mir_func_tys.deinit(gpa); | ||
| 719 | cg.* = undefined; | 729 | cg.* = undefined; |
| 720 | } | 730 | } |
| 721 | 731 | ||
| ... | @@ -876,7 +886,7 @@ fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void { | ... | @@ -876,7 +886,7 @@ fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void { |
| 876 | } | 886 | } |
| 877 | 887 | ||
| 878 | fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void { | 888 | fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void { |
| 879 | const extra_index = cg.extraLen(); | 889 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 880 | try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode)); | 890 | try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode)); |
| 881 | try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } }); | 891 | try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } }); |
| 882 | } | 892 | } |
| ... | @@ -889,10 +899,6 @@ fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void | ... | @@ -889,10 +899,6 @@ fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void |
| 889 | try cg.addInst(.{ .tag = tag, .data = .{ .local = local } }); | 899 | try cg.addInst(.{ .tag = tag, .data = .{ .local = local } }); |
| 890 | } | 900 | } |
| 891 | 901 | ||
| 892 | fn addFuncTy(cg: *CodeGen, tag: Mir.Inst.Tag, i: Wasm.FunctionType.Index) error{OutOfMemory}!void { | ||
| 893 | try cg.addInst(.{ .tag = tag, .data = .{ .func_ty = i } }); | ||
| 894 | } | ||
| 895 | |||
| 896 | /// Accepts an unsigned 32bit integer rather than a signed integer to | 902 | /// Accepts an unsigned 32bit integer rather than a signed integer to |
| 897 | /// prevent us from having to bitcast multiple times as most values | 903 | /// prevent us from having to bitcast multiple times as most values |
| 898 | /// within codegen are represented as unsigned rather than signed. | 904 | /// within codegen are represented as unsigned rather than signed. |
| ... | @@ -911,7 +917,7 @@ fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void { | ... | @@ -911,7 +917,7 @@ fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void { |
| 911 | /// Accepts the index into the list of 128bit-immediates | 917 | /// Accepts the index into the list of 128bit-immediates |
| 912 | fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void { | 918 | fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void { |
| 913 | const simd_values = cg.simd_immediates.items[index]; | 919 | const simd_values = cg.simd_immediates.items[index]; |
| 914 | const extra_index = cg.extraLen(); | 920 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 915 | // tag + 128bit value | 921 | // tag + 128bit value |
| 916 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5); | 922 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5); |
| 917 | cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const)); | 923 | cg.mir_extra.appendAssumeCapacity(@intFromEnum(std.wasm.SimdOpcode.v128_const)); |
| ... | @@ -956,15 +962,13 @@ fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { | ... | @@ -956,15 +962,13 @@ fn addExtra(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { |
| 956 | /// Returns the index into `mir_extra` | 962 | /// Returns the index into `mir_extra` |
| 957 | fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { | 963 | fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { |
| 958 | const fields = std.meta.fields(@TypeOf(extra)); | 964 | const fields = std.meta.fields(@TypeOf(extra)); |
| 959 | const result = cg.extraLen(); | 965 | const result: u32 = @intCast(cg.mir_extra.items.len); |
| 960 | inline for (fields) |field| { | 966 | inline for (fields) |field| { |
| 961 | cg.mir_extra.appendAssumeCapacity(switch (field.type) { | 967 | cg.mir_extra.appendAssumeCapacity(switch (field.type) { |
| 962 | u32 => @field(extra, field.name), | 968 | u32 => @field(extra, field.name), |
| 963 | i32 => @bitCast(@field(extra, field.name)), | 969 | i32 => @bitCast(@field(extra, field.name)), |
| 964 | InternPool.Index, | 970 | InternPool.Index, |
| 965 | InternPool.Nav.Index, | 971 | InternPool.Nav.Index, |
| 966 | Wasm.UavsObjIndex, | ||
| 967 | Wasm.UavsExeIndex, | ||
| 968 | => @intFromEnum(@field(extra, field.name)), | 972 | => @intFromEnum(@field(extra, field.name)), |
| 969 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), | 973 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), |
| 970 | }); | 974 | }); |
| ... | @@ -1034,18 +1038,12 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { | ... | @@ -1034,18 +1038,12 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 1034 | .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), | 1038 | .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), |
| 1035 | .float64 => |val| try cg.addFloat64(val), | 1039 | .float64 => |val| try cg.addFloat64(val), |
| 1036 | .nav_ref => |nav_ref| { | 1040 | .nav_ref => |nav_ref| { |
| 1037 | const wasm = cg.wasm; | 1041 | const zcu = cg.pt.zcu; |
| 1038 | const comp = wasm.base.comp; | ||
| 1039 | const zcu = comp.zcu.?; | ||
| 1040 | const ip = &zcu.intern_pool; | 1042 | const ip = &zcu.intern_pool; |
| 1041 | if (ip.getNav(nav_ref.nav_index).isFn(ip)) { | 1043 | if (ip.getNav(nav_ref.nav_index).isFn(ip)) { |
| 1042 | assert(nav_ref.offset == 0); | 1044 | assert(nav_ref.offset == 0); |
| 1043 | const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, nav_ref.nav_index); | 1045 | try cg.mir_indirect_function_set.put(cg.gpa, nav_ref.nav_index, {}); |
| 1044 | if (!gop.found_existing) gop.value_ptr.* = {}; | 1046 | try cg.addInst(.{ .tag = .func_ref, .data = .{ .nav_index = nav_ref.nav_index } }); |
| 1045 | try cg.addInst(.{ | ||
| 1046 | .tag = .func_ref, | ||
| 1047 | .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) }, | ||
| 1048 | }); | ||
| 1049 | } else if (nav_ref.offset == 0) { | 1047 | } else if (nav_ref.offset == 0) { |
| 1050 | try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } }); | 1048 | try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } }); |
| 1051 | } else { | 1049 | } else { |
| ... | @@ -1061,41 +1059,37 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { | ... | @@ -1061,41 +1059,37 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 1061 | } | 1059 | } |
| 1062 | }, | 1060 | }, |
| 1063 | .uav_ref => |uav| { | 1061 | .uav_ref => |uav| { |
| 1064 | const wasm = cg.wasm; | 1062 | const zcu = cg.pt.zcu; |
| 1065 | const comp = wasm.base.comp; | ||
| 1066 | const is_obj = comp.config.output_mode == .Obj; | ||
| 1067 | const zcu = comp.zcu.?; | ||
| 1068 | const ip = &zcu.intern_pool; | 1063 | const ip = &zcu.intern_pool; |
| 1069 | if (ip.isFunctionType(ip.typeOf(uav.ip_index))) { | 1064 | assert(!ip.isFunctionType(ip.typeOf(uav.ip_index))); |
| 1070 | assert(uav.offset == 0); | 1065 | const gop = try cg.mir_uavs.getOrPut(cg.gpa, uav.ip_index); |
| 1071 | const owner_nav = ip.toFunc(uav.ip_index).owner_nav; | 1066 | const this_align: Alignment = a: { |
| 1072 | const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, owner_nav); | 1067 | if (uav.orig_ptr_ty == .none) break :a .none; |
| 1073 | if (!gop.found_existing) gop.value_ptr.* = {}; | 1068 | const ptr_type = ip.indexToKey(uav.orig_ptr_ty).ptr_type; |
| 1074 | try cg.addInst(.{ | 1069 | const this_align = ptr_type.flags.alignment; |
| 1075 | .tag = .func_ref, | 1070 | if (this_align == .none) break :a .none; |
| 1076 | .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) }, | 1071 | const abi_align = Type.fromInterned(ptr_type.child).abiAlignment(zcu); |
| 1077 | }); | 1072 | if (this_align.compare(.lte, abi_align)) break :a .none; |
| 1078 | } else if (uav.offset == 0) { | 1073 | break :a this_align; |
| 1074 | }; | ||
| 1075 | if (!gop.found_existing or | ||
| 1076 | gop.value_ptr.* == .none or | ||
| 1077 | (this_align != .none and this_align.compare(.gt, gop.value_ptr.*))) | ||
| 1078 | { | ||
| 1079 | gop.value_ptr.* = this_align; | ||
| 1080 | } | ||
| 1081 | if (uav.offset == 0) { | ||
| 1079 | try cg.addInst(.{ | 1082 | try cg.addInst(.{ |
| 1080 | .tag = .uav_ref, | 1083 | .tag = .uav_ref, |
| 1081 | .data = if (is_obj) .{ | 1084 | .data = .{ .ip_index = uav.ip_index }, |
| 1082 | .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty), | ||
| 1083 | } else .{ | ||
| 1084 | .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty), | ||
| 1085 | }, | ||
| 1086 | }); | 1085 | }); |
| 1087 | } else { | 1086 | } else { |
| 1088 | try cg.addInst(.{ | 1087 | try cg.addInst(.{ |
| 1089 | .tag = .uav_ref_off, | 1088 | .tag = .uav_ref_off, |
| 1090 | .data = .{ | 1089 | .data = .{ .payload = try cg.addExtra(@as(Mir.UavRefOff, .{ |
| 1091 | .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{ | 1090 | .value = uav.ip_index, |
| 1092 | .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty), | 1091 | .offset = uav.offset, |
| 1093 | .offset = uav.offset, | 1092 | })) }, |
| 1094 | }) else try cg.addExtra(Mir.UavRefOffExe{ | ||
| 1095 | .uav_exe = try wasm.refUavExe(uav.ip_index, uav.orig_ptr_ty), | ||
| 1096 | .offset = uav.offset, | ||
| 1097 | }), | ||
| 1098 | }, | ||
| 1099 | }); | 1093 | }); |
| 1100 | } | 1094 | } |
| 1101 | }, | 1095 | }, |
| ... | @@ -1157,106 +1151,12 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -1157,106 +1151,12 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 1157 | /// to use a zero-initialized local. | 1151 | /// to use a zero-initialized local. |
| 1158 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { | 1152 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 1159 | const zcu = cg.pt.zcu; | 1153 | const zcu = cg.pt.zcu; |
| 1160 | try cg.locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); | 1154 | try cg.mir_locals.append(cg.gpa, typeToValtype(ty, zcu, cg.target)); |
| 1161 | const initial_index = cg.local_index; | 1155 | const initial_index = cg.local_index; |
| 1162 | cg.local_index += 1; | 1156 | cg.local_index += 1; |
| 1163 | return .{ .local = .{ .value = initial_index, .references = 1 } }; | 1157 | return .{ .local = .{ .value = initial_index, .references = 1 } }; |
| 1164 | } | 1158 | } |
| 1165 | 1159 | ||
| 1166 | pub const Function = extern struct { | ||
| 1167 | /// Index into `Wasm.mir_instructions`. | ||
| 1168 | mir_off: u32, | ||
| 1169 | /// This is unused except for as a safety slice bound and could be removed. | ||
| 1170 | mir_len: u32, | ||
| 1171 | /// Index into `Wasm.mir_extra`. | ||
| 1172 | mir_extra_off: u32, | ||
| 1173 | /// This is unused except for as a safety slice bound and could be removed. | ||
| 1174 | mir_extra_len: u32, | ||
| 1175 | locals_off: u32, | ||
| 1176 | locals_len: u32, | ||
| 1177 | prologue: Prologue, | ||
| 1178 | |||
| 1179 | pub const Prologue = extern struct { | ||
| 1180 | flags: Flags, | ||
| 1181 | sp_local: u32, | ||
| 1182 | stack_size: u32, | ||
| 1183 | bottom_stack_local: u32, | ||
| 1184 | |||
| 1185 | pub const Flags = packed struct(u32) { | ||
| 1186 | stack_alignment: Alignment, | ||
| 1187 | padding: u26 = 0, | ||
| 1188 | }; | ||
| 1189 | |||
| 1190 | pub const none: Prologue = .{ | ||
| 1191 | .sp_local = 0, | ||
| 1192 | .flags = .{ .stack_alignment = .none }, | ||
| 1193 | .stack_size = 0, | ||
| 1194 | .bottom_stack_local = 0, | ||
| 1195 | }; | ||
| 1196 | |||
| 1197 | pub fn isNone(p: *const Prologue) bool { | ||
| 1198 | return p.flags.stack_alignment != .none; | ||
| 1199 | } | ||
| 1200 | }; | ||
| 1201 | |||
| 1202 | pub fn lower(f: *Function, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { | ||
| 1203 | const gpa = wasm.base.comp.gpa; | ||
| 1204 | |||
| 1205 | // Write the locals in the prologue of the function body. | ||
| 1206 | const locals = wasm.all_zcu_locals.items[f.locals_off..][0..f.locals_len]; | ||
| 1207 | try code.ensureUnusedCapacity(gpa, 5 + locals.len * 6 + 38); | ||
| 1208 | |||
| 1209 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(locals.len))) catch unreachable; | ||
| 1210 | for (locals) |local| { | ||
| 1211 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable; | ||
| 1212 | code.appendAssumeCapacity(@intFromEnum(local)); | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | // Stack management section of function prologue. | ||
| 1216 | const stack_alignment = f.prologue.flags.stack_alignment; | ||
| 1217 | if (stack_alignment.toByteUnits()) |align_bytes| { | ||
| 1218 | const sp_global: Wasm.GlobalIndex = .stack_pointer; | ||
| 1219 | // load stack pointer | ||
| 1220 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get)); | ||
| 1221 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; | ||
| 1222 | // store stack pointer so we can restore it when we return from the function | ||
| 1223 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); | ||
| 1224 | leb.writeUleb128(code.fixedWriter(), f.prologue.sp_local) catch unreachable; | ||
| 1225 | // get the total stack size | ||
| 1226 | const aligned_stack: i32 = @intCast(stack_alignment.forward(f.prologue.stack_size)); | ||
| 1227 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1228 | leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable; | ||
| 1229 | // subtract it from the current stack pointer | ||
| 1230 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub)); | ||
| 1231 | // Get negative stack alignment | ||
| 1232 | const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; | ||
| 1233 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 1234 | leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable; | ||
| 1235 | // Bitwise-and the value to get the new stack pointer to ensure the | ||
| 1236 | // pointers are aligned with the abi alignment. | ||
| 1237 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and)); | ||
| 1238 | // The bottom will be used to calculate all stack pointer offsets. | ||
| 1239 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); | ||
| 1240 | leb.writeUleb128(code.fixedWriter(), f.prologue.bottom_stack_local) catch unreachable; | ||
| 1241 | // Store the current stack pointer value into the global stack pointer so other function calls will | ||
| 1242 | // start from this value instead and not overwrite the current stack. | ||
| 1243 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); | ||
| 1244 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | var emit: Emit = .{ | ||
| 1248 | .mir = .{ | ||
| 1249 | .instruction_tags = wasm.mir_instructions.items(.tag)[f.mir_off..][0..f.mir_len], | ||
| 1250 | .instruction_datas = wasm.mir_instructions.items(.data)[f.mir_off..][0..f.mir_len], | ||
| 1251 | .extra = wasm.mir_extra.items[f.mir_extra_off..][0..f.mir_extra_len], | ||
| 1252 | }, | ||
| 1253 | .wasm = wasm, | ||
| 1254 | .code = code, | ||
| 1255 | }; | ||
| 1256 | try emit.lowerToCode(); | ||
| 1257 | } | ||
| 1258 | }; | ||
| 1259 | |||
| 1260 | pub const Error = error{ | 1160 | pub const Error = error{ |
| 1261 | OutOfMemory, | 1161 | OutOfMemory, |
| 1262 | /// Compiler was asked to operate on a number larger than supported. | 1162 | /// Compiler was asked to operate on a number larger than supported. |
| ... | @@ -1265,13 +1165,16 @@ pub const Error = error{ | ... | @@ -1265,13 +1165,16 @@ pub const Error = error{ |
| 1265 | CodegenFail, | 1165 | CodegenFail, |
| 1266 | }; | 1166 | }; |
| 1267 | 1167 | ||
| 1268 | pub fn function( | 1168 | pub fn generate( |
| 1269 | wasm: *Wasm, | 1169 | bin_file: *link.File, |
| 1270 | pt: Zcu.PerThread, | 1170 | pt: Zcu.PerThread, |
| 1171 | src_loc: Zcu.LazySrcLoc, | ||
| 1271 | func_index: InternPool.Index, | 1172 | func_index: InternPool.Index, |
| 1272 | air: Air, | 1173 | air: *const Air, |
| 1273 | liveness: Air.Liveness, | 1174 | liveness: *const Air.Liveness, |
| 1274 | ) Error!Function { | 1175 | ) Error!Mir { |
| 1176 | _ = src_loc; | ||
| 1177 | _ = bin_file; | ||
| 1275 | const zcu = pt.zcu; | 1178 | const zcu = pt.zcu; |
| 1276 | const gpa = zcu.gpa; | 1179 | const gpa = zcu.gpa; |
| 1277 | const cg = zcu.funcInfo(func_index); | 1180 | const cg = zcu.funcInfo(func_index); |
| ... | @@ -1279,10 +1182,8 @@ pub fn function( | ... | @@ -1279,10 +1182,8 @@ pub fn function( |
| 1279 | const target = &file_scope.mod.?.resolved_target.result; | 1182 | const target = &file_scope.mod.?.resolved_target.result; |
| 1280 | const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu); | 1183 | const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu); |
| 1281 | const fn_info = zcu.typeToFunc(fn_ty).?; | 1184 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 1282 | const ip = &zcu.intern_pool; | 1185 | const ret_ty: Type = .fromInterned(fn_info.return_type); |
| 1283 | const fn_ty_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target); | 1186 | const any_returns = !firstParamSRet(fn_info.cc, ret_ty, zcu, target) and ret_ty.hasRuntimeBitsIgnoreComptime(zcu); |
| 1284 | const returns = fn_ty_index.ptr(wasm).returns.slice(wasm); | ||
| 1285 | const any_returns = returns.len != 0; | ||
| 1286 | 1187 | ||
| 1287 | var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target); | 1188 | var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target); |
| 1288 | defer cc_result.deinit(gpa); | 1189 | defer cc_result.deinit(gpa); |
| ... | @@ -1290,8 +1191,8 @@ pub fn function( | ... | @@ -1290,8 +1191,8 @@ pub fn function( |
| 1290 | var code_gen: CodeGen = .{ | 1191 | var code_gen: CodeGen = .{ |
| 1291 | .gpa = gpa, | 1192 | .gpa = gpa, |
| 1292 | .pt = pt, | 1193 | .pt = pt, |
| 1293 | .air = air, | 1194 | .air = air.*, |
| 1294 | .liveness = liveness, | 1195 | .liveness = liveness.*, |
| 1295 | .owner_nav = cg.owner_nav, | 1196 | .owner_nav = cg.owner_nav, |
| 1296 | .target = target, | 1197 | .target = target, |
| 1297 | .ptr_size = switch (target.cpu.arch) { | 1198 | .ptr_size = switch (target.cpu.arch) { |
| ... | @@ -1299,31 +1200,33 @@ pub fn function( | ... | @@ -1299,31 +1200,33 @@ pub fn function( |
| 1299 | .wasm64 => .wasm64, | 1200 | .wasm64 => .wasm64, |
| 1300 | else => unreachable, | 1201 | else => unreachable, |
| 1301 | }, | 1202 | }, |
| 1302 | .wasm = wasm, | ||
| 1303 | .func_index = func_index, | 1203 | .func_index = func_index, |
| 1304 | .args = cc_result.args, | 1204 | .args = cc_result.args, |
| 1305 | .return_value = cc_result.return_value, | 1205 | .return_value = cc_result.return_value, |
| 1306 | .local_index = cc_result.local_index, | 1206 | .local_index = cc_result.local_index, |
| 1307 | .mir_instructions = &wasm.mir_instructions, | 1207 | .mir_instructions = .empty, |
| 1308 | .mir_extra = &wasm.mir_extra, | 1208 | .mir_extra = .empty, |
| 1309 | .locals = &wasm.all_zcu_locals, | 1209 | .mir_locals = .empty, |
| 1310 | .start_mir_extra_off = @intCast(wasm.mir_extra.items.len), | 1210 | .mir_uavs = .empty, |
| 1311 | .start_locals_off = @intCast(wasm.all_zcu_locals.items.len), | 1211 | .mir_indirect_function_set = .empty, |
| 1212 | .mir_func_tys = .empty, | ||
| 1213 | .error_name_table_ref_count = 0, | ||
| 1312 | }; | 1214 | }; |
| 1313 | defer code_gen.deinit(); | 1215 | defer code_gen.deinit(); |
| 1314 | 1216 | ||
| 1315 | return functionInner(&code_gen, any_returns) catch |err| switch (err) { | 1217 | try code_gen.mir_func_tys.putNoClobber(gpa, fn_ty.toIntern(), {}); |
| 1316 | error.CodegenFail => return error.CodegenFail, | 1218 | |
| 1219 | return generateInner(&code_gen, any_returns) catch |err| switch (err) { | ||
| 1220 | error.CodegenFail, | ||
| 1221 | error.OutOfMemory, | ||
| 1222 | error.Overflow, | ||
| 1223 | => |e| return e, | ||
| 1317 | else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), | 1224 | else => |e| return code_gen.fail("failed to generate function: {s}", .{@errorName(e)}), |
| 1318 | }; | 1225 | }; |
| 1319 | } | 1226 | } |
| 1320 | 1227 | ||
| 1321 | fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { | 1228 | fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { |
| 1322 | const wasm = cg.wasm; | ||
| 1323 | const zcu = cg.pt.zcu; | 1229 | const zcu = cg.pt.zcu; |
| 1324 | |||
| 1325 | const start_mir_off: u32 = @intCast(wasm.mir_instructions.len); | ||
| 1326 | |||
| 1327 | try cg.branches.append(cg.gpa, .{}); | 1230 | try cg.branches.append(cg.gpa, .{}); |
| 1328 | // clean up outer branch | 1231 | // clean up outer branch |
| 1329 | defer { | 1232 | defer { |
| ... | @@ -1347,20 +1250,25 @@ fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { | ... | @@ -1347,20 +1250,25 @@ fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { |
| 1347 | try cg.addTag(.end); | 1250 | try cg.addTag(.end); |
| 1348 | try cg.addTag(.dbg_epilogue_begin); | 1251 | try cg.addTag(.dbg_epilogue_begin); |
| 1349 | 1252 | ||
| 1350 | return .{ | 1253 | var mir: Mir = .{ |
| 1351 | .mir_off = start_mir_off, | 1254 | .instructions = cg.mir_instructions.toOwnedSlice(), |
| 1352 | .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off), | 1255 | .extra = &.{}, // fallible so assigned after errdefer |
| 1353 | .mir_extra_off = cg.start_mir_extra_off, | 1256 | .locals = &.{}, // fallible so assigned after errdefer |
| 1354 | .mir_extra_len = cg.extraLen(), | ||
| 1355 | .locals_off = cg.start_locals_off, | ||
| 1356 | .locals_len = @intCast(wasm.all_zcu_locals.items.len - cg.start_locals_off), | ||
| 1357 | .prologue = if (cg.initial_stack_value == .none) .none else .{ | 1257 | .prologue = if (cg.initial_stack_value == .none) .none else .{ |
| 1358 | .sp_local = cg.initial_stack_value.local.value, | 1258 | .sp_local = cg.initial_stack_value.local.value, |
| 1359 | .flags = .{ .stack_alignment = cg.stack_alignment }, | 1259 | .flags = .{ .stack_alignment = cg.stack_alignment }, |
| 1360 | .stack_size = cg.stack_size, | 1260 | .stack_size = cg.stack_size, |
| 1361 | .bottom_stack_local = cg.bottom_stack_value.local.value, | 1261 | .bottom_stack_local = cg.bottom_stack_value.local.value, |
| 1362 | }, | 1262 | }, |
| 1263 | .uavs = cg.mir_uavs.move(), | ||
| 1264 | .indirect_function_set = cg.mir_indirect_function_set.move(), | ||
| 1265 | .func_tys = cg.mir_func_tys.move(), | ||
| 1266 | .error_name_table_ref_count = cg.error_name_table_ref_count, | ||
| 1363 | }; | 1267 | }; |
| 1268 | errdefer mir.deinit(cg.gpa); | ||
| 1269 | mir.extra = try cg.mir_extra.toOwnedSlice(cg.gpa); | ||
| 1270 | mir.locals = try cg.mir_locals.toOwnedSlice(cg.gpa); | ||
| 1271 | return mir; | ||
| 1364 | } | 1272 | } |
| 1365 | 1273 | ||
| 1366 | const CallWValues = struct { | 1274 | const CallWValues = struct { |
| ... | @@ -2220,7 +2128,6 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2220,7 +2128,6 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2220 | } | 2128 | } |
| 2221 | 2129 | ||
| 2222 | fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { | 2130 | fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { |
| 2223 | const wasm = cg.wasm; | ||
| 2224 | if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{}); | 2131 | if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{}); |
| 2225 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; | 2132 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 2226 | const extra = cg.air.extraData(Air.Call, pl_op.payload); | 2133 | const extra = cg.air.extraData(Air.Call, pl_op.payload); |
| ... | @@ -2277,8 +2184,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie | ... | @@ -2277,8 +2184,11 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifie |
| 2277 | const operand = try cg.resolveInst(pl_op.operand); | 2184 | const operand = try cg.resolveInst(pl_op.operand); |
| 2278 | try cg.emitWValue(operand); | 2185 | try cg.emitWValue(operand); |
| 2279 | 2186 | ||
| 2280 | const fn_type_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), cg.target); | 2187 | try cg.mir_func_tys.put(cg.gpa, fn_ty.toIntern(), {}); |
| 2281 | try cg.addFuncTy(.call_indirect, fn_type_index); | 2188 | try cg.addInst(.{ |
| 2189 | .tag = .call_indirect, | ||
| 2190 | .data = .{ .ip_index = fn_ty.toIntern() }, | ||
| 2191 | }); | ||
| 2282 | } | 2192 | } |
| 2283 | 2193 | ||
| 2284 | const result_value = result_value: { | 2194 | const result_value = result_value: { |
| ... | @@ -2449,7 +2359,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr | ... | @@ -2449,7 +2359,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr |
| 2449 | try cg.emitWValue(lhs); | 2359 | try cg.emitWValue(lhs); |
| 2450 | try cg.lowerToStack(rhs); | 2360 | try cg.lowerToStack(rhs); |
| 2451 | // TODO: Add helper functions for simd opcodes | 2361 | // TODO: Add helper functions for simd opcodes |
| 2452 | const extra_index = cg.extraLen(); | 2362 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 2453 | // stores as := opcode, offset, alignment (opcode::memarg) | 2363 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 2454 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ | 2364 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 2455 | @intFromEnum(std.wasm.SimdOpcode.v128_store), | 2365 | @intFromEnum(std.wasm.SimdOpcode.v128_store), |
| ... | @@ -2574,7 +2484,7 @@ fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue | ... | @@ -2574,7 +2484,7 @@ fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue |
| 2574 | 2484 | ||
| 2575 | if (ty.zigTypeTag(zcu) == .vector) { | 2485 | if (ty.zigTypeTag(zcu) == .vector) { |
| 2576 | // TODO: Add helper functions for simd opcodes | 2486 | // TODO: Add helper functions for simd opcodes |
| 2577 | const extra_index = cg.extraLen(); | 2487 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 2578 | // stores as := opcode, offset, alignment (opcode::memarg) | 2488 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 2579 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ | 2489 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 2580 | @intFromEnum(std.wasm.SimdOpcode.v128_load), | 2490 | @intFromEnum(std.wasm.SimdOpcode.v128_load), |
| ... | @@ -4971,7 +4881,7 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4971,7 +4881,7 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4971 | 4881 | ||
| 4972 | try cg.emitWValue(array); | 4882 | try cg.emitWValue(array); |
| 4973 | 4883 | ||
| 4974 | const extra_index = cg.extraLen(); | 4884 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 4975 | try cg.mir_extra.appendSlice(cg.gpa, &operands); | 4885 | try cg.mir_extra.appendSlice(cg.gpa, &operands); |
| 4976 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 4886 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 4977 | 4887 | ||
| ... | @@ -5123,7 +5033,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5123,7 +5033,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5123 | else => break :blk, // Cannot make use of simd-instructions | 5033 | else => break :blk, // Cannot make use of simd-instructions |
| 5124 | }; | 5034 | }; |
| 5125 | try cg.emitWValue(operand); | 5035 | try cg.emitWValue(operand); |
| 5126 | const extra_index: u32 = cg.extraLen(); | 5036 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 5127 | // stores as := opcode, offset, alignment (opcode::memarg) | 5037 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 5128 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ | 5038 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 5129 | opcode, | 5039 | opcode, |
| ... | @@ -5142,7 +5052,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5142,7 +5052,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5142 | else => break :blk, // Cannot make use of simd-instructions | 5052 | else => break :blk, // Cannot make use of simd-instructions |
| 5143 | }; | 5053 | }; |
| 5144 | try cg.emitWValue(operand); | 5054 | try cg.emitWValue(operand); |
| 5145 | const extra_index = cg.extraLen(); | 5055 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 5146 | try cg.mir_extra.append(cg.gpa, opcode); | 5056 | try cg.mir_extra.append(cg.gpa, opcode); |
| 5147 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); | 5057 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 5148 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); | 5058 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| ... | @@ -5246,7 +5156,7 @@ fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -5246,7 +5156,7 @@ fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5246 | } | 5156 | } |
| 5247 | try cg.emitWValue(operand_a); | 5157 | try cg.emitWValue(operand_a); |
| 5248 | try cg.emitWValue(operand_b); | 5158 | try cg.emitWValue(operand_b); |
| 5249 | const extra_index = cg.extraLen(); | 5159 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); |
| 5250 | try cg.mir_extra.appendSlice(cg.gpa, &.{ | 5160 | try cg.mir_extra.appendSlice(cg.gpa, &.{ |
| 5251 | @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle), | 5161 | @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle), |
| 5252 | @bitCast(lane_map[0..4].*), | 5162 | @bitCast(lane_map[0..4].*), |
| ... | @@ -6016,9 +5926,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -6016,9 +5926,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6016 | const name_ty = Type.slice_const_u8_sentinel_0; | 5926 | const name_ty = Type.slice_const_u8_sentinel_0; |
| 6017 | const abi_size = name_ty.abiSize(pt.zcu); | 5927 | const abi_size = name_ty.abiSize(pt.zcu); |
| 6018 | 5928 | ||
| 6019 | cg.wasm.error_name_table_ref_count += 1; | ||
| 6020 | |||
| 6021 | // Lowers to a i32.const or i64.const with the error table memory address. | 5929 | // Lowers to a i32.const or i64.const with the error table memory address. |
| 5930 | cg.error_name_table_ref_count += 1; | ||
| 6022 | try cg.addTag(.error_name_table_ref); | 5931 | try cg.addTag(.error_name_table_ref); |
| 6023 | try cg.emitWValue(operand); | 5932 | try cg.emitWValue(operand); |
| 6024 | switch (cg.ptr_size) { | 5933 | switch (cg.ptr_size) { |
| ... | @@ -6046,7 +5955,7 @@ fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerErr | ... | @@ -6046,7 +5955,7 @@ fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerErr |
| 6046 | 5955 | ||
| 6047 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits | 5956 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits |
| 6048 | fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue { | 5957 | fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 6049 | const zcu = cg.wasm.base.comp.zcu.?; | 5958 | const zcu = cg.pt.zcu; |
| 6050 | const int_info = ty.intInfo(zcu); | 5959 | const int_info = ty.intInfo(zcu); |
| 6051 | const wasm_bits = toWasmBits(int_info.bits) orelse { | 5960 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6052 | return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits}); | 5961 | return cg.fail("TODO: Implement intZeroValue for integer bitsize: {d}", .{int_info.bits}); |
| ... | @@ -7673,7 +7582,3 @@ fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic { | ... | @@ -7673,7 +7582,3 @@ fn floatCmpIntrinsic(op: std.math.CompareOperator, bits: u16) Mir.Intrinsic { |
| 7673 | }, | 7582 | }, |
| 7674 | }; | 7583 | }; |
| 7675 | } | 7584 | } |
| 7676 | |||
| 7677 | fn extraLen(cg: *const CodeGen) u32 { | ||
| 7678 | return @intCast(cg.mir_extra.items.len - cg.start_mir_extra_off); | ||
| 7679 | } |
src/arch/wasm/Emit.zig+25-14| ... | @@ -31,8 +31,8 @@ pub fn lowerToCode(emit: *Emit) Error!void { | ... | @@ -31,8 +31,8 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 31 | const target = &comp.root_mod.resolved_target.result; | 31 | const target = &comp.root_mod.resolved_target.result; |
| 32 | const is_wasm32 = target.cpu.arch == .wasm32; | 32 | const is_wasm32 = target.cpu.arch == .wasm32; |
| 33 | 33 | ||
| 34 | const tags = mir.instruction_tags; | 34 | const tags = mir.instructions.items(.tag); |
| 35 | const datas = mir.instruction_datas; | 35 | const datas = mir.instructions.items(.data); |
| 36 | var inst: u32 = 0; | 36 | var inst: u32 = 0; |
| 37 | 37 | ||
| 38 | loop: switch (tags[inst]) { | 38 | loop: switch (tags[inst]) { |
| ... | @@ -50,18 +50,19 @@ pub fn lowerToCode(emit: *Emit) Error!void { | ... | @@ -50,18 +50,19 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 50 | }, | 50 | }, |
| 51 | .uav_ref => { | 51 | .uav_ref => { |
| 52 | if (is_obj) { | 52 | if (is_obj) { |
| 53 | try uavRefOffObj(wasm, code, .{ .uav_obj = datas[inst].uav_obj, .offset = 0 }, is_wasm32); | 53 | try uavRefObj(wasm, code, datas[inst].ip_index, 0, is_wasm32); |
| 54 | } else { | 54 | } else { |
| 55 | try uavRefOffExe(wasm, code, .{ .uav_exe = datas[inst].uav_exe, .offset = 0 }, is_wasm32); | 55 | try uavRefExe(wasm, code, datas[inst].ip_index, 0, is_wasm32); |
| 56 | } | 56 | } |
| 57 | inst += 1; | 57 | inst += 1; |
| 58 | continue :loop tags[inst]; | 58 | continue :loop tags[inst]; |
| 59 | }, | 59 | }, |
| 60 | .uav_ref_off => { | 60 | .uav_ref_off => { |
| 61 | const extra = mir.extraData(Mir.UavRefOff, datas[inst].payload).data; | ||
| 61 | if (is_obj) { | 62 | if (is_obj) { |
| 62 | try uavRefOffObj(wasm, code, mir.extraData(Mir.UavRefOffObj, datas[inst].payload).data, is_wasm32); | 63 | try uavRefObj(wasm, code, extra.value, extra.offset, is_wasm32); |
| 63 | } else { | 64 | } else { |
| 64 | try uavRefOffExe(wasm, code, mir.extraData(Mir.UavRefOffExe, datas[inst].payload).data, is_wasm32); | 65 | try uavRefExe(wasm, code, extra.value, extra.offset, is_wasm32); |
| 65 | } | 66 | } |
| 66 | inst += 1; | 67 | inst += 1; |
| 67 | continue :loop tags[inst]; | 68 | continue :loop tags[inst]; |
| ... | @@ -77,11 +78,14 @@ pub fn lowerToCode(emit: *Emit) Error!void { | ... | @@ -77,11 +78,14 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 77 | continue :loop tags[inst]; | 78 | continue :loop tags[inst]; |
| 78 | }, | 79 | }, |
| 79 | .func_ref => { | 80 | .func_ref => { |
| 81 | const indirect_func_idx: Wasm.ZcuIndirectFunctionSetIndex = @enumFromInt( | ||
| 82 | wasm.zcu_indirect_function_set.getIndex(datas[inst].nav_index).?, | ||
| 83 | ); | ||
| 80 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | 84 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 81 | if (is_obj) { | 85 | if (is_obj) { |
| 82 | @panic("TODO"); | 86 | @panic("TODO"); |
| 83 | } else { | 87 | } else { |
| 84 | leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(datas[inst].indirect_function_table_index)) catch unreachable; | 88 | leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(indirect_func_idx)) catch unreachable; |
| 85 | } | 89 | } |
| 86 | inst += 1; | 90 | inst += 1; |
| 87 | continue :loop tags[inst]; | 91 | continue :loop tags[inst]; |
| ... | @@ -101,6 +105,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { | ... | @@ -101,6 +105,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 101 | continue :loop tags[inst]; | 105 | continue :loop tags[inst]; |
| 102 | }, | 106 | }, |
| 103 | .error_name_table_ref => { | 107 | .error_name_table_ref => { |
| 108 | wasm.error_name_table_ref_count += 1; | ||
| 104 | try code.ensureUnusedCapacity(gpa, 11); | 109 | try code.ensureUnusedCapacity(gpa, 11); |
| 105 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; | 110 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; |
| 106 | code.appendAssumeCapacity(@intFromEnum(opcode)); | 111 | code.appendAssumeCapacity(@intFromEnum(opcode)); |
| ... | @@ -176,7 +181,13 @@ pub fn lowerToCode(emit: *Emit) Error!void { | ... | @@ -176,7 +181,13 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 176 | 181 | ||
| 177 | .call_indirect => { | 182 | .call_indirect => { |
| 178 | try code.ensureUnusedCapacity(gpa, 11); | 183 | try code.ensureUnusedCapacity(gpa, 11); |
| 179 | const func_ty_index = datas[inst].func_ty; | 184 | const fn_info = comp.zcu.?.typeToFunc(.fromInterned(datas[inst].ip_index)).?; |
| 185 | const func_ty_index = wasm.getExistingFunctionType( | ||
| 186 | fn_info.cc, | ||
| 187 | fn_info.param_types.get(&comp.zcu.?.intern_pool), | ||
| 188 | .fromInterned(fn_info.return_type), | ||
| 189 | target, | ||
| 190 | ).?; | ||
| 180 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect)); | 191 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect)); |
| 181 | if (is_obj) { | 192 | if (is_obj) { |
| 182 | try wasm.out_relocs.append(gpa, .{ | 193 | try wasm.out_relocs.append(gpa, .{ |
| ... | @@ -912,7 +923,7 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void { | ... | @@ -912,7 +923,7 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void { |
| 912 | leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable; | 923 | leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable; |
| 913 | } | 924 | } |
| 914 | 925 | ||
| 915 | fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffObj, is_wasm32: bool) !void { | 926 | fn uavRefObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void { |
| 916 | const comp = wasm.base.comp; | 927 | const comp = wasm.base.comp; |
| 917 | const gpa = comp.gpa; | 928 | const gpa = comp.gpa; |
| 918 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; | 929 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; |
| ... | @@ -922,14 +933,14 @@ fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef | ... | @@ -922,14 +933,14 @@ fn uavRefOffObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef |
| 922 | 933 | ||
| 923 | try wasm.out_relocs.append(gpa, .{ | 934 | try wasm.out_relocs.append(gpa, .{ |
| 924 | .offset = @intCast(code.items.len), | 935 | .offset = @intCast(code.items.len), |
| 925 | .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(data.uav_obj.key(wasm).*) }, | 936 | .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(value) }, |
| 926 | .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, | 937 | .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, |
| 927 | .addend = data.offset, | 938 | .addend = offset, |
| 928 | }); | 939 | }); |
| 929 | code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); | 940 | code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10); |
| 930 | } | 941 | } |
| 931 | 942 | ||
| 932 | fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRefOffExe, is_wasm32: bool) !void { | 943 | fn uavRefExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void { |
| 933 | const comp = wasm.base.comp; | 944 | const comp = wasm.base.comp; |
| 934 | const gpa = comp.gpa; | 945 | const gpa = comp.gpa; |
| 935 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; | 946 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; |
| ... | @@ -937,8 +948,8 @@ fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef | ... | @@ -937,8 +948,8 @@ fn uavRefOffExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.UavRef |
| 937 | try code.ensureUnusedCapacity(gpa, 11); | 948 | try code.ensureUnusedCapacity(gpa, 11); |
| 938 | code.appendAssumeCapacity(@intFromEnum(opcode)); | 949 | code.appendAssumeCapacity(@intFromEnum(opcode)); |
| 939 | 950 | ||
| 940 | const addr = wasm.uavAddr(data.uav_exe); | 951 | const addr = wasm.uavAddr(value); |
| 941 | leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable; | 952 | leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + offset))) catch unreachable; |
| 942 | } | 953 | } |
| 943 | 954 | ||
| 944 | fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void { | 955 | fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void { |
src/arch/wasm/Mir.zig+104-19| ... | @@ -9,16 +9,53 @@ | ... | @@ -9,16 +9,53 @@ |
| 9 | const Mir = @This(); | 9 | const Mir = @This(); |
| 10 | const InternPool = @import("../../InternPool.zig"); | 10 | const InternPool = @import("../../InternPool.zig"); |
| 11 | const Wasm = @import("../../link/Wasm.zig"); | 11 | const Wasm = @import("../../link/Wasm.zig"); |
| 12 | const Emit = @import("Emit.zig"); | ||
| 13 | const Alignment = InternPool.Alignment; | ||
| 12 | 14 | ||
| 13 | const builtin = @import("builtin"); | 15 | const builtin = @import("builtin"); |
| 14 | const std = @import("std"); | 16 | const std = @import("std"); |
| 15 | const assert = std.debug.assert; | 17 | const assert = std.debug.assert; |
| 18 | const leb = std.leb; | ||
| 16 | 19 | ||
| 17 | instruction_tags: []const Inst.Tag, | 20 | instructions: std.MultiArrayList(Inst).Slice, |
| 18 | instruction_datas: []const Inst.Data, | ||
| 19 | /// A slice of indexes where the meaning of the data is determined by the | 21 | /// A slice of indexes where the meaning of the data is determined by the |
| 20 | /// `Inst.Tag` value. | 22 | /// `Inst.Tag` value. |
| 21 | extra: []const u32, | 23 | extra: []const u32, |
| 24 | locals: []const std.wasm.Valtype, | ||
| 25 | prologue: Prologue, | ||
| 26 | |||
| 27 | /// Not directly used by `Emit`, but the linker needs this to merge it with a global set. | ||
| 28 | /// Value is the explicit alignment if greater than natural alignment, `.none` otherwise. | ||
| 29 | uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), | ||
| 30 | /// Not directly used by `Emit`, but the linker needs this to merge it with a global set. | ||
| 31 | indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void), | ||
| 32 | /// Not directly used by `Emit`, but the linker needs this to ensure these types are interned. | ||
| 33 | func_tys: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), | ||
| 34 | /// Not directly used by `Emit`, but the linker needs this to add it to its own refcount. | ||
| 35 | error_name_table_ref_count: u32, | ||
| 36 | |||
| 37 | pub const Prologue = extern struct { | ||
| 38 | flags: Flags, | ||
| 39 | sp_local: u32, | ||
| 40 | stack_size: u32, | ||
| 41 | bottom_stack_local: u32, | ||
| 42 | |||
| 43 | pub const Flags = packed struct(u32) { | ||
| 44 | stack_alignment: Alignment, | ||
| 45 | padding: u26 = 0, | ||
| 46 | }; | ||
| 47 | |||
| 48 | pub const none: Prologue = .{ | ||
| 49 | .sp_local = 0, | ||
| 50 | .flags = .{ .stack_alignment = .none }, | ||
| 51 | .stack_size = 0, | ||
| 52 | .bottom_stack_local = 0, | ||
| 53 | }; | ||
| 54 | |||
| 55 | pub fn isNone(p: *const Prologue) bool { | ||
| 56 | return p.flags.stack_alignment != .none; | ||
| 57 | } | ||
| 58 | }; | ||
| 22 | 59 | ||
| 23 | pub const Inst = struct { | 60 | pub const Inst = struct { |
| 24 | /// The opcode that represents this instruction | 61 | /// The opcode that represents this instruction |
| ... | @@ -80,7 +117,7 @@ pub const Inst = struct { | ... | @@ -80,7 +117,7 @@ pub const Inst = struct { |
| 80 | /// Lowers to an i32_const which is the index of the function in the | 117 | /// Lowers to an i32_const which is the index of the function in the |
| 81 | /// table section. | 118 | /// table section. |
| 82 | /// | 119 | /// |
| 83 | /// Uses `indirect_function_table_index`. | 120 | /// Uses `nav_index`. |
| 84 | func_ref, | 121 | func_ref, |
| 85 | /// Inserts debug information about the current line and column | 122 | /// Inserts debug information about the current line and column |
| 86 | /// of the source code | 123 | /// of the source code |
| ... | @@ -123,7 +160,7 @@ pub const Inst = struct { | ... | @@ -123,7 +160,7 @@ pub const Inst = struct { |
| 123 | /// Calls a function pointer by its function signature | 160 | /// Calls a function pointer by its function signature |
| 124 | /// and index into the function table. | 161 | /// and index into the function table. |
| 125 | /// | 162 | /// |
| 126 | /// Uses `func_ty` | 163 | /// Uses `ip_index`; the `InternPool.Index` is the function type. |
| 127 | call_indirect, | 164 | call_indirect, |
| 128 | /// Calls a function by its index. | 165 | /// Calls a function by its index. |
| 129 | /// | 166 | /// |
| ... | @@ -611,11 +648,7 @@ pub const Inst = struct { | ... | @@ -611,11 +648,7 @@ pub const Inst = struct { |
| 611 | 648 | ||
| 612 | ip_index: InternPool.Index, | 649 | ip_index: InternPool.Index, |
| 613 | nav_index: InternPool.Nav.Index, | 650 | nav_index: InternPool.Nav.Index, |
| 614 | func_ty: Wasm.FunctionType.Index, | ||
| 615 | intrinsic: Intrinsic, | 651 | intrinsic: Intrinsic, |
| 616 | uav_obj: Wasm.UavsObjIndex, | ||
| 617 | uav_exe: Wasm.UavsExeIndex, | ||
| 618 | indirect_function_table_index: Wasm.ZcuIndirectFunctionSetIndex, | ||
| 619 | 652 | ||
| 620 | comptime { | 653 | comptime { |
| 621 | switch (builtin.mode) { | 654 | switch (builtin.mode) { |
| ... | @@ -626,10 +659,66 @@ pub const Inst = struct { | ... | @@ -626,10 +659,66 @@ pub const Inst = struct { |
| 626 | }; | 659 | }; |
| 627 | }; | 660 | }; |
| 628 | 661 | ||
| 629 | pub fn deinit(self: *Mir, gpa: std.mem.Allocator) void { | 662 | pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { |
| 630 | self.instructions.deinit(gpa); | 663 | mir.instructions.deinit(gpa); |
| 631 | gpa.free(self.extra); | 664 | gpa.free(mir.extra); |
| 632 | self.* = undefined; | 665 | gpa.free(mir.locals); |
| 666 | mir.uavs.deinit(gpa); | ||
| 667 | mir.indirect_function_set.deinit(gpa); | ||
| 668 | mir.func_tys.deinit(gpa); | ||
| 669 | mir.* = undefined; | ||
| 670 | } | ||
| 671 | |||
| 672 | pub fn lower(mir: *const Mir, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) std.mem.Allocator.Error!void { | ||
| 673 | const gpa = wasm.base.comp.gpa; | ||
| 674 | |||
| 675 | // Write the locals in the prologue of the function body. | ||
| 676 | try code.ensureUnusedCapacity(gpa, 5 + mir.locals.len * 6 + 38); | ||
| 677 | |||
| 678 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(mir.locals.len))) catch unreachable; | ||
| 679 | for (mir.locals) |local| { | ||
| 680 | std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable; | ||
| 681 | code.appendAssumeCapacity(@intFromEnum(local)); | ||
| 682 | } | ||
| 683 | |||
| 684 | // Stack management section of function prologue. | ||
| 685 | const stack_alignment = mir.prologue.flags.stack_alignment; | ||
| 686 | if (stack_alignment.toByteUnits()) |align_bytes| { | ||
| 687 | const sp_global: Wasm.GlobalIndex = .stack_pointer; | ||
| 688 | // load stack pointer | ||
| 689 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get)); | ||
| 690 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; | ||
| 691 | // store stack pointer so we can restore it when we return from the function | ||
| 692 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); | ||
| 693 | leb.writeUleb128(code.fixedWriter(), mir.prologue.sp_local) catch unreachable; | ||
| 694 | // get the total stack size | ||
| 695 | const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size)); | ||
| 696 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 697 | leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable; | ||
| 698 | // subtract it from the current stack pointer | ||
| 699 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub)); | ||
| 700 | // Get negative stack alignment | ||
| 701 | const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1; | ||
| 702 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); | ||
| 703 | leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable; | ||
| 704 | // Bitwise-and the value to get the new stack pointer to ensure the | ||
| 705 | // pointers are aligned with the abi alignment. | ||
| 706 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and)); | ||
| 707 | // The bottom will be used to calculate all stack pointer offsets. | ||
| 708 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee)); | ||
| 709 | leb.writeUleb128(code.fixedWriter(), mir.prologue.bottom_stack_local) catch unreachable; | ||
| 710 | // Store the current stack pointer value into the global stack pointer so other function calls will | ||
| 711 | // start from this value instead and not overwrite the current stack. | ||
| 712 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set)); | ||
| 713 | std.leb.writeULEB128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable; | ||
| 714 | } | ||
| 715 | |||
| 716 | var emit: Emit = .{ | ||
| 717 | .mir = mir.*, | ||
| 718 | .wasm = wasm, | ||
| 719 | .code = code, | ||
| 720 | }; | ||
| 721 | try emit.lowerToCode(); | ||
| 633 | } | 722 | } |
| 634 | 723 | ||
| 635 | pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } { | 724 | pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data: T, end: usize } { |
| ... | @@ -643,6 +732,7 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data | ... | @@ -643,6 +732,7 @@ pub fn extraData(self: *const Mir, comptime T: type, index: usize) struct { data |
| 643 | Wasm.UavsObjIndex, | 732 | Wasm.UavsObjIndex, |
| 644 | Wasm.UavsExeIndex, | 733 | Wasm.UavsExeIndex, |
| 645 | InternPool.Nav.Index, | 734 | InternPool.Nav.Index, |
| 735 | InternPool.Index, | ||
| 646 | => @enumFromInt(self.extra[i]), | 736 | => @enumFromInt(self.extra[i]), |
| 647 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), | 737 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), |
| 648 | }; | 738 | }; |
| ... | @@ -695,13 +785,8 @@ pub const MemArg = struct { | ... | @@ -695,13 +785,8 @@ pub const MemArg = struct { |
| 695 | alignment: u32, | 785 | alignment: u32, |
| 696 | }; | 786 | }; |
| 697 | 787 | ||
| 698 | pub const UavRefOffObj = struct { | 788 | pub const UavRefOff = struct { |
| 699 | uav_obj: Wasm.UavsObjIndex, | 789 | value: InternPool.Index, |
| 700 | offset: i32, | ||
| 701 | }; | ||
| 702 | |||
| 703 | pub const UavRefOffExe = struct { | ||
| 704 | uav_exe: Wasm.UavsExeIndex, | ||
| 705 | offset: i32, | 790 | offset: i32, |
| 706 | }; | 791 | }; |
| 707 | 792 |
src/codegen.zig+3-16| ... | @@ -123,6 +123,7 @@ pub const AnyMir = union { | ... | @@ -123,6 +123,7 @@ pub const AnyMir = union { |
| 123 | .stage2_riscv64, | 123 | .stage2_riscv64, |
| 124 | .stage2_sparc64, | 124 | .stage2_sparc64, |
| 125 | .stage2_x86_64, | 125 | .stage2_x86_64, |
| 126 | .stage2_wasm, | ||
| 126 | .stage2_c, | 127 | .stage2_c, |
| 127 | => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa), | 128 | => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa), |
| 128 | } | 129 | } |
| ... | @@ -153,6 +154,7 @@ pub fn generateFunction( | ... | @@ -153,6 +154,7 @@ pub fn generateFunction( |
| 153 | .stage2_riscv64, | 154 | .stage2_riscv64, |
| 154 | .stage2_sparc64, | 155 | .stage2_sparc64, |
| 155 | .stage2_x86_64, | 156 | .stage2_x86_64, |
| 157 | .stage2_wasm, | ||
| 156 | .stage2_c, | 158 | .stage2_c, |
| 157 | => |backend| { | 159 | => |backend| { |
| 158 | dev.check(devFeatureForBackend(backend)); | 160 | dev.check(devFeatureForBackend(backend)); |
| ... | @@ -784,7 +786,6 @@ fn lowerUavRef( | ... | @@ -784,7 +786,6 @@ fn lowerUavRef( |
| 784 | const comp = lf.comp; | 786 | const comp = lf.comp; |
| 785 | const target = &comp.root_mod.resolved_target.result; | 787 | const target = &comp.root_mod.resolved_target.result; |
| 786 | const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); | 788 | const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); |
| 787 | const is_obj = comp.config.output_mode == .Obj; | ||
| 788 | const uav_val = uav.val; | 789 | const uav_val = uav.val; |
| 789 | const uav_ty = Type.fromInterned(ip.typeOf(uav_val)); | 790 | const uav_ty = Type.fromInterned(ip.typeOf(uav_val)); |
| 790 | const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; | 791 | const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; |
| ... | @@ -804,21 +805,7 @@ fn lowerUavRef( | ... | @@ -804,21 +805,7 @@ fn lowerUavRef( |
| 804 | dev.check(link.File.Tag.wasm.devFeature()); | 805 | dev.check(link.File.Tag.wasm.devFeature()); |
| 805 | const wasm = lf.cast(.wasm).?; | 806 | const wasm = lf.cast(.wasm).?; |
| 806 | assert(reloc_parent == .none); | 807 | assert(reloc_parent == .none); |
| 807 | if (is_obj) { | 808 | try wasm.addUavReloc(code.items.len, uav.val, uav.orig_ty, @intCast(offset)); |
| 808 | try wasm.out_relocs.append(gpa, .{ | ||
| 809 | .offset = @intCast(code.items.len), | ||
| 810 | .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav.val) }, | ||
| 811 | .tag = if (ptr_width_bytes == 4) .memory_addr_i32 else .memory_addr_i64, | ||
| 812 | .addend = @intCast(offset), | ||
| 813 | }); | ||
| 814 | } else { | ||
| 815 | try wasm.uav_fixups.ensureUnusedCapacity(gpa, 1); | ||
| 816 | wasm.uav_fixups.appendAssumeCapacity(.{ | ||
| 817 | .uavs_exe_index = try wasm.refUavExe(uav.val, uav.orig_ty), | ||
| 818 | .offset = @intCast(code.items.len), | ||
| 819 | .addend = @intCast(offset), | ||
| 820 | }); | ||
| 821 | } | ||
| 822 | code.appendNTimesAssumeCapacity(0, ptr_width_bytes); | 809 | code.appendNTimesAssumeCapacity(0, ptr_width_bytes); |
| 823 | return; | 810 | return; |
| 824 | }, | 811 | }, |
src/link.zig+1-2| ... | @@ -759,7 +759,6 @@ pub const File = struct { | ... | @@ -759,7 +759,6 @@ pub const File = struct { |
| 759 | switch (base.tag) { | 759 | switch (base.tag) { |
| 760 | .lld => unreachable, | 760 | .lld => unreachable, |
| 761 | inline else => |tag| { | 761 | inline else => |tag| { |
| 762 | if (tag == .wasm) @panic("MLUGG TODO"); | ||
| 763 | if (tag == .spirv) @panic("MLUGG TODO"); | 762 | if (tag == .spirv) @panic("MLUGG TODO"); |
| 764 | dev.check(tag.devFeature()); | 763 | dev.check(tag.devFeature()); |
| 765 | return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air); | 764 | return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir, maybe_undef_air); |
| ... | @@ -1450,12 +1449,12 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { | ... | @@ -1450,12 +1449,12 @@ pub fn doZcuTask(comp: *Compilation, tid: usize, task: ZcuTask) void { |
| 1450 | const nav = zcu.funcInfo(func.func).owner_nav; | 1449 | const nav = zcu.funcInfo(func.func).owner_nav; |
| 1451 | const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); | 1450 | const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); |
| 1452 | defer pt.deactivate(); | 1451 | defer pt.deactivate(); |
| 1453 | assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR | ||
| 1454 | switch (func.mir.status.load(.monotonic)) { | 1452 | switch (func.mir.status.load(.monotonic)) { |
| 1455 | .pending => unreachable, | 1453 | .pending => unreachable, |
| 1456 | .ready => {}, | 1454 | .ready => {}, |
| 1457 | .failed => return, | 1455 | .failed => return, |
| 1458 | } | 1456 | } |
| 1457 | assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR | ||
| 1459 | const mir = &func.mir.value; | 1458 | const mir = &func.mir.value; |
| 1460 | if (comp.bin_file) |lf| { | 1459 | if (comp.bin_file) |lf| { |
| 1461 | lf.updateFunc(pt, func.func, mir, func.air) catch |err| switch (err) { | 1460 | lf.updateFunc(pt, func.func, mir, func.air) catch |err| switch (err) { |
src/link/Wasm.zig+134-59| ... | @@ -282,7 +282,7 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, | ... | @@ -282,7 +282,7 @@ mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 282 | /// Corresponds to `mir_instructions`. | 282 | /// Corresponds to `mir_instructions`. |
| 283 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, | 283 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, |
| 284 | /// All local types for all Zcu functions. | 284 | /// All local types for all Zcu functions. |
| 285 | all_zcu_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, | 285 | mir_locals: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, |
| 286 | 286 | ||
| 287 | params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, | 287 | params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, |
| 288 | returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, | 288 | returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, |
| ... | @@ -866,9 +866,24 @@ const ZcuDataStarts = struct { | ... | @@ -866,9 +866,24 @@ const ZcuDataStarts = struct { |
| 866 | }; | 866 | }; |
| 867 | 867 | ||
| 868 | pub const ZcuFunc = union { | 868 | pub const ZcuFunc = union { |
| 869 | function: CodeGen.Function, | 869 | function: Function, |
| 870 | tag_name: TagName, | 870 | tag_name: TagName, |
| 871 | 871 | ||
| 872 | pub const Function = extern struct { | ||
| 873 | /// Index into `Wasm.mir_instructions`. | ||
| 874 | instructions_off: u32, | ||
| 875 | /// This is unused except for as a safety slice bound and could be removed. | ||
| 876 | instructions_len: u32, | ||
| 877 | /// Index into `Wasm.mir_extra`. | ||
| 878 | extra_off: u32, | ||
| 879 | /// This is unused except for as a safety slice bound and could be removed. | ||
| 880 | extra_len: u32, | ||
| 881 | /// Index into `Wasm.mir_locals`. | ||
| 882 | locals_off: u32, | ||
| 883 | locals_len: u32, | ||
| 884 | prologue: Mir.Prologue, | ||
| 885 | }; | ||
| 886 | |||
| 872 | pub const TagName = extern struct { | 887 | pub const TagName = extern struct { |
| 873 | symbol_name: String, | 888 | symbol_name: String, |
| 874 | type_index: FunctionType.Index, | 889 | type_index: FunctionType.Index, |
| ... | @@ -3107,7 +3122,7 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -3107,7 +3122,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 3107 | 3122 | ||
| 3108 | wasm.mir_instructions.deinit(gpa); | 3123 | wasm.mir_instructions.deinit(gpa); |
| 3109 | wasm.mir_extra.deinit(gpa); | 3124 | wasm.mir_extra.deinit(gpa); |
| 3110 | wasm.all_zcu_locals.deinit(gpa); | 3125 | wasm.mir_locals.deinit(gpa); |
| 3111 | 3126 | ||
| 3112 | if (wasm.dwarf) |*dwarf| dwarf.deinit(); | 3127 | if (wasm.dwarf) |*dwarf| dwarf.deinit(); |
| 3113 | 3128 | ||
| ... | @@ -3167,33 +3182,96 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -3167,33 +3182,96 @@ pub fn deinit(wasm: *Wasm) void { |
| 3167 | wasm.missing_exports.deinit(gpa); | 3182 | wasm.missing_exports.deinit(gpa); |
| 3168 | } | 3183 | } |
| 3169 | 3184 | ||
| 3170 | pub fn updateFunc(wasm: *Wasm, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Air.Liveness) !void { | 3185 | pub fn updateFunc( |
| 3186 | wasm: *Wasm, | ||
| 3187 | pt: Zcu.PerThread, | ||
| 3188 | func_index: InternPool.Index, | ||
| 3189 | any_mir: *const codegen.AnyMir, | ||
| 3190 | maybe_undef_air: *const Air, | ||
| 3191 | ) !void { | ||
| 3171 | if (build_options.skip_non_native and builtin.object_format != .wasm) { | 3192 | if (build_options.skip_non_native and builtin.object_format != .wasm) { |
| 3172 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 3193 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 3173 | } | 3194 | } |
| 3174 | 3195 | ||
| 3175 | dev.check(.wasm_backend); | 3196 | dev.check(.wasm_backend); |
| 3197 | _ = maybe_undef_air; // we (correctly) do not need this | ||
| 3176 | 3198 | ||
| 3199 | // This linker implementation only works with codegen backend `.stage2_wasm`. | ||
| 3200 | const mir = &any_mir.wasm; | ||
| 3177 | const zcu = pt.zcu; | 3201 | const zcu = pt.zcu; |
| 3178 | const gpa = zcu.gpa; | 3202 | const gpa = zcu.gpa; |
| 3179 | try wasm.functions.ensureUnusedCapacity(gpa, 1); | ||
| 3180 | try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1); | ||
| 3181 | |||
| 3182 | const ip = &zcu.intern_pool; | 3203 | const ip = &zcu.intern_pool; |
| 3204 | const is_obj = zcu.comp.config.output_mode == .Obj; | ||
| 3205 | const target = &zcu.comp.root_mod.resolved_target.result; | ||
| 3183 | const owner_nav = zcu.funcInfo(func_index).owner_nav; | 3206 | const owner_nav = zcu.funcInfo(func_index).owner_nav; |
| 3184 | log.debug("updateFunc {}", .{ip.getNav(owner_nav).fqn.fmt(ip)}); | 3207 | log.debug("updateFunc {}", .{ip.getNav(owner_nav).fqn.fmt(ip)}); |
| 3185 | 3208 | ||
| 3209 | // For Wasm, we do not lower the MIR to code just yet. That lowering happens during `flush`, | ||
| 3210 | // after garbage collection, which can affect function and global indexes, which affects the | ||
| 3211 | // LEB integer encoding, which affects the output binary size. | ||
| 3212 | |||
| 3213 | // However, we do move the MIR into a more efficient in-memory representation, where the arrays | ||
| 3214 | // for all functions are packed together rather than keeping them each in their own `Mir`. | ||
| 3215 | const mir_instructions_off: u32 = @intCast(wasm.mir_instructions.len); | ||
| 3216 | const mir_extra_off: u32 = @intCast(wasm.mir_extra.items.len); | ||
| 3217 | const mir_locals_off: u32 = @intCast(wasm.mir_locals.items.len); | ||
| 3218 | { | ||
| 3219 | // Copying MultiArrayList data is a little non-trivial. Resize, then memcpy both slices. | ||
| 3220 | const old_len = wasm.mir_instructions.len; | ||
| 3221 | try wasm.mir_instructions.resize(gpa, old_len + mir.instructions.len); | ||
| 3222 | const dest_slice = wasm.mir_instructions.slice().subslice(old_len, mir.instructions.len); | ||
| 3223 | const src_slice = mir.instructions; | ||
| 3224 | @memcpy(dest_slice.items(.tag), src_slice.items(.tag)); | ||
| 3225 | @memcpy(dest_slice.items(.data), src_slice.items(.data)); | ||
| 3226 | } | ||
| 3227 | try wasm.mir_extra.appendSlice(gpa, mir.extra); | ||
| 3228 | try wasm.mir_locals.appendSlice(gpa, mir.locals); | ||
| 3229 | |||
| 3230 | // We also need to populate some global state from `mir`. | ||
| 3231 | try wasm.zcu_indirect_function_set.ensureUnusedCapacity(gpa, mir.indirect_function_set.count()); | ||
| 3232 | for (mir.indirect_function_set.keys()) |nav| wasm.zcu_indirect_function_set.putAssumeCapacity(nav, {}); | ||
| 3233 | for (mir.func_tys.keys()) |func_ty| { | ||
| 3234 | const fn_info = zcu.typeToFunc(.fromInterned(func_ty)).?; | ||
| 3235 | _ = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target); | ||
| 3236 | } | ||
| 3237 | wasm.error_name_table_ref_count += mir.error_name_table_ref_count; | ||
| 3238 | // We need to populate UAV data. In theory, we can lower the UAV values while we fill `mir.uavs`. | ||
| 3239 | // However, lowering the data might cause *more* UAVs to be created, and mixing them up would be | ||
| 3240 | // a headache. So instead, just write `undefined` placeholder code and use the `ZcuDataStarts`. | ||
| 3186 | const zds: ZcuDataStarts = .init(wasm); | 3241 | const zds: ZcuDataStarts = .init(wasm); |
| 3242 | for (mir.uavs.keys(), mir.uavs.values()) |uav_val, uav_align| { | ||
| 3243 | if (uav_align != .none) { | ||
| 3244 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val); | ||
| 3245 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(uav_align) else uav_align; | ||
| 3246 | } | ||
| 3247 | if (is_obj) { | ||
| 3248 | const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val); | ||
| 3249 | if (!gop.found_existing) gop.value_ptr.* = undefined; // `zds` handles lowering | ||
| 3250 | } else { | ||
| 3251 | const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val); | ||
| 3252 | if (!gop.found_existing) gop.value_ptr.* = .{ | ||
| 3253 | .code = undefined, // `zds` handles lowering | ||
| 3254 | .count = 0, | ||
| 3255 | }; | ||
| 3256 | gop.value_ptr.count += 1; | ||
| 3257 | } | ||
| 3258 | } | ||
| 3259 | try zds.finish(wasm, pt); // actually generates the UAVs | ||
| 3260 | |||
| 3261 | try wasm.functions.ensureUnusedCapacity(gpa, 1); | ||
| 3262 | try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1); | ||
| 3187 | 3263 | ||
| 3188 | // This converts AIR to MIR but does not yet lower to wasm code. | 3264 | // This converts AIR to MIR but does not yet lower to wasm code. |
| 3189 | // That lowering happens during `flush`, after garbage collection, which | 3265 | wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = .{ |
| 3190 | // can affect function and global indexes, which affects the LEB integer | 3266 | .instructions_off = mir_instructions_off, |
| 3191 | // encoding, which affects the output binary size. | 3267 | .instructions_len = @intCast(mir.instructions.len), |
| 3192 | const function = try CodeGen.function(wasm, pt, func_index, air, liveness); | 3268 | .extra_off = mir_extra_off, |
| 3193 | wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = function }); | 3269 | .extra_len = @intCast(mir.extra.len), |
| 3270 | .locals_off = mir_locals_off, | ||
| 3271 | .locals_len = @intCast(mir.locals.len), | ||
| 3272 | .prologue = mir.prologue, | ||
| 3273 | } }); | ||
| 3194 | wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {}); | 3274 | wasm.functions.putAssumeCapacity(.pack(wasm, .{ .zcu_func = @enumFromInt(wasm.zcu_funcs.entries.len - 1) }), {}); |
| 3195 | |||
| 3196 | try zds.finish(wasm, pt); | ||
| 3197 | } | 3275 | } |
| 3198 | 3276 | ||
| 3199 | // Generate code for the "Nav", storing it in memory to be later written to | 3277 | // Generate code for the "Nav", storing it in memory to be later written to |
| ... | @@ -3988,58 +4066,54 @@ pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableInd | ... | @@ -3988,58 +4066,54 @@ pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableInd |
| 3988 | return @enumFromInt(gop.index); | 4066 | return @enumFromInt(gop.index); |
| 3989 | } | 4067 | } |
| 3990 | 4068 | ||
| 3991 | pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsObjIndex { | 4069 | pub fn addUavReloc( |
| 3992 | const comp = wasm.base.comp; | 4070 | wasm: *Wasm, |
| 3993 | const zcu = comp.zcu.?; | 4071 | reloc_offset: usize, |
| 3994 | const ip = &zcu.intern_pool; | 4072 | uav_val: InternPool.Index, |
| 3995 | const gpa = comp.gpa; | 4073 | orig_ptr_ty: InternPool.Index, |
| 3996 | assert(comp.config.output_mode == .Obj); | 4074 | addend: u32, |
| 3997 | 4075 | ) !void { | |
| 3998 | if (orig_ptr_ty != .none) { | ||
| 3999 | const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); | ||
| 4000 | const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; | ||
| 4001 | if (explicit_alignment.compare(.gt, abi_alignment)) { | ||
| 4002 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); | ||
| 4003 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment; | ||
| 4004 | } | ||
| 4005 | } | ||
| 4006 | |||
| 4007 | const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index); | ||
| 4008 | if (!gop.found_existing) gop.value_ptr.* = .{ | ||
| 4009 | // Lowering the value is delayed to avoid recursion. | ||
| 4010 | .code = undefined, | ||
| 4011 | .relocs = undefined, | ||
| 4012 | }; | ||
| 4013 | return @enumFromInt(gop.index); | ||
| 4014 | } | ||
| 4015 | |||
| 4016 | pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index, orig_ptr_ty: InternPool.Index) !UavsExeIndex { | ||
| 4017 | const comp = wasm.base.comp; | 4076 | const comp = wasm.base.comp; |
| 4018 | const zcu = comp.zcu.?; | 4077 | const zcu = comp.zcu.?; |
| 4019 | const ip = &zcu.intern_pool; | 4078 | const ip = &zcu.intern_pool; |
| 4020 | const gpa = comp.gpa; | 4079 | const gpa = comp.gpa; |
| 4021 | assert(comp.config.output_mode != .Obj); | ||
| 4022 | 4080 | ||
| 4023 | if (orig_ptr_ty != .none) { | 4081 | @"align": { |
| 4024 | const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); | 4082 | const ptr_type = ip.indexToKey(orig_ptr_ty).ptr_type; |
| 4025 | const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; | 4083 | const this_align = ptr_type.flags.alignment; |
| 4026 | if (explicit_alignment.compare(.gt, abi_alignment)) { | 4084 | if (this_align == .none) break :@"align"; |
| 4027 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); | 4085 | const abi_align = Zcu.Type.fromInterned(ptr_type.child).abiAlignment(zcu); |
| 4028 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment; | 4086 | if (this_align.compare(.lte, abi_align)) break :@"align"; |
| 4029 | } | 4087 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val); |
| 4030 | } | 4088 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align; |
| 4031 | 4089 | } | |
| 4032 | const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index); | 4090 | |
| 4033 | if (gop.found_existing) { | 4091 | if (comp.config.output_mode == .Obj) { |
| 4034 | gop.value_ptr.count += 1; | 4092 | const gop = try wasm.uavs_obj.getOrPut(gpa, uav_val); |
| 4093 | if (!gop.found_existing) gop.value_ptr.* = undefined; // to avoid recursion, `ZcuDataStarts` will lower the value later | ||
| 4094 | try wasm.out_relocs.append(gpa, .{ | ||
| 4095 | .offset = @intCast(reloc_offset), | ||
| 4096 | .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(uav_val) }, | ||
| 4097 | .tag = switch (wasm.pointerSize()) { | ||
| 4098 | 32 => .memory_addr_i32, | ||
| 4099 | 64 => .memory_addr_i64, | ||
| 4100 | else => unreachable, | ||
| 4101 | }, | ||
| 4102 | .addend = @intCast(addend), | ||
| 4103 | }); | ||
| 4035 | } else { | 4104 | } else { |
| 4036 | gop.value_ptr.* = .{ | 4105 | const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val); |
| 4037 | // Lowering the value is delayed to avoid recursion. | 4106 | if (!gop.found_existing) gop.value_ptr.* = .{ |
| 4038 | .code = undefined, | 4107 | .code = undefined, // to avoid recursion, `ZcuDataStarts` will lower the value later |
| 4039 | .count = 1, | 4108 | .count = 0, |
| 4040 | }; | 4109 | }; |
| 4110 | gop.value_ptr.count += 1; | ||
| 4111 | try wasm.uav_fixups.append(gpa, .{ | ||
| 4112 | .uavs_exe_index = @enumFromInt(gop.index), | ||
| 4113 | .offset = @intCast(reloc_offset), | ||
| 4114 | .addend = addend, | ||
| 4115 | }); | ||
| 4041 | } | 4116 | } |
| 4042 | return @enumFromInt(gop.index); | ||
| 4043 | } | 4117 | } |
| 4044 | 4118 | ||
| 4045 | pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { | 4119 | pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex { |
| ... | @@ -4073,10 +4147,11 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { | ... | @@ -4073,10 +4147,11 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex { |
| 4073 | } | 4147 | } |
| 4074 | 4148 | ||
| 4075 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. | 4149 | /// Asserts it is called after `Flush.data_segments` is fully populated and sorted. |
| 4076 | pub fn uavAddr(wasm: *Wasm, uav_index: UavsExeIndex) u32 { | 4150 | pub fn uavAddr(wasm: *Wasm, ip_index: InternPool.Index) u32 { |
| 4077 | assert(wasm.flush_buffer.memory_layout_finished); | 4151 | assert(wasm.flush_buffer.memory_layout_finished); |
| 4078 | const comp = wasm.base.comp; | 4152 | const comp = wasm.base.comp; |
| 4079 | assert(comp.config.output_mode != .Obj); | 4153 | assert(comp.config.output_mode != .Obj); |
| 4154 | const uav_index: UavsExeIndex = @enumFromInt(wasm.uavs_exe.getIndex(ip_index).?); | ||
| 4080 | const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index }); | 4155 | const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index }); |
| 4081 | return wasm.flush_buffer.data_segments.get(ds_id).?; | 4156 | return wasm.flush_buffer.data_segments.get(ds_id).?; |
| 4082 | } | 4157 | } |
src/link/Wasm/Flush.zig+16-1| ... | @@ -9,6 +9,7 @@ const Alignment = Wasm.Alignment; | ... | @@ -9,6 +9,7 @@ const Alignment = Wasm.Alignment; |
| 9 | const String = Wasm.String; | 9 | const String = Wasm.String; |
| 10 | const Relocation = Wasm.Relocation; | 10 | const Relocation = Wasm.Relocation; |
| 11 | const InternPool = @import("../../InternPool.zig"); | 11 | const InternPool = @import("../../InternPool.zig"); |
| 12 | const Mir = @import("../../arch/wasm/Mir.zig"); | ||
| 12 | 13 | ||
| 13 | const build_options = @import("build_options"); | 14 | const build_options = @import("build_options"); |
| 14 | 15 | ||
| ... | @@ -868,7 +869,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -868,7 +869,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 868 | .enum_type => { | 869 | .enum_type => { |
| 869 | try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index); | 870 | try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index); |
| 870 | }, | 871 | }, |
| 871 | else => try i.value(wasm).function.lower(wasm, binary_bytes), | 872 | else => { |
| 873 | const func = i.value(wasm).function; | ||
| 874 | const mir: Mir = .{ | ||
| 875 | .instructions = wasm.mir_instructions.slice().subslice(func.instructions_off, func.instructions_len), | ||
| 876 | .extra = wasm.mir_extra.items[func.extra_off..][0..func.extra_len], | ||
| 877 | .locals = wasm.mir_locals.items[func.locals_off..][0..func.locals_len], | ||
| 878 | .prologue = func.prologue, | ||
| 879 | // These fields are unused by `lower`. | ||
| 880 | .uavs = undefined, | ||
| 881 | .indirect_function_set = undefined, | ||
| 882 | .func_tys = undefined, | ||
| 883 | .error_name_table_ref_count = undefined, | ||
| 884 | }; | ||
| 885 | try mir.lower(wasm, binary_bytes); | ||
| 886 | }, | ||
| 872 | } | 887 | } |
| 873 | }, | 888 | }, |
| 874 | }; | 889 | }; |
src/target.zig+1-1| ... | @@ -851,7 +851,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt | ... | @@ -851,7 +851,7 @@ pub inline fn backendSupportsFeature(backend: std.builtin.CompilerBackend, compt |
| 851 | .separate_thread => switch (backend) { | 851 | .separate_thread => switch (backend) { |
| 852 | .stage2_llvm => false, | 852 | .stage2_llvm => false, |
| 853 | // MLUGG TODO | 853 | // MLUGG TODO |
| 854 | .stage2_c => true, | 854 | .stage2_c, .stage2_wasm => true, |
| 855 | else => false, | 855 | else => false, |
| 856 | }, | 856 | }, |
| 857 | }; | 857 | }; |