| ... | ... | @@ -39,6 +39,14 @@ const WValue = union(enum) { |
| 39 | 39 | /// Note: The value contains the symbol index, rather than the actual address |
| 40 | 40 | /// as we use this to perform the relocation. |
| 41 | 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 | 50 | /// Represents a function pointer |
| 43 | 51 | /// In wasm function pointers are indexes into a function table, |
| 44 | 52 | /// rather than an address in the data section. |
| ... | ... | @@ -552,6 +560,9 @@ mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 552 | 560 | /// When a function is executing, we store the the current stack pointer's value within this local. |
| 553 | 561 | /// This value is then used to restore the stack pointer to the original value at the return of the function. |
| 554 | 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 | 566 | /// Arguments of this function declaration |
| 556 | 567 | /// This will be set after `resolveCallingConventionValues` |
| 557 | 568 | args: []WValue = &.{}, |
| ... | ... | @@ -559,6 +570,14 @@ args: []WValue = &.{}, |
| 559 | 570 | /// When it returns a pointer to the stack, the `.local` tag will be active and must be populated |
| 560 | 571 | /// before this function returns its execution to the caller. |
| 561 | 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 | 582 | const InnerError = error{ |
| 564 | 583 | OutOfMemory, |
| ... | ... | @@ -598,7 +617,10 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!WValue { |
| 598 | 617 | // means we must generate it from a constant. |
| 599 | 618 | const val = self.air.value(ref).?; |
| 600 | 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 | 625 | // When we need to pass the value by reference (such as a struct), we will |
| 604 | 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 | 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 | 668 | fn addTag(self: *Self, tag: Mir.Inst.Tag) error{OutOfMemory}!void { |
| 654 | 669 | try self.addInst(.{ .tag = tag, .data = .{ .tag = {} } }); |
| 655 | 670 | } |
| ... | ... | @@ -754,7 +769,14 @@ fn emitWValue(self: *Self, value: WValue) InnerError!void { |
| 754 | 769 | .imm64 => |val| try self.addImm64(val), |
| 755 | 770 | .float32 => |val| try self.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), |
| 756 | 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 | 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 | 849 | try self.addTag(.@"unreachable"); |
| 828 | 850 | } |
| 829 | 851 | } |
| 830 | | |
| 831 | 852 | // End of function body |
| 832 | 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 | 889 | var mir: Mir = .{ |
| 835 | 890 | .instructions = self.mir_instructions.toOwnedSlice(), |
| 836 | 891 | .extra = self.mir_extra.toOwnedSlice(self.gpa), |
| ... | ... | @@ -927,7 +982,7 @@ pub const DeclGen = struct { |
| 927 | 982 | .function => val.castTag(.function).?.data.owner_decl, |
| 928 | 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 | 987 | .Optional => { |
| 933 | 988 | var opt_buf: Type.Payload.ElemType = undefined; |
| ... | ... | @@ -1115,11 +1170,11 @@ pub const DeclGen = struct { |
| 1115 | 1170 | .Pointer => switch (val.tag()) { |
| 1116 | 1171 | .variable => { |
| 1117 | 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 | 1175 | .decl_ref => { |
| 1121 | 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 | 1179 | .slice => { |
| 1125 | 1180 | const slice = val.castTag(.slice).?.data; |
| ... | ... | @@ -1139,6 +1194,13 @@ pub const DeclGen = struct { |
| 1139 | 1194 | try writer.writeByteNTimes(0, @divExact(self.target().cpu.arch.ptrBitWidth(), 8)); |
| 1140 | 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 | 1204 | else => return self.fail("TODO: Implement zig decl gen for pointer type value: '{s}'", .{@tagName(val.tag())}), |
| 1143 | 1205 | }, |
| 1144 | 1206 | .ErrorUnion => { |
| ... | ... | @@ -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 | 1274 | const writer = self.code.writer(); |
| 1184 | 1275 | if (ty.isSlice()) { |
| 1185 | 1276 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| ... | ... | @@ -1202,6 +1293,7 @@ pub const DeclGen = struct { |
| 1202 | 1293 | self.symbol_index, // source symbol index |
| 1203 | 1294 | decl.link.wasm.sym_index, // target symbol index |
| 1204 | 1295 | @intCast(u32, self.code.items.len), // offset |
| 1296 | @intCast(u32, offset), // addend |
| 1205 | 1297 | )); |
| 1206 | 1298 | return Result{ .appended = {} }; |
| 1207 | 1299 | } |
| ... | ... | @@ -1254,22 +1346,16 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) InnerError!CallWValu |
| 1254 | 1346 | return result; |
| 1255 | 1347 | } |
| 1256 | 1348 | |
| 1257 | | /// Retrieves the stack pointer's value from the global variable and stores |
| 1258 | | /// it in a local |
| 1349 | /// Creates a local for the initial stack value |
| 1259 | 1350 | /// Asserts `initial_stack_value` is `.none` |
| 1260 | 1351 | fn initializeStack(self: *Self) !void { |
| 1261 | 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 | 1353 | // Reserve a local to store the current stack pointer |
| 1267 | 1354 | // We can later use this local to set the stack pointer back to the value |
| 1268 | 1355 | // we have stored here. |
| 1269 | | self.initial_stack_value = try self.allocLocal(Type.initTag(.i32)); |
| 1270 | | |
| 1271 | | // save the value to the local |
| 1272 | | try self.addLabel(.local_set, self.initial_stack_value.local); |
| 1356 | self.initial_stack_value = try self.allocLocal(Type.usize); |
| 1357 | // Also reserve a local to store the bottom stack value |
| 1358 | self.bottom_stack_value = try self.allocLocal(Type.usize); |
| 1273 | 1359 | } |
| 1274 | 1360 | |
| 1275 | 1361 | /// Reads the stack pointer from `Context.initial_stack_value` and writes it |
| ... | ... | @@ -1284,36 +1370,75 @@ fn restoreStackPointer(self: *Self) !void { |
| 1284 | 1370 | try self.addLabel(.global_set, 0); |
| 1285 | 1371 | } |
| 1286 | 1372 | |
| 1287 | | /// Moves the stack pointer by given `offset` |
| 1288 | | /// It does this by retrieving the stack pointer, subtracting `offset` and storing |
| 1289 | | /// the result back into the stack pointer. |
| 1290 | | fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| 1291 | | if (offset == 0) return; |
| 1292 | | try self.addLabel(.global_get, 0); |
| 1293 | | try self.addImm32(@bitCast(i32, offset)); |
| 1294 | | try self.addTag(.i32_sub); |
| 1295 | | try self.addLabel(.local_tee, local); |
| 1296 | | try self.addLabel(.global_set, 0); |
| 1373 | /// Saves the current stack size's stack pointer position into a given local |
| 1374 | /// It does this by retrieving the bottom stack pointer, adding `self.stack_size` and storing |
| 1375 | /// the result back into the local. |
| 1376 | fn saveStack(self: *Self) !WValue { |
| 1377 | const local = try self.allocLocal(Type.usize); |
| 1378 | try self.addLabel(.local_get, self.bottom_stack_value.local); |
| 1379 | try self.addImm32(@intCast(i32, self.stack_size)); |
| 1380 | try self.addTag(.i32_add); |
| 1381 | try self.addLabel(.local_set, local.local); |
| 1382 | return local; |
| 1297 | 1383 | } |
| 1298 | 1384 | |
| 1299 | 1385 | /// From a given type, will create space on the virtual stack to store the value of such type. |
| 1300 | 1386 | /// This returns a `WValue` with its active tag set to `local`, containing the index to the local |
| 1301 | 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 | 1390 | /// Asserts Type has codegenbits |
| 1305 | 1391 | fn allocStack(self: *Self, ty: Type) !WValue { |
| 1306 | 1392 | assert(ty.hasRuntimeBits()); |
| 1393 | if (self.initial_stack_value == .none) { |
| 1394 | try self.initializeStack(); |
| 1395 | } |
| 1307 | 1396 | |
| 1308 | | // calculate needed stack space |
| 1309 | 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 |
| 1314 | | const local = try self.allocLocal(Type.@"usize"); |
| 1315 | | try self.moveStack(abi_size, local.local); |
| 1316 | | return local; |
| 1402 | if (abi_align > self.stack_alignment) { |
| 1403 | self.stack_alignment = abi_align; |
| 1404 | } |
| 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 | 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 | 1717 | fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1593 | 1718 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1594 | 1719 | const operand = try self.resolveInst(un_op); |
| 1720 | |
| 1595 | 1721 | // result must be stored in the stack and we return a pointer |
| 1596 | 1722 | // to the stack instead |
| 1597 | 1723 | if (self.return_value != .none) { |
| ... | ... | @@ -1601,7 +1727,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1601 | 1727 | } |
| 1602 | 1728 | try self.restoreStackPointer(); |
| 1603 | 1729 | try self.addTag(.@"return"); |
| 1604 | | return .none; |
| 1730 | return WValue{ .none = {} }; |
| 1605 | 1731 | } |
| 1606 | 1732 | |
| 1607 | 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 | 1737 | if (isByRef(child_type, self.target)) { |
| 1612 | 1738 | return self.return_value; |
| 1613 | 1739 | } |
| 1614 | | |
| 1615 | | // Initialize the stack |
| 1616 | | if (self.initial_stack_value == .none) { |
| 1617 | | try self.initializeStack(); |
| 1618 | | } |
| 1619 | | return self.allocStack(child_type); |
| 1740 | return self.allocStackPtr(inst); |
| 1620 | 1741 | } |
| 1621 | 1742 | |
| 1622 | 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 | 1829 | } |
| 1709 | 1830 | |
| 1710 | 1831 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1711 | | const pointee_type = self.air.typeOfIndex(inst).childType(); |
| 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); |
| 1832 | return self.allocStackPtr(inst); |
| 1725 | 1833 | } |
| 1726 | 1834 | |
| 1727 | 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 | 1849 | const err_ty = ty.errorUnionSet(); |
| 1742 | 1850 | const pl_ty = ty.errorUnionPayload(); |
| 1743 | 1851 | if (!pl_ty.hasRuntimeBits()) { |
| 1744 | | const err_val = try self.load(rhs, err_ty, 0); |
| 1745 | | return self.store(lhs, err_val, err_ty, 0); |
| 1852 | return self.store(lhs, rhs, 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 | 1857 | .Optional => { |
| 1751 | 1858 | if (ty.isPtrLikeOptional()) { |
| ... | ... | @@ -1760,7 +1867,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1760 | 1867 | return self.memCopy(ty, lhs, rhs); |
| 1761 | 1868 | }, |
| 1762 | 1869 | .Struct, .Array, .Union => { |
| 1763 | | return try self.memCopy(ty, lhs, rhs); |
| 1870 | return self.memCopy(ty, lhs, rhs); |
| 1764 | 1871 | }, |
| 1765 | 1872 | .Pointer => { |
| 1766 | 1873 | if (ty.isSlice()) { |
| ... | ... | @@ -1775,7 +1882,7 @@ fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerErro |
| 1775 | 1882 | } |
| 1776 | 1883 | }, |
| 1777 | 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 | 1887 | else => {}, |
| 1781 | 1888 | } |
| ... | ... | @@ -1974,6 +2081,17 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue { |
| 1974 | 2081 | return WValue{ .function_index = target_sym_index }; |
| 1975 | 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 | 2095 | .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt()) }, |
| 1978 | 2096 | .zero, .null_value => return WValue{ .imm32 = 0 }, |
| 1979 | 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 | 2642 | if (isByRef(payload_ty, self.target)) { |
| 2525 | 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 | 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 | 2651 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2534 | 2652 | const operand = try self.resolveInst(ty_op.operand); |
| ... | ... | @@ -2538,11 +2656,12 @@ fn airUnwrapErrUnionError(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2538 | 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 | 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 | 2665 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2547 | 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 | 2683 | } |
| 2565 | 2684 | |
| 2566 | 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 | 2688 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2569 | 2689 | const operand = try self.resolveInst(ty_op.operand); |
| 2570 | 2690 | const err_ty = self.air.getRefType(ty_op.ty); |
| 2571 | 2691 | |
| 2692 | if (!err_ty.errorUnionPayload().hasRuntimeBits()) return operand; |
| 2693 | |
| 2572 | 2694 | const err_union = try self.allocStack(err_ty); |
| 2573 | 2695 | // TODO: Also write 'undefined' to the payload |
| 2574 | 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 | 2872 | } |
| 2751 | 2873 | |
| 2752 | 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 | 2877 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2756 | 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 | 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 | 2886 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2765 | 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 | 2906 | if (isByRef(elem_ty, self.target)) { |
| 2785 | 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 | 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 | 2934 | } |
| 2813 | 2935 | |
| 2814 | 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 | 2938 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2817 | 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 | 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 | 3002 | |
| 2881 | 3003 | fn airBoolToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2882 | 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 | 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 | 3034 | fn airPtrToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2913 | 3035 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 2914 | 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 | 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 | 3049 | |
| 2928 | 3050 | // load pointer onto the stack |
| 2929 | 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 | 3053 | try self.addLabel(.local_get, ptr_local.local); |
| 2932 | 3054 | } else { |
| 2933 | 3055 | try self.emitWValue(pointer); |
| ... | ... | @@ -2944,7 +3066,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 2944 | 3066 | if (isByRef(elem_ty, self.target)) { |
| 2945 | 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 | 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 | 3082 | |
| 2961 | 3083 | // load pointer onto the stack |
| 2962 | 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 | 3086 | try self.addLabel(.local_get, ptr_local.local); |
| 2965 | 3087 | } else { |
| 2966 | 3088 | try self.emitWValue(ptr); |
| ... | ... | @@ -3094,7 +3216,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 3094 | 3216 | if (isByRef(elem_ty, self.target)) { |
| 3095 | 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 | 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 | 3260 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3139 | 3261 | const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); |
| 3140 | 3262 | |
| 3141 | | _ = elements; |
| 3142 | | return self.fail("TODO: Wasm backend: implement airVectorInit", .{}); |
| 3263 | switch (vector_ty.zigTypeTag()) { |
| 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 | 3322 | fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |