| ... | @@ -39,6 +39,14 @@ const WValue = union(enum) { | ... | @@ -39,6 +39,14 @@ const WValue = union(enum) { |
| 39 | /// Note: The value contains the symbol index, rather than the actual address | 39 | /// Note: The value contains the symbol index, rather than the actual address |
| 40 | /// as we use this to perform the relocation. | 40 | /// as we use this to perform the relocation. |
| 41 | memory: u32, | 41 | memory: u32, |
| | 42 | /// A value that represents a parent pointer and an offset |
| | 43 | /// from that pointer. i.e. when slicing with constant values. |
| | 44 | memory_offset: struct { |
| | 45 | /// The symbol of the parent pointer |
| | 46 | pointer: u32, |
| | 47 | /// Offset will be set as addend when relocating |
| | 48 | offset: u32, |
| | 49 | }, |
| 42 | /// Represents a function pointer | 50 | /// Represents a function pointer |
| 43 | /// In wasm function pointers are indexes into a function table, | 51 | /// In wasm function pointers are indexes into a function table, |
| 44 | /// rather than an address in the data section. | 52 | /// rather than an address in the data section. |
| ... | @@ -552,6 +560,9 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, | ... | @@ -552,6 +560,9 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 552 | /// When a function is executing, we store the the current stack pointer's value within this local. | 560 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| 553 | /// This value is then used to restore the stack pointer to the original value at the return of the function. | 561 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| 554 | initial_stack_value: WValue = .none, | 562 | initial_stack_value: WValue = .none, |
| | 563 | /// The current stack pointer substracted with the stack size. From this value, we will calculate |
| | 564 | /// all offsets of the stack values. |
| | 565 | bottom_stack_value: WValue = .none, |
| 555 | /// Arguments of this function declaration | 566 | /// Arguments of this function declaration |
| 556 | /// This will be set after `resolveCallingConventionValues` | 567 | /// This will be set after `resolveCallingConventionValues` |
| 557 | args: []WValue = &.{}, | 568 | args: []WValue = &.{}, |
| ... | @@ -559,6 +570,14 @@ args: []WValue = &.{}, | ... | @@ -559,6 +570,14 @@ args: []WValue = &.{}, |
| 559 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated | 570 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated |
| 560 | /// before this function returns its execution to the caller. | 571 | /// before this function returns its execution to the caller. |
| 561 | return_value: WValue = .none, | 572 | return_value: WValue = .none, |
| | 573 | /// The size of the stack this function occupies. In the function prologue |
| | 574 | /// we will move the stack pointer by this number, forward aligned with the `stack_alignment`. |
| | 575 | stack_size: u32 = 0, |
| | 576 | /// The stack alignment, which is 16 bytes by default. This is specified by the |
| | 577 | /// tool-conventions: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md |
| | 578 | /// and also what the llvm backend will emit. |
| | 579 | /// However, local variables or the usage of `@setAlignStack` can overwrite this default. |
| | 580 | stack_alignment: u32 = 16, |
| 562 | | 581 | |
| 563 | const InnerError = error{ | 582 | const InnerError = error{ |
| 564 | OutOfMemory, | 583 | OutOfMemory, |
| ... | @@ -598,7 +617,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { | ... | @@ -598,7 +617,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { |
| 598 | // means we must generate it from a constant. | 617 | // means we must generate it from a constant. |
| 599 | const val = self.air.value(ref).?; | 618 | const val = self.air.value(ref).?; |
| 600 | const ty = self.air.typeOf(ref); | 619 | const ty = self.air.typeOf(ref); |
| 601 | if (!ty.hasRuntimeBits() and !ty.isInt()) return WValue{ .none = {} }; | 620 | if (!ty.hasRuntimeBits() and !ty.isInt()) { |
| | 621 | gop.value_ptr.* = WValue{ .none = {} }; |
| | 622 | return gop.value_ptr.*; |
| | 623 | } |
| 602 | | 624 | |
| 603 | // When we need to pass the value by reference (such as a struct), we will | 625 | // When we need to pass the value by reference (such as a struct), we will |
| 604 | // leverage `genTypedValue` to lower the constant to bytes and emit it | 626 | // leverage `genTypedValue` to lower the constant to bytes and emit it |
| ... | @@ -643,13 +665,6 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void { | ... | @@ -643,13 +665,6 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!void { |
| 643 | try self.mir_instructions.append(self.gpa, inst); | 665 | try self.mir_instructions.append(self.gpa, inst); |
| 644 | } | 666 | } |
| 645 | | 667 | |
| 646 | /// Inserts a Mir instruction at the given `offset`. | | |
| 647 | /// Asserts offset is within bound. | | |
| 648 | fn addInstAt(self: *Self, offset: usize, inst: Mir.Inst) error{OutOfMemory}!void { | | |
| 649 | try self.mir_instructions.ensureUnusedCapacity(self.gpa, 1); | | |
| 650 | self.mir_instructions.insertAssumeCapacity(offset, inst); | | |
| 651 | } | | |
| 652 | | | |
| 653 | fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void { | 668 | fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void { |
| 654 | try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); | 669 | try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); |
| 655 | } | 670 | } |
| ... | @@ -754,7 +769,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { | ... | @@ -754,7 +769,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { |
| 754 | .imm64 => |val| try self.addImm64(val), | 769 | .imm64 => |val| try self.addImm64(val), |
| 755 | .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), | 770 | .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), |
| 756 | .float64 => |val| try self.addFloat64(val), | 771 | .float64 => |val| try self.addFloat64(val), |
| 757 | .memory => |ptr| try self.addLabel(.memory_address, ptr), // write sybol address and generate relocation | 772 | .memory => |ptr| { |
| | 773 | const extra_index = try self.addExtra(Mir.Memory{ .pointer = ptr, .offset = 0 }); |
| | 774 | try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); |
| | 775 | }, |
| | 776 | .memory_offset => |mem_off| { |
| | 777 | const extra_index = try self.addExtra(Mir.Memory{ .pointer = mem_off.pointer, .offset = mem_off.offset }); |
| | 778 | try self.addInst(.{ .tag = .memory_address, .data = .{ .payload = extra_index } }); |
| | 779 | }, |
| 758 | .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation | 780 | .function_index => |index| try self.addLabel(.function_index, index), // write function index and generate relocation |
| 759 | } | 781 | } |
| 760 | } | 782 | } |
| ... | @@ -827,10 +849,43 @@ pub fn genFunc(self: *Self) InnerError!void { | ... | @@ -827,10 +849,43 @@ pub fn genFunc(self: *Self) InnerError!void { |
| 827 | try self.addTag(.@"unreachable"); | 849 | try self.addTag(.@"unreachable"); |
| 828 | } | 850 | } |
| 829 | } | 851 | } |
| 830 | | | |
| 831 | // End of function body | 852 | // End of function body |
| 832 | try self.addTag(.end); | 853 | try self.addTag(.end); |
| 833 | | 854 | |
| | 855 | // check if we have to initialize and allocate anything into the stack frame. |
| | 856 | // If so, create enough stack space and insert the instructions at the front of the list. |
| | 857 | if (self.stack_size > 0) { |
| | 858 | var prologue = std.ArrayList(Mir.Inst).init(self.gpa); |
| | 859 | defer prologue.deinit(); |
| | 860 | |
| | 861 | // load stack pointer |
| | 862 | try prologue.append(.{ .tag = .global_get, .data = .{ .label = 0 } }); |
| | 863 | // store stack pointer so we can restore it when we return from the function |
| | 864 | try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.initial_stack_value.local } }); |
| | 865 | // get the total stack size |
| | 866 | const aligned_stack = std.mem.alignForwardGeneric(u32, self.stack_size, self.stack_alignment); |
| | 867 | try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, aligned_stack) } }); |
| | 868 | // substract it from the current stack pointer |
| | 869 | try prologue.append(.{ .tag = .i32_sub, .data = .{ .tag = {} } }); |
| | 870 | // Get negative stack aligment |
| | 871 | try prologue.append(.{ .tag = .i32_const, .data = .{ .imm32 = @intCast(i32, self.stack_alignment) * -1 } }); |
| | 872 | // Bit and the value to get the new stack pointer to ensure the pointers are aligned with the abi alignment |
| | 873 | try prologue.append(.{ .tag = .i32_and, .data = .{ .tag = {} } }); |
| | 874 | // store the current stack pointer as the bottom, which will be used to calculate all stack pointer offsets |
| | 875 | try prologue.append(.{ .tag = .local_tee, .data = .{ .label = self.bottom_stack_value.local } }); |
| | 876 | // Store the current stack pointer value into the global stack pointer so other function calls will |
| | 877 | // start from this value instead and not overwrite the current stack. |
| | 878 | try prologue.append(.{ .tag = .global_set, .data = .{ .label = 0 } }); |
| | 879 | |
| | 880 | // reserve space and insert all prologue instructions at the front of the instruction list |
| | 881 | // We insert them in reserve order as there is no insertSlice in multiArrayList. |
| | 882 | try self.mir_instructions.ensureUnusedCapacity(self.gpa, prologue.items.len); |
| | 883 | for (prologue.items) |_, index| { |
| | 884 | const inst = prologue.items[prologue.items.len - 1 - index]; |
| | 885 | self.mir_instructions.insertAssumeCapacity(0, inst); |
| | 886 | } |
| | 887 | } |
| | 888 | |
| 834 | var mir: Mir = .{ | 889 | var mir: Mir = .{ |
| 835 | .instructions = self.mir_instructions.toOwnedSlice(), | 890 | .instructions = self.mir_instructions.toOwnedSlice(), |
| 836 | .extra = self.mir_extra.toOwnedSlice(self.gpa), | 891 | .extra = self.mir_extra.toOwnedSlice(self.gpa), |
| ... | @@ -927,7 +982,7 @@ pub const DeclGen = struct { | ... | @@ -927,7 +982,7 @@ pub const DeclGen = struct { |
| 927 | .function => val.castTag(.function).?.data.owner_decl, | 982 | .function => val.castTag(.function).?.data.owner_decl, |
| 928 | else => unreachable, | 983 | else => unreachable, |
| 929 | }; | 984 | }; |
| 930 | return try self.lowerDeclRef(ty, val, fn_decl); | 985 | return try self.lowerDeclRefValue(ty, val, fn_decl, 0); |
| 931 | }, | 986 | }, |
| 932 | .Optional => { | 987 | .Optional => { |
| 933 | var opt_buf: Type.Payload.ElemType = undefined; | 988 | var opt_buf: Type.Payload.ElemType = undefined; |
| ... | @@ -1115,11 +1170,11 @@ pub const DeclGen = struct { | ... | @@ -1115,11 +1170,11 @@ pub const DeclGen = struct { |
| 1115 | .Pointer => switch (val.tag()) { | 1170 | .Pointer => switch (val.tag()) { |
| 1116 | .variable => { | 1171 | .variable => { |
| 1117 | const decl = val.castTag(.variable).?.data.owner_decl; | 1172 | const decl = val.castTag(.variable).?.data.owner_decl; |
| 1118 | return self.lowerDeclRef(ty, val, decl); | 1173 | return self.lowerDeclRefValue(ty, val, decl, 0); |
| 1119 | }, | 1174 | }, |
| 1120 | .decl_ref => { | 1175 | .decl_ref => { |
| 1121 | const decl = val.castTag(.decl_ref).?.data; | 1176 | const decl = val.castTag(.decl_ref).?.data; |
| 1122 | return self.lowerDeclRef(ty, val, decl); | 1177 | return self.lowerDeclRefValue(ty, val, decl, 0); |
| 1123 | }, | 1178 | }, |
| 1124 | .slice => { | 1179 | .slice => { |
| 1125 | const slice = val.castTag(.slice).?.data; | 1180 | const slice = val.castTag(.slice).?.data; |
| ... | @@ -1139,6 +1194,13 @@ pub const DeclGen = struct { | ... | @@ -1139,6 +1194,13 @@ pub const DeclGen = struct { |
| 1139 | try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8)); | 1194 | try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8)); |
| 1140 | return Result{ .appended = {} }; | 1195 | return Result{ .appended = {} }; |
| 1141 | }, | 1196 | }, |
| | 1197 | .elem_ptr => { |
| | 1198 | const elem_ptr = val.castTag(.elem_ptr).?.data; |
| | 1199 | const elem_size = ty.childType().abiSize(self.target()); |
| | 1200 | const offset = elem_ptr.index * elem_size; |
| | 1201 | return self.lowerParentPtr(elem_ptr.array_ptr, @intCast(usize, offset)); |
| | 1202 | }, |
| | 1203 | .int_u64 => return self.genTypedValue(Type.usize, val), |
| 1142 | else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), | 1204 | else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), |
| 1143 | }, | 1205 | }, |
| 1144 | .ErrorUnion => { | 1206 | .ErrorUnion => { |
| ... | @@ -1179,7 +1241,36 @@ pub const DeclGen = struct { | ... | @@ -1179,7 +1241,36 @@ pub const DeclGen = struct { |
| 1179 | } | 1241 | } |
| 1180 | } | 1242 | } |
| 1181 | | 1243 | |
| 1182 | fn lowerDeclRef(self: *DeclGen, ty: Type, val: Value, decl: *Module.Decl) InnerError!Result { | 1244 | fn lowerParentPtr(self: *DeclGen, ptr_value: Value, offset: usize) InnerError!Result { |
| | 1245 | switch (ptr_value.tag()) { |
| | 1246 | .decl_ref => { |
| | 1247 | const decl = ptr_value.castTag(.decl_ref).?.data; |
| | 1248 | return self.lowerParentPtrDecl(ptr_value, decl, offset); |
| | 1249 | }, |
| | 1250 | else => |tag| return self.fail("TODO: Implement lowerParentPtr for pointer value tag: {s}", .{tag}), |
| | 1251 | } |
| | 1252 | } |
| | 1253 | |
| | 1254 | fn lowerParentPtrDecl(self: *DeclGen, ptr_val: Value, decl: *Module.Decl, offset: usize) InnerError!Result { |
| | 1255 | decl.markAlive(); |
| | 1256 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| | 1257 | .base = .{ .tag = .single_mut_pointer }, |
| | 1258 | .data = decl.ty, |
| | 1259 | }; |
| | 1260 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| | 1261 | return self.lowerDeclRefValue(ptr_ty, ptr_val, decl, offset); |
| | 1262 | } |
| | 1263 | |
| | 1264 | fn lowerDeclRefValue( |
| | 1265 | self: *DeclGen, |
| | 1266 | ty: Type, |
| | 1267 | val: Value, |
| | 1268 | /// The target decl that is being pointed to |
| | 1269 | decl: *Module.Decl, |
| | 1270 | /// When lowering to an indexed pointer, we can specify the offset |
| | 1271 | /// which will then be used as 'addend' to the relocation. |
| | 1272 | offset: usize, |
| | 1273 | ) InnerError!Result { |
| 1183 | const writer = self.code.writer(); | 1274 | const writer = self.code.writer(); |
| 1184 | if (ty.isSlice()) { | 1275 | if (ty.isSlice()) { |
| 1185 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 1276 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| ... | @@ -1202,6 +1293,7 @@ pub const DeclGen = struct { | ... | @@ -1202,6 +1293,7 @@ pub const DeclGen = struct { |
| 1202 | self.symbol_index, // source symbol index | 1293 | self.symbol_index, // source symbol index |
| 1203 | decl.link.wasm.sym_index, // target symbol index | 1294 | decl.link.wasm.sym_index, // target symbol index |
| 1204 | @intCast(u32, self.code.items.len), // offset | 1295 | @intCast(u32, self.code.items.len), // offset |
| | 1296 | @intCast(u32, offset), // addend |
| 1205 | )); | 1297 | )); |
| 1206 | return Result{ .appended = {} }; | 1298 | return Result{ .appended = {} }; |
| 1207 | } | 1299 | } |
| ... | @@ -1254,22 +1346,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu | ... | @@ -1254,22 +1346,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1254 | return result; | 1346 | return result; |
| 1255 | } | 1347 | } |
| 1256 | | 1348 | |
| 1257 | /// Retrieves the stack pointer's value from the global variable and stores | 1349 | /// Creates a local for the initial stack value |
| 1258 | /// it in a local | | |
| 1259 | /// Asserts `initial_stack_value` is `.none` | 1350 | /// Asserts `initial_stack_value` is `.none` |
| 1260 | fn initializeStack(self: *Self) !void { | 1351 | fn initializeStack(self: *Self) !void { |
| 1261 | assert(self.initial_stack_value == .none); | 1352 | assert(self.initial_stack_value == .none); |
| 1262 | // reserve space for immediate value | | |
| 1263 | // get stack pointer global | | |
| 1264 | try self.addLabel(.global_get, 0); | | |
| 1265 | | | |
| 1266 | // Reserve a local to store the current stack pointer | 1353 | // Reserve a local to store the current stack pointer |
| 1267 | // We can later use this local to set the stack pointer back to the value | 1354 | // We can later use this local to set the stack pointer back to the value |
| 1268 | // we have stored here. | 1355 | // we have stored here. |
| 1269 | self.initial_stack_value = try self.allocLocal(Type.initTag(.i32)); | 1356 | self.initial_stack_value = try self.allocLocal(Type.usize); |
| 1270 | | 1357 | // Also reserve a local to store the bottom stack value |
| 1271 | // save the value to the local | 1358 | self.bottom_stack_value = try self.allocLocal(Type.usize); |
| 1272 | try self.addLabel(.local_set, self.initial_stack_value.local); | | |
| 1273 | } | 1359 | } |
| 1274 | | 1360 | |
| 1275 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it | 1361 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it |
| ... | @@ -1284,36 +1370,75 @@ fn restoreStackPointer(self: *Self) !void { | ... | @@ -1284,36 +1370,75 @@ fn restoreStackPointer(self: *Self) !void { |
| 1284 | try self.addLabel(.global_set, 0); | 1370 | try self.addLabel(.global_set, 0); |
| 1285 | } | 1371 | } |
| 1286 | | 1372 | |
| 1287 | /// Moves the stack pointer by given `offset` | 1373 | /// Saves the current stack size's stack pointer position into a given local |
| 1288 | /// It does this by retrieving the stack pointer, subtracting `offset` and storing | 1374 | /// It does this by retrieving the bottom stack pointer, adding `self.stack_size` and storing |
| 1289 | /// the result back into the stack pointer. | 1375 | /// the result back into the local. |
| 1290 | fn moveStack(self: *Self, offset: u32, local: u32) !void { | 1376 | fn saveStack(self: *Self) !WValue { |
| 1291 | if (offset == 0) return; | 1377 | const local = try self.allocLocal(Type.usize); |
| 1292 | try self.addLabel(.global_get, 0); | 1378 | try self.addLabel(.local_get, self.bottom_stack_value.local); |
| 1293 | try self.addImm32(@bitCast(i32, offset)); | 1379 | try self.addImm32(@intCast(i32, self.stack_size)); |
| 1294 | try self.addTag(.i32_sub); | 1380 | try self.addTag(.i32_add); |
| 1295 | try self.addLabel(.local_tee, local); | 1381 | try self.addLabel(.local_set, local.local); |
| 1296 | try self.addLabel(.global_set, 0); | 1382 | return local; |
| 1297 | } | 1383 | } |
| 1298 | | 1384 | |
| 1299 | /// From a given type, will create space on the virtual stack to store the value of such type. | 1385 | /// From a given type, will create space on the virtual stack to store the value of such type. |
| 1300 | /// This returns a `WValue` with its active tag set to `local`, containing the index to the local | 1386 | /// This returns a `WValue` with its active tag set to `local`, containing the index to the local |
| 1301 | /// that points to the position on the virtual stack. This function should be used instead of | 1387 | /// that points to the position on the virtual stack. This function should be used instead of |
| 1302 | /// moveStack unless a local was already created to store the point. | 1388 | /// moveStack unless a local was already created to store the pointer. |
| 1303 | /// | 1389 | /// |
| 1304 | /// Asserts Type has codegenbits | 1390 | /// Asserts Type has codegenbits |
| 1305 | fn allocStack(self: *Self, ty: Type) !WValue { | 1391 | fn allocStack(self: *Self, ty: Type) !WValue { |
| 1306 | assert(ty.hasRuntimeBits()); | 1392 | assert(ty.hasRuntimeBits()); |
| | 1393 | if (self.initial_stack_value == .none) { |
| | 1394 | try self.initializeStack(); |
| | 1395 | } |
| 1307 | | 1396 | |
| 1308 | // calculate needed stack space | | |
| 1309 | const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { | 1397 | const abi_size = std.math.cast(u32, ty.abiSize(self.target)) catch { |
| 1310 | return self.fail("Given type '{}' too big to fit into stack frame", .{ty}); | 1398 | return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ ty, ty.abiSize(self.target) }); |
| 1311 | }; | 1399 | }; |
| | 1400 | const abi_align = ty.abiAlignment(self.target); |
| 1312 | | 1401 | |
| 1313 | // allocate a local using wasm's pointer size | 1402 | if (abi_align > self.stack_alignment) { |
| 1314 | const local = try self.allocLocal(Type.@"usize"); | 1403 | self.stack_alignment = abi_align; |
| 1315 | try self.moveStack(abi_size, local.local); | 1404 | } |
| 1316 | return local; | 1405 | |
| | 1406 | const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_align); |
| | 1407 | defer self.stack_size = offset + abi_size; |
| | 1408 | |
| | 1409 | // store the stack pointer and return a local to it |
| | 1410 | return self.saveStack(); |
| | 1411 | } |
| | 1412 | |
| | 1413 | /// From a given AIR instruction generates a pointer to the stack where |
| | 1414 | /// the value of its type will live. |
| | 1415 | /// This is different from allocStack where this will use the pointer's alignment |
| | 1416 | /// if it is set, to ensure the stack alignment will be set correctly. |
| | 1417 | fn allocStackPtr(self: *Self, inst: Air.Inst.Index) !WValue { |
| | 1418 | const ptr_ty = self.air.typeOfIndex(inst); |
| | 1419 | const pointee_ty = ptr_ty.childType(); |
| | 1420 | |
| | 1421 | if (self.initial_stack_value == .none) { |
| | 1422 | try self.initializeStack(); |
| | 1423 | } |
| | 1424 | |
| | 1425 | if (!pointee_ty.hasRuntimeBits()) { |
| | 1426 | return self.allocStack(Type.usize); // create a value containing just the stack pointer. |
| | 1427 | } |
| | 1428 | |
| | 1429 | const abi_alignment = ptr_ty.ptrAlignment(self.target); |
| | 1430 | const abi_size = std.math.cast(u32, pointee_ty.abiSize(self.target)) catch { |
| | 1431 | return self.fail("Type {} with ABI size of {d} exceeds stack frame size", .{ pointee_ty, pointee_ty.abiSize(self.target) }); |
| | 1432 | }; |
| | 1433 | if (abi_alignment > self.stack_alignment) { |
| | 1434 | self.stack_alignment = abi_alignment; |
| | 1435 | } |
| | 1436 | |
| | 1437 | const offset = std.mem.alignForwardGeneric(u32, self.stack_size, abi_alignment); |
| | 1438 | defer self.stack_size = offset + abi_size; |
| | 1439 | |
| | 1440 | // store the stack pointer and return a local to it |
| | 1441 | return self.saveStack(); |
| 1317 | } | 1442 | } |
| 1318 | | 1443 | |
| 1319 | /// From given zig bitsize, returns the wasm bitsize | 1444 | /// From given zig bitsize, returns the wasm bitsize |
| ... | @@ -1592,6 +1717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -1592,6 +1717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 1592 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1717 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1593 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1718 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1594 | const operand = try self.resolveInst(un_op); | 1719 | const operand = try self.resolveInst(un_op); |
| | 1720 | |
| 1595 | // result must be stored in the stack and we return a pointer | 1721 | // result must be stored in the stack and we return a pointer |
| 1596 | // to the stack instead | 1722 | // to the stack instead |
| 1597 | if (self.return_value != .none) { | 1723 | if (self.return_value != .none) { |
| ... | @@ -1601,7 +1727,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1601,7 +1727,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1601 | } | 1727 | } |
| 1602 | try self.restoreStackPointer(); | 1728 | try self.restoreStackPointer(); |
| 1603 | try self.addTag(.@"return"); | 1729 | try self.addTag(.@"return"); |
| 1604 | return .none; | 1730 | return WValue{ .none = {} }; |
| 1605 | } | 1731 | } |
| 1606 | | 1732 | |
| 1607 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1733 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1611,12 +1737,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1611,12 +1737,7 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1611 | if (isByRef(child_type, self.target)) { | 1737 | if (isByRef(child_type, self.target)) { |
| 1612 | return self.return_value; | 1738 | return self.return_value; |
| 1613 | } | 1739 | } |
| 1614 | | 1740 | return self.allocStackPtr(inst); |
| 1615 | // Initialize the stack | | |
| 1616 | if (self.initial_stack_value == .none) { | | |
| 1617 | try self.initializeStack(); | | |
| 1618 | } | | |
| 1619 | return self.allocStack(child_type); | | |
| 1620 | } | 1741 | } |
| 1621 | | 1742 | |
| 1622 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1743 | fn airRetLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1708,20 +1829,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1708,20 +1829,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1708 | } | 1829 | } |
| 1709 | | 1830 | |
| 1710 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1831 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1711 | const pointee_type = self.air.typeOfIndex(inst).childType(); | 1832 | return self.allocStackPtr(inst); |
| 1712 | | | |
| 1713 | // Initialize the stack | | |
| 1714 | if (self.initial_stack_value == .none) { | | |
| 1715 | try self.initializeStack(); | | |
| 1716 | } | | |
| 1717 | | | |
| 1718 | if (!pointee_type.hasRuntimeBits()) { | | |
| 1719 | // when the pointee is zero-sized, we still want to create a pointer. | | |
| 1720 | // but instead use a default pointer type as storage. | | |
| 1721 | const zero_ptr = try self.allocStack(Type.usize); | | |
| 1722 | return zero_ptr; | | |
| 1723 | } | | |
| 1724 | return self.allocStack(pointee_type); | | |
| 1725 | } | 1833 | } |
| 1726 | | 1834 | |
| 1727 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1835 | fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -1741,11 +1849,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1741,11 +1849,10 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1741 | const err_ty = ty.errorUnionSet(); | 1849 | const err_ty = ty.errorUnionSet(); |
| 1742 | const pl_ty = ty.errorUnionPayload(); | 1850 | const pl_ty = ty.errorUnionPayload(); |
| 1743 | if (!pl_ty.hasRuntimeBits()) { | 1851 | if (!pl_ty.hasRuntimeBits()) { |
| 1744 | const err_val = try self.load(rhs, err_ty, 0); | 1852 | return self.store(lhs, rhs, err_ty, 0); |
| 1745 | return self.store(lhs, err_val, err_ty, 0); | | |
| 1746 | } | 1853 | } |
| 1747 | | 1854 | |
| 1748 | return try self.memCopy(ty, lhs, rhs); | 1855 | return self.memCopy(ty, lhs, rhs); |
| 1749 | }, | 1856 | }, |
| 1750 | .Optional => { | 1857 | .Optional => { |
| 1751 | if (ty.isPtrLikeOptional()) { | 1858 | if (ty.isPtrLikeOptional()) { |
| ... | @@ -1760,7 +1867,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1760,7 +1867,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1760 | return self.memCopy(ty, lhs, rhs); | 1867 | return self.memCopy(ty, lhs, rhs); |
| 1761 | }, | 1868 | }, |
| 1762 | .Struct, .Array, .Union => { | 1869 | .Struct, .Array, .Union => { |
| 1763 | return try self.memCopy(ty, lhs, rhs); | 1870 | return self.memCopy(ty, lhs, rhs); |
| 1764 | }, | 1871 | }, |
| 1765 | .Pointer => { | 1872 | .Pointer => { |
| 1766 | if (ty.isSlice()) { | 1873 | if (ty.isSlice()) { |
| ... | @@ -1775,7 +1882,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro | ... | @@ -1775,7 +1882,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1775 | } | 1882 | } |
| 1776 | }, | 1883 | }, |
| 1777 | .Int => if (ty.intInfo(self.target).bits > 64) { | 1884 | .Int => if (ty.intInfo(self.target).bits > 64) { |
| 1778 | return try self.memCopy(ty, lhs, rhs); | 1885 | return self.memCopy(ty, lhs, rhs); |
| 1779 | }, | 1886 | }, |
| 1780 | else => {}, | 1887 | else => {}, |
| 1781 | } | 1888 | } |
| ... | @@ -1974,6 +2081,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { | ... | @@ -1974,6 +2081,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 1974 | return WValue{ .function_index = target_sym_index }; | 2081 | return WValue{ .function_index = target_sym_index }; |
| 1975 | } else return WValue{ .memory = target_sym_index }; | 2082 | } else return WValue{ .memory = target_sym_index }; |
| 1976 | }, | 2083 | }, |
| | 2084 | .elem_ptr => { |
| | 2085 | const elem_ptr = val.castTag(.elem_ptr).?.data; |
| | 2086 | const index = elem_ptr.index; |
| | 2087 | const offset = index * ty.childType().abiSize(self.target); |
| | 2088 | const array_ptr = try self.lowerConstant(elem_ptr.array_ptr, ty); |
| | 2089 | |
| | 2090 | return WValue{ .memory_offset = .{ |
| | 2091 | .pointer = array_ptr.memory, |
| | 2092 | .offset = @intCast(u32, offset), |
| | 2093 | } }; |
| | 2094 | }, |
| 1977 | .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, | 2095 | .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, |
| 1978 | .zero, .null_value => return WValue{ .imm32 = 0 }, | 2096 | .zero, .null_value => return WValue{ .imm32 = 0 }, |
| 1979 | else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}), | 2097 | else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}), |
| ... | @@ -2524,11 +2642,11 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue | ... | @@ -2524,11 +2642,11 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue |
| 2524 | if (isByRef(payload_ty, self.target)) { | 2642 | if (isByRef(payload_ty, self.target)) { |
| 2525 | return self.buildPointerOffset(operand, offset, .new); | 2643 | return self.buildPointerOffset(operand, offset, .new); |
| 2526 | } | 2644 | } |
| 2527 | return try self.load(operand, payload_ty, offset); | 2645 | return self.load(operand, payload_ty, offset); |
| 2528 | } | 2646 | } |
| 2529 | | 2647 | |
| 2530 | fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2648 | fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2531 | if (self.liveness.isUnused(inst)) return WValue.none; | 2649 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2532 | | 2650 | |
| 2533 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2651 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2534 | const operand = try self.resolveInst(ty_op.operand); | 2652 | const operand = try self.resolveInst(ty_op.operand); |
| ... | @@ -2538,11 +2656,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2538,11 +2656,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2538 | return operand; | 2656 | return operand; |
| 2539 | } | 2657 | } |
| 2540 | | 2658 | |
| 2541 | return try self.load(operand, err_ty.errorUnionSet(), 0); | 2659 | return self.load(operand, err_ty.errorUnionSet(), 0); |
| 2542 | } | 2660 | } |
| 2543 | | 2661 | |
| 2544 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2662 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2545 | if (self.liveness.isUnused(inst)) return WValue.none; | 2663 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2664 | |
| 2546 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2665 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2547 | const operand = try self.resolveInst(ty_op.operand); | 2666 | const operand = try self.resolveInst(ty_op.operand); |
| 2548 | | 2667 | |
| ... | @@ -2564,11 +2683,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2564,11 +2683,14 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2564 | } | 2683 | } |
| 2565 | | 2684 | |
| 2566 | fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2685 | fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2567 | if (self.liveness.isUnused(inst)) return WValue.none; | 2686 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| | 2687 | |
| 2568 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2688 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2569 | const operand = try self.resolveInst(ty_op.operand); | 2689 | const operand = try self.resolveInst(ty_op.operand); |
| 2570 | const err_ty = self.air.getRefType(ty_op.ty); | 2690 | const err_ty = self.air.getRefType(ty_op.ty); |
| 2571 | | 2691 | |
| | 2692 | if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand; |
| | 2693 | |
| 2572 | const err_union = try self.allocStack(err_ty); | 2694 | const err_union = try self.allocStack(err_ty); |
| 2573 | // TODO: Also write 'undefined' to the payload | 2695 | // TODO: Also write 'undefined' to the payload |
| 2574 | try self.store(err_union, operand, err_ty.errorUnionSet(), 0); | 2696 | try self.store(err_union, operand, err_ty.errorUnionSet(), 0); |
| ... | @@ -2750,16 +2872,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2750,16 +2872,16 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2750 | } | 2872 | } |
| 2751 | | 2873 | |
| 2752 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2874 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2753 | if (self.liveness.isUnused(inst)) return WValue.none; | 2875 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2754 | | 2876 | |
| 2755 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2877 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2756 | const operand = try self.resolveInst(ty_op.operand); | 2878 | const operand = try self.resolveInst(ty_op.operand); |
| 2757 | | 2879 | |
| 2758 | return try self.load(operand, Type.usize, self.ptrSize()); | 2880 | return self.load(operand, Type.usize, self.ptrSize()); |
| 2759 | } | 2881 | } |
| 2760 | | 2882 | |
| 2761 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2883 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2762 | if (self.liveness.isUnused(inst)) return WValue.none; | 2884 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2763 | | 2885 | |
| 2764 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 2886 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2765 | const slice_ty = self.air.typeOf(bin_op.lhs); | 2887 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| ... | @@ -2784,7 +2906,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2784,7 +2906,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2784 | if (isByRef(elem_ty, self.target)) { | 2906 | if (isByRef(elem_ty, self.target)) { |
| 2785 | return result; | 2907 | return result; |
| 2786 | } | 2908 | } |
| 2787 | return try self.load(result, elem_ty, 0); | 2909 | return self.load(result, elem_ty, 0); |
| 2788 | } | 2910 | } |
| 2789 | | 2911 | |
| 2790 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2912 | fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2812,10 +2934,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2812,10 +2934,10 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2812 | } | 2934 | } |
| 2813 | | 2935 | |
| 2814 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2936 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2815 | if (self.liveness.isUnused(inst)) return WValue.none; | 2937 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2816 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 2938 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2817 | const operand = try self.resolveInst(ty_op.operand); | 2939 | const operand = try self.resolveInst(ty_op.operand); |
| 2818 | return try self.load(operand, Type.usize, 0); | 2940 | return self.load(operand, Type.usize, 0); |
| 2819 | } | 2941 | } |
| 2820 | | 2942 | |
| 2821 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 2943 | fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2880,7 +3002,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2880,7 +3002,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2880 | | 3002 | |
| 2881 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3003 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2882 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 3004 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2883 | return try self.resolveInst(un_op); | 3005 | return self.resolveInst(un_op); |
| 2884 | } | 3006 | } |
| 2885 | | 3007 | |
| 2886 | fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3008 | fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2912,7 +3034,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2912,7 +3034,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2912 | fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3034 | fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2913 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; | 3035 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2914 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 3036 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2915 | return try self.resolveInst(un_op); | 3037 | return self.resolveInst(un_op); |
| 2916 | } | 3038 | } |
| 2917 | | 3039 | |
| 2918 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3040 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2927,7 +3049,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2927,7 +3049,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2927 | | 3049 | |
| 2928 | // load pointer onto the stack | 3050 | // load pointer onto the stack |
| 2929 | if (ptr_ty.isSlice()) { | 3051 | if (ptr_ty.isSlice()) { |
| 2930 | const ptr_local = try self.load(pointer, ptr_ty, 0); | 3052 | const ptr_local = try self.load(pointer, Type.usize, 0); |
| 2931 | try self.addLabel(.local_get, ptr_local.local); | 3053 | try self.addLabel(.local_get, ptr_local.local); |
| 2932 | } else { | 3054 | } else { |
| 2933 | try self.emitWValue(pointer); | 3055 | try self.emitWValue(pointer); |
| ... | @@ -2944,7 +3066,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2944,7 +3066,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2944 | if (isByRef(elem_ty, self.target)) { | 3066 | if (isByRef(elem_ty, self.target)) { |
| 2945 | return result; | 3067 | return result; |
| 2946 | } | 3068 | } |
| 2947 | return try self.load(result, elem_ty, 0); | 3069 | return self.load(result, elem_ty, 0); |
| 2948 | } | 3070 | } |
| 2949 | | 3071 | |
| 2950 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3072 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -2960,7 +3082,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -2960,7 +3082,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2960 | | 3082 | |
| 2961 | // load pointer onto the stack | 3083 | // load pointer onto the stack |
| 2962 | if (ptr_ty.isSlice()) { | 3084 | if (ptr_ty.isSlice()) { |
| 2963 | const ptr_local = try self.load(ptr, ptr_ty, 0); | 3085 | const ptr_local = try self.load(ptr, Type.usize, 0); |
| 2964 | try self.addLabel(.local_get, ptr_local.local); | 3086 | try self.addLabel(.local_get, ptr_local.local); |
| 2965 | } else { | 3087 | } else { |
| 2966 | try self.emitWValue(ptr); | 3088 | try self.emitWValue(ptr); |
| ... | @@ -3094,7 +3216,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3094,7 +3216,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3094 | if (isByRef(elem_ty, self.target)) { | 3216 | if (isByRef(elem_ty, self.target)) { |
| 3095 | return result; | 3217 | return result; |
| 3096 | } | 3218 | } |
| 3097 | return try self.load(result, elem_ty, 0); | 3219 | return self.load(result, elem_ty, 0); |
| 3098 | } | 3220 | } |
| 3099 | | 3221 | |
| 3100 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3222 | fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -3138,8 +3260,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -3138,8 +3260,63 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3138 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 3260 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3139 | const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); | 3261 | const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); |
| 3140 | | 3262 | |
| 3141 | _ = elements; | 3263 | switch (vector_ty.zigTypeTag()) { |
| 3142 | return self.fail("TODO: Wasm backend: implement airVectorInit", .{}); | 3264 | .Vector => return self.fail("TODO: Wasm backend: implement airVectorInit for vectors", .{}), |
| | 3265 | .Array => { |
| | 3266 | const result = try self.allocStack(vector_ty); |
| | 3267 | const elem_ty = vector_ty.childType(); |
| | 3268 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target)); |
| | 3269 | |
| | 3270 | // When the element type is by reference, we must copy the entire |
| | 3271 | // value. It is therefore safer to move the offset pointer and store |
| | 3272 | // each value individually, instead of using store offsets. |
| | 3273 | if (isByRef(elem_ty, self.target)) { |
| | 3274 | // copy stack pointer into a temporary local, which is |
| | 3275 | // moved for each element to store each value in the right position. |
| | 3276 | const offset = try self.allocLocal(Type.usize); |
| | 3277 | try self.emitWValue(result); |
| | 3278 | try self.addLabel(.local_set, offset.local); |
| | 3279 | for (elements) |elem, elem_index| { |
| | 3280 | const elem_val = try self.resolveInst(elem); |
| | 3281 | try self.store(offset, elem_val, elem_ty, 0); |
| | 3282 | |
| | 3283 | if (elem_index < elements.len - 1) { |
| | 3284 | _ = try self.buildPointerOffset(offset, elem_size, .modify); |
| | 3285 | } |
| | 3286 | } |
| | 3287 | } else { |
| | 3288 | var offset: u32 = 0; |
| | 3289 | for (elements) |elem| { |
| | 3290 | const elem_val = try self.resolveInst(elem); |
| | 3291 | try self.store(result, elem_val, elem_ty, offset); |
| | 3292 | offset += elem_size; |
| | 3293 | } |
| | 3294 | } |
| | 3295 | return result; |
| | 3296 | }, |
| | 3297 | .Struct => { |
| | 3298 | const tuple = vector_ty.castTag(.tuple).?.data; |
| | 3299 | const result = try self.allocStack(vector_ty); |
| | 3300 | const offset = try self.allocLocal(Type.usize); // pointer to offset |
| | 3301 | try self.emitWValue(result); |
| | 3302 | try self.addLabel(.local_set, offset.local); |
| | 3303 | for (elements) |elem, elem_index| { |
| | 3304 | if (tuple.values[elem_index].tag() != .unreachable_value) continue; |
| | 3305 | |
| | 3306 | const elem_ty = tuple.types[elem_index]; |
| | 3307 | const elem_size = @intCast(u32, elem_ty.abiSize(self.target)); |
| | 3308 | const value = try self.resolveInst(elem); |
| | 3309 | try self.store(offset, value, elem_ty, 0); |
| | 3310 | |
| | 3311 | if (elem_index < elements.len - 1) { |
| | 3312 | _ = try self.buildPointerOffset(offset, elem_size, .modify); |
| | 3313 | } |
| | 3314 | } |
| | 3315 | |
| | 3316 | return result; |
| | 3317 | }, |
| | 3318 | else => unreachable, |
| | 3319 | } |
| 3143 | } | 3320 | } |
| 3144 | | 3321 | |
| 3145 | fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 3322 | fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |