| 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 | 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 | 154 | /// This function is used in the debugger pretty formatters in tools/ to fetch the |
| 139 | 155 | /// child field order and entry type to facilitate fancy debug printing for this type. |
| 140 | 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 | 3500 | // TODO handle the union safety field |
| 3501 | 3501 | //addBuf(&bufs, mem.sliceAsBytes(wasm.mir_instructions.items(.data))); |
| 3502 | 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 | 3504 | addBuf(&bufs, mem.sliceAsBytes(wasm.tag_name_bytes.items)); |
| 3505 | 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 | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const testing = std.testing; |
| 6 | const leb = std.leb; | |
| 7 | 6 | const mem = std.mem; |
| 8 | 7 | const log = std.log.scoped(.codegen); |
| 9 | 8 | |
| ... | ... | @@ -18,12 +17,10 @@ const Compilation = @import("../../Compilation.zig"); |
| 18 | 17 | const link = @import("../../link.zig"); |
| 19 | 18 | const Air = @import("../../Air.zig"); |
| 20 | 19 | const Mir = @import("Mir.zig"); |
| 21 | const Emit = @import("Emit.zig"); | |
| 22 | 20 | const abi = @import("abi.zig"); |
| 23 | 21 | const Alignment = InternPool.Alignment; |
| 24 | 22 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 25 | 23 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 26 | const Wasm = link.File.Wasm; | |
| 27 | 24 | |
| 28 | 25 | const target_util = @import("../../target.zig"); |
| 29 | 26 | const libcFloatPrefix = target_util.libcFloatPrefix; |
| ... | ... | @@ -78,17 +75,24 @@ simd_immediates: std.ArrayListUnmanaged([16]u8) = .empty, |
| 78 | 75 | /// The Target we're emitting (used to call intInfo) |
| 79 | 76 | target: *const std.Target, |
| 80 | 77 | ptr_size: enum { wasm32, wasm64 }, |
| 81 | wasm: *link.File.Wasm, | |
| 82 | 78 | pt: Zcu.PerThread, |
| 83 | 79 | /// List of MIR Instructions |
| 84 | mir_instructions: *std.MultiArrayList(Mir.Inst), | |
| 80 | mir_instructions: std.MultiArrayList(Mir.Inst), | |
| 85 | 81 | /// Contains extra data for MIR |
| 86 | mir_extra: *std.ArrayListUnmanaged(u32), | |
| 87 | start_mir_extra_off: u32, | |
| 88 | start_locals_off: u32, | |
| 82 | mir_extra: std.ArrayListUnmanaged(u32), | |
| 89 | 83 | /// List of all locals' types generated throughout this declaration |
| 90 | 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 | 96 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| 93 | 97 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| 94 | 98 | initial_stack_value: WValue = .none, |
| ... | ... | @@ -219,7 +223,7 @@ const WValue = union(enum) { |
| 219 | 223 | if (local_value < reserved + 2) return; // reserved locals may never be re-used. Also accounts for 2 stack locals. |
| 220 | 224 | |
| 221 | 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 | 227 | switch (valtype) { |
| 224 | 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 | 229 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, |
| ... | ... | @@ -716,6 +720,12 @@ pub fn deinit(cg: *CodeGen) void { |
| 716 | 720 | cg.free_locals_f32.deinit(gpa); |
| 717 | 721 | cg.free_locals_f64.deinit(gpa); |
| 718 | 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 | 729 | cg.* = undefined; |
| 720 | 730 | } |
| 721 | 731 | |
| ... | ... | @@ -876,7 +886,7 @@ fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void { |
| 876 | 886 | } |
| 877 | 887 | |
| 878 | 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 | 890 | try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode)); |
| 881 | 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 | 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 | 902 | /// Accepts an unsigned 32bit integer rather than a signed integer to |
| 897 | 903 | /// prevent us from having to bitcast multiple times as most values |
| 898 | 904 | /// within codegen are represented as unsigned rather than signed. |
| ... | ... | @@ -911,7 +917,7 @@ fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void { |
| 911 | 917 | /// Accepts the index into the list of 128bit-immediates |
| 912 | 918 | fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void { |
| 913 | 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 | 921 | // tag + 128bit value |
| 916 | 922 | try cg.mir_extra.ensureUnusedCapacity(cg.gpa, 5); |
| 917 | 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 | 962 | /// Returns the index into `mir_extra` |
| 957 | 963 | fn addExtraAssumeCapacity(cg: *CodeGen, extra: anytype) error{OutOfMemory}!u32 { |
| 958 | 964 | const fields = std.meta.fields(@TypeOf(extra)); |
| 959 | const result = cg.extraLen(); | |
| 965 | const result: u32 = @intCast(cg.mir_extra.items.len); | |
| 960 | 966 | inline for (fields) |field| { |
| 961 | 967 | cg.mir_extra.appendAssumeCapacity(switch (field.type) { |
| 962 | 968 | u32 => @field(extra, field.name), |
| 963 | 969 | i32 => @bitCast(@field(extra, field.name)), |
| 964 | 970 | InternPool.Index, |
| 965 | 971 | InternPool.Nav.Index, |
| 966 | Wasm.UavsObjIndex, | |
| 967 | Wasm.UavsExeIndex, | |
| 968 | 972 | => @intFromEnum(@field(extra, field.name)), |
| 969 | 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 | 1038 | .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), |
| 1035 | 1039 | .float64 => |val| try cg.addFloat64(val), |
| 1036 | 1040 | .nav_ref => |nav_ref| { |
| 1037 | const wasm = cg.wasm; | |
| 1038 | const comp = wasm.base.comp; | |
| 1039 | const zcu = comp.zcu.?; | |
| 1041 | const zcu = cg.pt.zcu; | |
| 1040 | 1042 | const ip = &zcu.intern_pool; |
| 1041 | 1043 | if (ip.getNav(nav_ref.nav_index).isFn(ip)) { |
| 1042 | 1044 | assert(nav_ref.offset == 0); |
| 1043 | const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, nav_ref.nav_index); | |
| 1044 | if (!gop.found_existing) gop.value_ptr.* = {}; | |
| 1045 | try cg.addInst(.{ | |
| 1046 | .tag = .func_ref, | |
| 1047 | .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) }, | |
| 1048 | }); | |
| 1045 | try cg.mir_indirect_function_set.put(cg.gpa, nav_ref.nav_index, {}); | |
| 1046 | try cg.addInst(.{ .tag = .func_ref, .data = .{ .nav_index = nav_ref.nav_index } }); | |
| 1049 | 1047 | } else if (nav_ref.offset == 0) { |
| 1050 | 1048 | try cg.addInst(.{ .tag = .nav_ref, .data = .{ .nav_index = nav_ref.nav_index } }); |
| 1051 | 1049 | } else { |
| ... | ... | @@ -1061,41 +1059,37 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void { |
| 1061 | 1059 | } |
| 1062 | 1060 | }, |
| 1063 | 1061 | .uav_ref => |uav| { |
| 1064 | const wasm = cg.wasm; | |
| 1065 | const comp = wasm.base.comp; | |
| 1066 | const is_obj = comp.config.output_mode == .Obj; | |
| 1067 | const zcu = comp.zcu.?; | |
| 1062 | const zcu = cg.pt.zcu; | |
| 1068 | 1063 | const ip = &zcu.intern_pool; |
| 1069 | if (ip.isFunctionType(ip.typeOf(uav.ip_index))) { | |
| 1070 | assert(uav.offset == 0); | |
| 1071 | const owner_nav = ip.toFunc(uav.ip_index).owner_nav; | |
| 1072 | const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, owner_nav); | |
| 1073 | if (!gop.found_existing) gop.value_ptr.* = {}; | |
| 1074 | try cg.addInst(.{ | |
| 1075 | .tag = .func_ref, | |
| 1076 | .data = .{ .indirect_function_table_index = @enumFromInt(gop.index) }, | |
| 1077 | }); | |
| 1078 | } else if (uav.offset == 0) { | |
| 1064 | assert(!ip.isFunctionType(ip.typeOf(uav.ip_index))); | |
| 1065 | const gop = try cg.mir_uavs.getOrPut(cg.gpa, uav.ip_index); | |
| 1066 | const this_align: Alignment = a: { | |
| 1067 | if (uav.orig_ptr_ty == .none) break :a .none; | |
| 1068 | const ptr_type = ip.indexToKey(uav.orig_ptr_ty).ptr_type; | |
| 1069 | const this_align = ptr_type.flags.alignment; | |
| 1070 | if (this_align == .none) break :a .none; | |
| 1071 | const abi_align = Type.fromInterned(ptr_type.child).abiAlignment(zcu); | |
| 1072 | if (this_align.compare(.lte, abi_align)) break :a .none; | |
| 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 | 1082 | try cg.addInst(.{ |
| 1080 | 1083 | .tag = .uav_ref, |
| 1081 | .data = if (is_obj) .{ | |
| 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 | }, | |
| 1084 | .data = .{ .ip_index = uav.ip_index }, | |
| 1086 | 1085 | }); |
| 1087 | 1086 | } else { |
| 1088 | 1087 | try cg.addInst(.{ |
| 1089 | 1088 | .tag = .uav_ref_off, |
| 1090 | .data = .{ | |
| 1091 | .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{ | |
| 1092 | .uav_obj = try wasm.refUavObj(uav.ip_index, uav.orig_ptr_ty), | |
| 1093 | .offset = uav.offset, | |
| 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 | }, | |
| 1089 | .data = .{ .payload = try cg.addExtra(@as(Mir.UavRefOff, .{ | |
| 1090 | .value = uav.ip_index, | |
| 1091 | .offset = uav.offset, | |
| 1092 | })) }, | |
| 1099 | 1093 | }); |
| 1100 | 1094 | } |
| 1101 | 1095 | }, |
| ... | ... | @@ -1157,106 +1151,12 @@ fn allocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 1157 | 1151 | /// to use a zero-initialized local. |
| 1158 | 1152 | fn ensureAllocLocal(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 1159 | 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 | 1155 | const initial_index = cg.local_index; |
| 1162 | 1156 | cg.local_index += 1; |
| 1163 | 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 | 1160 | pub const Error = error{ |
| 1261 | 1161 | OutOfMemory, |
| 1262 | 1162 | /// Compiler was asked to operate on a number larger than supported. |
| ... | ... | @@ -1265,13 +1165,16 @@ pub const Error = error{ |
| 1265 | 1165 | CodegenFail, |
| 1266 | 1166 | }; |
| 1267 | 1167 | |
| 1268 | pub fn function( | |
| 1269 | wasm: *Wasm, | |
| 1168 | pub fn generate( | |
| 1169 | bin_file: *link.File, | |
| 1270 | 1170 | pt: Zcu.PerThread, |
| 1171 | src_loc: Zcu.LazySrcLoc, | |
| 1271 | 1172 | func_index: InternPool.Index, |
| 1272 | air: Air, | |
| 1273 | liveness: Air.Liveness, | |
| 1274 | ) Error!Function { | |
| 1173 | air: *const Air, | |
| 1174 | liveness: *const Air.Liveness, | |
| 1175 | ) Error!Mir { | |
| 1176 | _ = src_loc; | |
| 1177 | _ = bin_file; | |
| 1275 | 1178 | const zcu = pt.zcu; |
| 1276 | 1179 | const gpa = zcu.gpa; |
| 1277 | 1180 | const cg = zcu.funcInfo(func_index); |
| ... | ... | @@ -1279,10 +1182,8 @@ pub fn function( |
| 1279 | 1182 | const target = &file_scope.mod.?.resolved_target.result; |
| 1280 | 1183 | const fn_ty = zcu.navValue(cg.owner_nav).typeOf(zcu); |
| 1281 | 1184 | const fn_info = zcu.typeToFunc(fn_ty).?; |
| 1282 | const ip = &zcu.intern_pool; | |
| 1283 | const fn_ty_index = try wasm.internFunctionType(fn_info.cc, fn_info.param_types.get(ip), .fromInterned(fn_info.return_type), target); | |
| 1284 | const returns = fn_ty_index.ptr(wasm).returns.slice(wasm); | |
| 1285 | const any_returns = returns.len != 0; | |
| 1185 | const ret_ty: Type = .fromInterned(fn_info.return_type); | |
| 1186 | const any_returns = !firstParamSRet(fn_info.cc, ret_ty, zcu, target) and ret_ty.hasRuntimeBitsIgnoreComptime(zcu); | |
| 1286 | 1187 | |
| 1287 | 1188 | var cc_result = try resolveCallingConventionValues(zcu, fn_ty, target); |
| 1288 | 1189 | defer cc_result.deinit(gpa); |
| ... | ... | @@ -1290,8 +1191,8 @@ pub fn function( |
| 1290 | 1191 | var code_gen: CodeGen = .{ |
| 1291 | 1192 | .gpa = gpa, |
| 1292 | 1193 | .pt = pt, |
| 1293 | .air = air, | |
| 1294 | .liveness = liveness, | |
| 1194 | .air = air.*, | |
| 1195 | .liveness = liveness.*, | |
| 1295 | 1196 | .owner_nav = cg.owner_nav, |
| 1296 | 1197 | .target = target, |
| 1297 | 1198 | .ptr_size = switch (target.cpu.arch) { |
| ... | ... | @@ -1299,31 +1200,33 @@ pub fn function( |
| 1299 | 1200 | .wasm64 => .wasm64, |
| 1300 | 1201 | else => unreachable, |
| 1301 | 1202 | }, |
| 1302 | .wasm = wasm, | |
| 1303 | 1203 | .func_index = func_index, |
| 1304 | 1204 | .args = cc_result.args, |
| 1305 | 1205 | .return_value = cc_result.return_value, |
| 1306 | 1206 | .local_index = cc_result.local_index, |
| 1307 | .mir_instructions = &wasm.mir_instructions, | |
| 1308 | .mir_extra = &wasm.mir_extra, | |
| 1309 | .locals = &wasm.all_zcu_locals, | |
| 1310 | .start_mir_extra_off = @intCast(wasm.mir_extra.items.len), | |
| 1311 | .start_locals_off = @intCast(wasm.all_zcu_locals.items.len), | |
| 1207 | .mir_instructions = .empty, | |
| 1208 | .mir_extra = .empty, | |
| 1209 | .mir_locals = .empty, | |
| 1210 | .mir_uavs = .empty, | |
| 1211 | .mir_indirect_function_set = .empty, | |
| 1212 | .mir_func_tys = .empty, | |
| 1213 | .error_name_table_ref_count = 0, | |
| 1312 | 1214 | }; |
| 1313 | 1215 | defer code_gen.deinit(); |
| 1314 | 1216 | |
| 1315 | return functionInner(&code_gen, any_returns) catch |err| switch (err) { | |
| 1316 | error.CodegenFail => return error.CodegenFail, | |
| 1217 | try code_gen.mir_func_tys.putNoClobber(gpa, fn_ty.toIntern(), {}); | |
| 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 | 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 { | |
| 1322 | const wasm = cg.wasm; | |
| 1228 | fn generateInner(cg: *CodeGen, any_returns: bool) InnerError!Mir { | |
| 1323 | 1229 | const zcu = cg.pt.zcu; |
| 1324 | ||
| 1325 | const start_mir_off: u32 = @intCast(wasm.mir_instructions.len); | |
| 1326 | ||
| 1327 | 1230 | try cg.branches.append(cg.gpa, .{}); |
| 1328 | 1231 | // clean up outer branch |
| 1329 | 1232 | defer { |
| ... | ... | @@ -1347,20 +1250,25 @@ fn functionInner(cg: *CodeGen, any_returns: bool) InnerError!Function { |
| 1347 | 1250 | try cg.addTag(.end); |
| 1348 | 1251 | try cg.addTag(.dbg_epilogue_begin); |
| 1349 | 1252 | |
| 1350 | return .{ | |
| 1351 | .mir_off = start_mir_off, | |
| 1352 | .mir_len = @intCast(wasm.mir_instructions.len - start_mir_off), | |
| 1353 | .mir_extra_off = cg.start_mir_extra_off, | |
| 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), | |
| 1253 | var mir: Mir = .{ | |
| 1254 | .instructions = cg.mir_instructions.toOwnedSlice(), | |
| 1255 | .extra = &.{}, // fallible so assigned after errdefer | |
| 1256 | .locals = &.{}, // fallible so assigned after errdefer | |
| 1357 | 1257 | .prologue = if (cg.initial_stack_value == .none) .none else .{ |
| 1358 | 1258 | .sp_local = cg.initial_stack_value.local.value, |
| 1359 | 1259 | .flags = .{ .stack_alignment = cg.stack_alignment }, |
| 1360 | 1260 | .stack_size = cg.stack_size, |
| 1361 | 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 | 1274 | const CallWValues = struct { |
| ... | ... | @@ -2220,7 +2128,6 @@ fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2220 | 2128 | } |
| 2221 | 2129 | |
| 2222 | 2130 | fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) InnerError!void { |
| 2223 | const wasm = cg.wasm; | |
| 2224 | 2131 | if (modifier == .always_tail) return cg.fail("TODO implement tail calls for wasm", .{}); |
| 2225 | 2132 | const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 2226 | 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 | 2184 | const operand = try cg.resolveInst(pl_op.operand); |
| 2278 | 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); | |
| 2281 | try cg.addFuncTy(.call_indirect, fn_type_index); | |
| 2187 | try cg.mir_func_tys.put(cg.gpa, fn_ty.toIntern(), {}); | |
| 2188 | try cg.addInst(.{ | |
| 2189 | .tag = .call_indirect, | |
| 2190 | .data = .{ .ip_index = fn_ty.toIntern() }, | |
| 2191 | }); | |
| 2282 | 2192 | } |
| 2283 | 2193 | |
| 2284 | 2194 | const result_value = result_value: { |
| ... | ... | @@ -2449,7 +2359,7 @@ fn store(cg: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErr |
| 2449 | 2359 | try cg.emitWValue(lhs); |
| 2450 | 2360 | try cg.lowerToStack(rhs); |
| 2451 | 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 | 2363 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 2454 | 2364 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 2455 | 2365 | @intFromEnum(std.wasm.SimdOpcode.v128_store), |
| ... | ... | @@ -2574,7 +2484,7 @@ fn load(cg: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue |
| 2574 | 2484 | |
| 2575 | 2485 | if (ty.zigTypeTag(zcu) == .vector) { |
| 2576 | 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 | 2488 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 2579 | 2489 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 2580 | 2490 | @intFromEnum(std.wasm.SimdOpcode.v128_load), |
| ... | ... | @@ -4971,7 +4881,7 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4971 | 4881 | |
| 4972 | 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 | 4885 | try cg.mir_extra.appendSlice(cg.gpa, &operands); |
| 4976 | 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 | 5033 | else => break :blk, // Cannot make use of simd-instructions |
| 5124 | 5034 | }; |
| 5125 | 5035 | try cg.emitWValue(operand); |
| 5126 | const extra_index: u32 = cg.extraLen(); | |
| 5036 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); | |
| 5127 | 5037 | // stores as := opcode, offset, alignment (opcode::memarg) |
| 5128 | 5038 | try cg.mir_extra.appendSlice(cg.gpa, &[_]u32{ |
| 5129 | 5039 | opcode, |
| ... | ... | @@ -5142,7 +5052,7 @@ fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5142 | 5052 | else => break :blk, // Cannot make use of simd-instructions |
| 5143 | 5053 | }; |
| 5144 | 5054 | try cg.emitWValue(operand); |
| 5145 | const extra_index = cg.extraLen(); | |
| 5055 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); | |
| 5146 | 5056 | try cg.mir_extra.append(cg.gpa, opcode); |
| 5147 | 5057 | try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } }); |
| 5148 | 5058 | return cg.finishAir(inst, .stack, &.{ty_op.operand}); |
| ... | ... | @@ -5246,7 +5156,7 @@ fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 5246 | 5156 | } |
| 5247 | 5157 | try cg.emitWValue(operand_a); |
| 5248 | 5158 | try cg.emitWValue(operand_b); |
| 5249 | const extra_index = cg.extraLen(); | |
| 5159 | const extra_index: u32 = @intCast(cg.mir_extra.items.len); | |
| 5250 | 5160 | try cg.mir_extra.appendSlice(cg.gpa, &.{ |
| 5251 | 5161 | @intFromEnum(std.wasm.SimdOpcode.i8x16_shuffle), |
| 5252 | 5162 | @bitCast(lane_map[0..4].*), |
| ... | ... | @@ -6016,9 +5926,8 @@ fn airErrorName(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 6016 | 5926 | const name_ty = Type.slice_const_u8_sentinel_0; |
| 6017 | 5927 | const abi_size = name_ty.abiSize(pt.zcu); |
| 6018 | 5928 | |
| 6019 | cg.wasm.error_name_table_ref_count += 1; | |
| 6020 | ||
| 6021 | 5929 | // Lowers to a i32.const or i64.const with the error table memory address. |
| 5930 | cg.error_name_table_ref_count += 1; | |
| 6022 | 5931 | try cg.addTag(.error_name_table_ref); |
| 6023 | 5932 | try cg.emitWValue(operand); |
| 6024 | 5933 | switch (cg.ptr_size) { |
| ... | ... | @@ -6046,7 +5955,7 @@ fn airPtrSliceFieldPtr(cg: *CodeGen, inst: Air.Inst.Index, offset: u32) InnerErr |
| 6046 | 5955 | |
| 6047 | 5956 | /// NOTE: Allocates place for result on virtual stack, when integer size > 64 bits |
| 6048 | 5957 | fn intZeroValue(cg: *CodeGen, ty: Type) InnerError!WValue { |
| 6049 | const zcu = cg.wasm.base.comp.zcu.?; | |
| 5958 | const zcu = cg.pt.zcu; | |
| 6050 | 5959 | const int_info = ty.intInfo(zcu); |
| 6051 | 5960 | const wasm_bits = toWasmBits(int_info.bits) orelse { |
| 6052 | 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 | 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 | 31 | const target = &comp.root_mod.resolved_target.result; |
| 32 | 32 | const is_wasm32 = target.cpu.arch == .wasm32; |
| 33 | 33 | |
| 34 | const tags = mir.instruction_tags; | |
| 35 | const datas = mir.instruction_datas; | |
| 34 | const tags = mir.instructions.items(.tag); | |
| 35 | const datas = mir.instructions.items(.data); | |
| 36 | 36 | var inst: u32 = 0; |
| 37 | 37 | |
| 38 | 38 | loop: switch (tags[inst]) { |
| ... | ... | @@ -50,18 +50,19 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 50 | 50 | }, |
| 51 | 51 | .uav_ref => { |
| 52 | 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 | 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 | 57 | inst += 1; |
| 58 | 58 | continue :loop tags[inst]; |
| 59 | 59 | }, |
| 60 | 60 | .uav_ref_off => { |
| 61 | const extra = mir.extraData(Mir.UavRefOff, datas[inst].payload).data; | |
| 61 | 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 | 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 | 67 | inst += 1; |
| 67 | 68 | continue :loop tags[inst]; |
| ... | ... | @@ -77,11 +78,14 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 77 | 78 | continue :loop tags[inst]; |
| 78 | 79 | }, |
| 79 | 80 | .func_ref => { |
| 81 | const indirect_func_idx: Wasm.ZcuIndirectFunctionSetIndex = @enumFromInt( | |
| 82 | wasm.zcu_indirect_function_set.getIndex(datas[inst].nav_index).?, | |
| 83 | ); | |
| 80 | 84 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); |
| 81 | 85 | if (is_obj) { |
| 82 | 86 | @panic("TODO"); |
| 83 | 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 | 90 | inst += 1; |
| 87 | 91 | continue :loop tags[inst]; |
| ... | ... | @@ -101,6 +105,7 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 101 | 105 | continue :loop tags[inst]; |
| 102 | 106 | }, |
| 103 | 107 | .error_name_table_ref => { |
| 108 | wasm.error_name_table_ref_count += 1; | |
| 104 | 109 | try code.ensureUnusedCapacity(gpa, 11); |
| 105 | 110 | const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const; |
| 106 | 111 | code.appendAssumeCapacity(@intFromEnum(opcode)); |
| ... | ... | @@ -176,7 +181,13 @@ pub fn lowerToCode(emit: *Emit) Error!void { |
| 176 | 181 | |
| 177 | 182 | .call_indirect => { |
| 178 | 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 | 191 | code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect)); |
| 181 | 192 | if (is_obj) { |
| 182 | 193 | try wasm.out_relocs.append(gpa, .{ |
| ... | ... | @@ -912,7 +923,7 @@ fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void { |
| 912 | 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 | 927 | const comp = wasm.base.comp; |
| 917 | 928 | const gpa = comp.gpa; |
| 918 | 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 | 933 | |
| 923 | 934 | try wasm.out_relocs.append(gpa, .{ |
| 924 | 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 | 937 | .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64, |
| 927 | .addend = data.offset, | |
| 938 | .addend = offset, | |
| 928 | 939 | }); |
| 929 | 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 | 944 | const comp = wasm.base.comp; |
| 934 | 945 | const gpa = comp.gpa; |
| 935 | 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 | 948 | try code.ensureUnusedCapacity(gpa, 11); |
| 938 | 949 | code.appendAssumeCapacity(@intFromEnum(opcode)); |
| 939 | 950 | |
| 940 | const addr = wasm.uavAddr(data.uav_exe); | |
| 941 | leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable; | |
| 951 | const addr = wasm.uavAddr(value); | |
| 952 | leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + offset))) catch unreachable; | |
| 942 | 953 | } |
| 943 | 954 | |
| 944 | 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 | 9 | const Mir = @This(); |
| 10 | 10 | const InternPool = @import("../../InternPool.zig"); |
| 11 | 11 | const Wasm = @import("../../link/Wasm.zig"); |
| 12 | const Emit = @import("Emit.zig"); | |
| 13 | const Alignment = InternPool.Alignment; | |
| 12 | 14 | |
| 13 | 15 | const builtin = @import("builtin"); |
| 14 | 16 | const std = @import("std"); |
| 15 | 17 | const assert = std.debug.assert; |
| 18 | const leb = std.leb; | |
| 16 | 19 | |
| 17 | instruction_tags: []const Inst.Tag, | |
| 18 | instruction_datas: []const Inst.Data, | |
| 20 | instructions: std.MultiArrayList(Inst).Slice, | |
| 19 | 21 | /// A slice of indexes where the meaning of the data is determined by the |
| 20 | 22 | /// `Inst.Tag` value. |
| 21 | 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 | 60 | pub const Inst = struct { |
| 24 | 61 | /// The opcode that represents this instruction |
| ... | ... | @@ -80,7 +117,7 @@ pub const Inst = struct { |
| 80 | 117 | /// Lowers to an i32_const which is the index of the function in the |
| 81 | 118 | /// table section. |
| 82 | 119 | /// |
| 83 | /// Uses `indirect_function_table_index`. | |
| 120 | /// Uses `nav_index`. | |
| 84 | 121 | func_ref, |
| 85 | 122 | /// Inserts debug information about the current line and column |
| 86 | 123 | /// of the source code |
| ... | ... | @@ -123,7 +160,7 @@ pub const Inst = struct { |
| 123 | 160 | /// Calls a function pointer by its function signature |
| 124 | 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 | 164 | call_indirect, |
| 128 | 165 | /// Calls a function by its index. |
| 129 | 166 | /// |
| ... | ... | @@ -611,11 +648,7 @@ pub const Inst = struct { |
| 611 | 648 | |
| 612 | 649 | ip_index: InternPool.Index, |
| 613 | 650 | nav_index: InternPool.Nav.Index, |
| 614 | func_ty: Wasm.FunctionType.Index, | |
| 615 | 651 | intrinsic: Intrinsic, |
| 616 | uav_obj: Wasm.UavsObjIndex, | |
| 617 | uav_exe: Wasm.UavsExeIndex, | |
| 618 | indirect_function_table_index: Wasm.ZcuIndirectFunctionSetIndex, | |
| 619 | 652 | |
| 620 | 653 | comptime { |
| 621 | 654 | switch (builtin.mode) { |
| ... | ... | @@ -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 { | |
| 630 | self.instructions.deinit(gpa); | |
| 631 | gpa.free(self.extra); | |
| 632 | self.* = undefined; | |
| 662 | pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { | |
| 663 | mir.instructions.deinit(gpa); | |
| 664 | gpa.free(mir.extra); | |
| 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 | 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 | 732 | Wasm.UavsObjIndex, |
| 644 | 733 | Wasm.UavsExeIndex, |
| 645 | 734 | InternPool.Nav.Index, |
| 735 | InternPool.Index, | |
| 646 | 736 | => @enumFromInt(self.extra[i]), |
| 647 | 737 | else => |field_type| @compileError("Unsupported field type " ++ @typeName(field_type)), |
| 648 | 738 | }; |
| ... | ... | @@ -695,13 +785,8 @@ pub const MemArg = struct { |
| 695 | 785 | alignment: u32, |
| 696 | 786 | }; |
| 697 | 787 | |
| 698 | pub const UavRefOffObj = struct { | |
| 699 | uav_obj: Wasm.UavsObjIndex, | |
| 700 | offset: i32, | |
| 701 | }; | |
| 702 | ||
| 703 | pub const UavRefOffExe = struct { | |
| 704 | uav_exe: Wasm.UavsExeIndex, | |
| 788 | pub const UavRefOff = struct { | |
| 789 | value: InternPool.Index, | |
| 705 | 790 | offset: i32, |
| 706 | 791 | }; |
| 707 | 792 |
src/codegen.zig+3-16| ... | ... | @@ -123,6 +123,7 @@ pub const AnyMir = union { |
| 123 | 123 | .stage2_riscv64, |
| 124 | 124 | .stage2_sparc64, |
| 125 | 125 | .stage2_x86_64, |
| 126 | .stage2_wasm, | |
| 126 | 127 | .stage2_c, |
| 127 | 128 | => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa), |
| 128 | 129 | } |
| ... | ... | @@ -153,6 +154,7 @@ pub fn generateFunction( |
| 153 | 154 | .stage2_riscv64, |
| 154 | 155 | .stage2_sparc64, |
| 155 | 156 | .stage2_x86_64, |
| 157 | .stage2_wasm, | |
| 156 | 158 | .stage2_c, |
| 157 | 159 | => |backend| { |
| 158 | 160 | dev.check(devFeatureForBackend(backend)); |
| ... | ... | @@ -784,7 +786,6 @@ fn lowerUavRef( |
| 784 | 786 | const comp = lf.comp; |
| 785 | 787 | const target = &comp.root_mod.resolved_target.result; |
| 786 | 788 | const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); |
| 787 | const is_obj = comp.config.output_mode == .Obj; | |
| 788 | 789 | const uav_val = uav.val; |
| 789 | 790 | const uav_ty = Type.fromInterned(ip.typeOf(uav_val)); |
| 790 | 791 | const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; |
| ... | ... | @@ -804,21 +805,7 @@ fn lowerUavRef( |
| 804 | 805 | dev.check(link.File.Tag.wasm.devFeature()); |
| 805 | 806 | const wasm = lf.cast(.wasm).?; |
| 806 | 807 | assert(reloc_parent == .none); |
| 807 | if (is_obj) { | |
| 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 | } | |
| 808 | try wasm.addUavReloc(code.items.len, uav.val, uav.orig_ty, @intCast(offset)); | |
| 822 | 809 | code.appendNTimesAssumeCapacity(0, ptr_width_bytes); |
| 823 | 810 | return; |
| 824 | 811 | }, |
src/link.zig+1-2| ... | ... | @@ -759,7 +759,6 @@ pub const File = struct { |
| 759 | 759 | switch (base.tag) { |
| 760 | 760 | .lld => unreachable, |
| 761 | 761 | inline else => |tag| { |
| 762 | if (tag == .wasm) @panic("MLUGG TODO"); | |
| 763 | 762 | if (tag == .spirv) @panic("MLUGG TODO"); |
| 764 | 763 | dev.check(tag.devFeature()); |
| 765 | 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 | 1449 | const nav = zcu.funcInfo(func.func).owner_nav; |
| 1451 | 1450 | const pt: Zcu.PerThread = .activate(zcu, @enumFromInt(tid)); |
| 1452 | 1451 | defer pt.deactivate(); |
| 1453 | assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR | |
| 1454 | 1452 | switch (func.mir.status.load(.monotonic)) { |
| 1455 | 1453 | .pending => unreachable, |
| 1456 | 1454 | .ready => {}, |
| 1457 | 1455 | .failed => return, |
| 1458 | 1456 | } |
| 1457 | assert(zcu.llvm_object == null); // LLVM codegen doesn't produce MIR | |
| 1459 | 1458 | const mir = &func.mir.value; |
| 1460 | 1459 | if (comp.bin_file) |lf| { |
| 1461 | 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 | 282 | /// Corresponds to `mir_instructions`. |
| 283 | 283 | mir_extra: std.ArrayListUnmanaged(u32) = .empty, |
| 284 | 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 | 287 | params_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, |
| 288 | 288 | returns_scratch: std.ArrayListUnmanaged(std.wasm.Valtype) = .empty, |
| ... | ... | @@ -866,9 +866,24 @@ const ZcuDataStarts = struct { |
| 866 | 866 | }; |
| 867 | 867 | |
| 868 | 868 | pub const ZcuFunc = union { |
| 869 | function: CodeGen.Function, | |
| 869 | function: Function, | |
| 870 | 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 | 887 | pub const TagName = extern struct { |
| 873 | 888 | symbol_name: String, |
| 874 | 889 | type_index: FunctionType.Index, |
| ... | ... | @@ -3107,7 +3122,7 @@ pub fn deinit(wasm: *Wasm) void { |
| 3107 | 3122 | |
| 3108 | 3123 | wasm.mir_instructions.deinit(gpa); |
| 3109 | 3124 | wasm.mir_extra.deinit(gpa); |
| 3110 | wasm.all_zcu_locals.deinit(gpa); | |
| 3125 | wasm.mir_locals.deinit(gpa); | |
| 3111 | 3126 | |
| 3112 | 3127 | if (wasm.dwarf) |*dwarf| dwarf.deinit(); |
| 3113 | 3128 | |
| ... | ... | @@ -3167,33 +3182,96 @@ pub fn deinit(wasm: *Wasm) void { |
| 3167 | 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 | 3192 | if (build_options.skip_non_native and builtin.object_format != .wasm) { |
| 3172 | 3193 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 3173 | 3194 | } |
| 3174 | 3195 | |
| 3175 | 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 | 3201 | const zcu = pt.zcu; |
| 3178 | 3202 | const gpa = zcu.gpa; |
| 3179 | try wasm.functions.ensureUnusedCapacity(gpa, 1); | |
| 3180 | try wasm.zcu_funcs.ensureUnusedCapacity(gpa, 1); | |
| 3181 | ||
| 3182 | 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 | 3206 | const owner_nav = zcu.funcInfo(func_index).owner_nav; |
| 3184 | 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 | 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 | 3264 | // This converts AIR to MIR but does not yet lower to wasm code. |
| 3189 | // That lowering happens during `flush`, after garbage collection, which | |
| 3190 | // can affect function and global indexes, which affects the LEB integer | |
| 3191 | // encoding, which affects the output binary size. | |
| 3192 | const function = try CodeGen.function(wasm, pt, func_index, air, liveness); | |
| 3193 | wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = function }); | |
| 3265 | wasm.zcu_funcs.putAssumeCapacity(func_index, .{ .function = .{ | |
| 3266 | .instructions_off = mir_instructions_off, | |
| 3267 | .instructions_len = @intCast(mir.instructions.len), | |
| 3268 | .extra_off = mir_extra_off, | |
| 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 | 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 | 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 | 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 { | |
| 3992 | const comp = wasm.base.comp; | |
| 3993 | const zcu = comp.zcu.?; | |
| 3994 | const ip = &zcu.intern_pool; | |
| 3995 | const gpa = comp.gpa; | |
| 3996 | assert(comp.config.output_mode == .Obj); | |
| 3997 | ||
| 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 { | |
| 4069 | pub fn addUavReloc( | |
| 4070 | wasm: *Wasm, | |
| 4071 | reloc_offset: usize, | |
| 4072 | uav_val: InternPool.Index, | |
| 4073 | orig_ptr_ty: InternPool.Index, | |
| 4074 | addend: u32, | |
| 4075 | ) !void { | |
| 4017 | 4076 | const comp = wasm.base.comp; |
| 4018 | 4077 | const zcu = comp.zcu.?; |
| 4019 | 4078 | const ip = &zcu.intern_pool; |
| 4020 | 4079 | const gpa = comp.gpa; |
| 4021 | assert(comp.config.output_mode != .Obj); | |
| 4022 | 4080 | |
| 4023 | if (orig_ptr_ty != .none) { | |
| 4024 | const abi_alignment = Zcu.Type.fromInterned(ip.typeOf(ip_index)).abiAlignment(zcu); | |
| 4025 | const explicit_alignment = ip.indexToKey(orig_ptr_ty).ptr_type.flags.alignment; | |
| 4026 | if (explicit_alignment.compare(.gt, abi_alignment)) { | |
| 4027 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, ip_index); | |
| 4028 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(explicit_alignment) else explicit_alignment; | |
| 4029 | } | |
| 4030 | } | |
| 4031 | ||
| 4032 | const gop = try wasm.uavs_exe.getOrPut(gpa, ip_index); | |
| 4033 | if (gop.found_existing) { | |
| 4034 | gop.value_ptr.count += 1; | |
| 4081 | @"align": { | |
| 4082 | const ptr_type = ip.indexToKey(orig_ptr_ty).ptr_type; | |
| 4083 | const this_align = ptr_type.flags.alignment; | |
| 4084 | if (this_align == .none) break :@"align"; | |
| 4085 | const abi_align = Zcu.Type.fromInterned(ptr_type.child).abiAlignment(zcu); | |
| 4086 | if (this_align.compare(.lte, abi_align)) break :@"align"; | |
| 4087 | const gop = try wasm.overaligned_uavs.getOrPut(gpa, uav_val); | |
| 4088 | gop.value_ptr.* = if (gop.found_existing) gop.value_ptr.maxStrict(this_align) else this_align; | |
| 4089 | } | |
| 4090 | ||
| 4091 | if (comp.config.output_mode == .Obj) { | |
| 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 | 4104 | } else { |
| 4036 | gop.value_ptr.* = .{ | |
| 4037 | // Lowering the value is delayed to avoid recursion. | |
| 4038 | .code = undefined, | |
| 4039 | .count = 1, | |
| 4105 | const gop = try wasm.uavs_exe.getOrPut(gpa, uav_val); | |
| 4106 | if (!gop.found_existing) gop.value_ptr.* = .{ | |
| 4107 | .code = undefined, // to avoid recursion, `ZcuDataStarts` will lower the value later | |
| 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 | 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 | 4147 | } |
| 4074 | 4148 | |
| 4075 | 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 | 4151 | assert(wasm.flush_buffer.memory_layout_finished); |
| 4078 | 4152 | const comp = wasm.base.comp; |
| 4079 | 4153 | assert(comp.config.output_mode != .Obj); |
| 4154 | const uav_index: UavsExeIndex = @enumFromInt(wasm.uavs_exe.getIndex(ip_index).?); | |
| 4080 | 4155 | const ds_id: DataSegmentId = .pack(wasm, .{ .uav_exe = uav_index }); |
| 4081 | 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 | 9 | const String = Wasm.String; |
| 10 | 10 | const Relocation = Wasm.Relocation; |
| 11 | 11 | const InternPool = @import("../../InternPool.zig"); |
| 12 | const Mir = @import("../../arch/wasm/Mir.zig"); | |
| 12 | 13 | |
| 13 | 14 | const build_options = @import("build_options"); |
| 14 | 15 | |
| ... | ... | @@ -868,7 +869,21 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 868 | 869 | .enum_type => { |
| 869 | 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 | 851 | .separate_thread => switch (backend) { |
| 852 | 852 | .stage2_llvm => false, |
| 853 | 853 | // MLUGG TODO |
| 854 | .stage2_c => true, | |
| 854 | .stage2_c, .stage2_wasm => true, | |
| 855 | 855 | else => false, |
| 856 | 856 | }, |
| 857 | 857 | }; |