| ... | ... | @@ -28,16 +28,15 @@ const WValue = union(enum) { |
| 28 | 28 | local: u32, |
| 29 | 29 | /// Holds a memoized typed value |
| 30 | 30 | constant: TypedValue, |
| 31 | | /// Offset position in the list of MIR instructions |
| 32 | | mir_offset: usize, |
| 33 | | /// Used for variables that create multiple locals on the stack when allocated |
| 34 | | /// such as structs and optionals. |
| 35 | | multi_value: struct { |
| 36 | | /// The index of the first local variable |
| 37 | | index: u32, |
| 38 | | /// The count of local variables this `WValue` consists of. |
| 39 | | /// i.e. an ErrorUnion has a 'count' of 2. |
| 40 | | count: u32, |
| 31 | /// Used for types that contains of multiple areas within |
| 32 | /// a memory region in the stack. |
| 33 | /// The local represents the position in the stack, |
| 34 | /// whereas the offset represents the offset from that position. |
| 35 | local_with_offset: struct { |
| 36 | /// Index of the local variable |
| 37 | local: u32, |
| 38 | /// The offset from the local's stack position |
| 39 | offset: u32, |
| 41 | 40 | }, |
| 42 | 41 | }; |
| 43 | 42 | |
| ... | ... | @@ -187,7 +186,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 187 | 186 | }, |
| 188 | 187 | 32 => switch (args.valtype1.?) { |
| 189 | 188 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, |
| 190 | | .i32, .f32, .f64 => unreachable, |
| 189 | .i32 => return .i32_load, |
| 190 | .f32, .f64 => unreachable, |
| 191 | 191 | }, |
| 192 | 192 | else => unreachable, |
| 193 | 193 | } else switch (args.valtype1.?) { |
| ... | ... | @@ -668,9 +668,10 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 668 | 668 | .Bool, |
| 669 | 669 | .Pointer, |
| 670 | 670 | .ErrorSet, |
| 671 | .Struct, |
| 672 | .ErrorUnion, |
| 671 | 673 | => wasm.Valtype.i32, |
| 672 | | .Struct, .ErrorUnion, .Optional => unreachable, // Multi typed, must be handled individually. |
| 673 | | else => |tag| self.fail("TODO - Wasm valtype for type '{s}'", .{tag}), |
| 674 | else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}), |
| 674 | 675 | }; |
| 675 | 676 | } |
| 676 | 677 | |
| ... | ... | @@ -692,76 +693,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 { |
| 692 | 693 | /// Writes the bytecode depending on the given `WValue` in `val` |
| 693 | 694 | fn emitWValue(self: *Self, val: WValue) InnerError!void { |
| 694 | 695 | switch (val) { |
| 695 | | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually |
| 696 | | .none, .mir_offset => {}, // no-op |
| 696 | .none => {}, // no-op |
| 697 | .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local), |
| 697 | 698 | .local => |idx| try self.addLabel(.local_get, idx), |
| 698 | 699 | .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack |
| 699 | 700 | } |
| 700 | 701 | } |
| 701 | 702 | |
| 702 | | /// Creates one or multiple locals for a given `Type`. |
| 703 | | /// Returns a corresponding `Wvalue` that can either be of tag |
| 704 | | /// local or multi_value |
| 703 | /// Creates one locals for a given `Type`. |
| 704 | /// Returns a corresponding `Wvalue` with `local` as active tag |
| 705 | 705 | fn allocLocal(self: *Self, ty: Type) InnerError!WValue { |
| 706 | 706 | const initial_index = self.local_index; |
| 707 | | switch (ty.zigTypeTag()) { |
| 708 | | .Struct => { |
| 709 | | // for each struct field, generate a local |
| 710 | | const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; |
| 711 | | const fields_len = @intCast(u32, struct_data.fields.count()); |
| 712 | | try self.locals.ensureUnusedCapacity(self.gpa, fields_len); |
| 713 | | for (struct_data.fields.values()) |*value| { |
| 714 | | const val_type = try self.genValtype(value.ty); |
| 715 | | self.locals.appendAssumeCapacity(val_type); |
| 716 | | self.local_index += 1; |
| 717 | | } |
| 718 | | return WValue{ .multi_value = .{ |
| 719 | | .index = initial_index, |
| 720 | | .count = fields_len, |
| 721 | | } }; |
| 722 | | }, |
| 723 | | .ErrorUnion => { |
| 724 | | const payload_type = ty.errorUnionPayload(); |
| 725 | | const val_type = try self.genValtype(payload_type); |
| 726 | | |
| 727 | | // we emit the error value as the first local, and the payload as the following. |
| 728 | | // The first local is also used to find the index of the error and payload. |
| 729 | | // |
| 730 | | // TODO: Add support where the payload is a type that contains multiple locals such as a struct. |
| 731 | | try self.locals.ensureUnusedCapacity(self.gpa, 2); |
| 732 | | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 |
| 733 | | self.locals.appendAssumeCapacity(val_type); |
| 734 | | self.local_index += 2; |
| 735 | | |
| 736 | | return WValue{ .multi_value = .{ |
| 737 | | .index = initial_index, |
| 738 | | .count = 2, |
| 739 | | } }; |
| 740 | | }, |
| 741 | | .Optional => { |
| 742 | | var opt_buf: Type.Payload.ElemType = undefined; |
| 743 | | const child_type = ty.optionalChild(&opt_buf); |
| 744 | | if (ty.isPtrLikeOptional()) { |
| 745 | | return self.fail("TODO: wasm optional pointer", .{}); |
| 746 | | } |
| 747 | | |
| 748 | | try self.locals.ensureUnusedCapacity(self.gpa, 2); |
| 749 | | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32 |
| 750 | | self.locals.appendAssumeCapacity(try self.genValtype(child_type)); |
| 751 | | self.local_index += 2; |
| 752 | | |
| 753 | | return WValue{ .multi_value = .{ |
| 754 | | .index = initial_index, |
| 755 | | .count = 2, |
| 756 | | } }; |
| 757 | | }, |
| 758 | | else => { |
| 759 | | const valtype = try self.genValtype(ty); |
| 760 | | try self.locals.append(self.gpa, valtype); |
| 761 | | self.local_index += 1; |
| 762 | | return WValue{ .local = initial_index }; |
| 763 | | }, |
| 764 | | } |
| 707 | const valtype = try self.genValtype(ty); |
| 708 | try self.locals.append(self.gpa, valtype); |
| 709 | self.local_index += 1; |
| 710 | return WValue{ .local = initial_index }; |
| 765 | 711 | } |
| 766 | 712 | |
| 767 | 713 | fn genFunctype(self: *Self) InnerError!void { |
| ... | ... | @@ -857,7 +803,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 857 | 803 | if (ty.sentinel()) |sentinel| { |
| 858 | 804 | try self.code.appendSlice(payload.data); |
| 859 | 805 | |
| 860 | | switch (try self.gen(ty.elemType(), sentinel)) { |
| 806 | switch (try self.gen(ty.childType(), sentinel)) { |
| 861 | 807 | .appended => return Result.appended, |
| 862 | 808 | .externally_managed => |data| { |
| 863 | 809 | try self.code.appendSlice(data); |
| ... | ... | @@ -927,15 +873,8 @@ fn restoreStackPointer(self: *Self) !void { |
| 927 | 873 | /// the result back into the stack pointer. |
| 928 | 874 | fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| 929 | 875 | if (offset == 0) return; |
| 930 | | // Generates the following code: |
| 931 | | // |
| 932 | | // global.get 0 |
| 933 | | // i32.const [offset] |
| 934 | | // i32.sub |
| 935 | | // global.set 0 |
| 936 | | |
| 937 | 876 | // TODO: Rather than hardcode the stack pointer to position 0, |
| 938 | | // have the linker resolve it. |
| 877 | // have the linker resolve its relocation |
| 939 | 878 | try self.addLabel(.global_get, 0); |
| 940 | 879 | try self.addImm32(@bitCast(i32, offset)); |
| 941 | 880 | try self.addTag(.i32_sub); |
| ... | ... | @@ -1053,17 +992,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1053 | 992 | } |
| 1054 | 993 | |
| 1055 | 994 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1056 | | const elem_type = self.air.typeOfIndex(inst).elemType(); |
| 995 | const child_type = self.air.typeOfIndex(inst).childType(); |
| 1057 | 996 | |
| 1058 | 997 | // Initialize the stack |
| 1059 | 998 | if (self.initial_stack_value == .none) { |
| 1060 | 999 | try self.initializeStack(); |
| 1061 | 1000 | } |
| 1062 | 1001 | |
| 1063 | | const abi_size = elem_type.abiSize(self.target); |
| 1002 | const abi_size = child_type.abiSize(self.target); |
| 1064 | 1003 | if (abi_size == 0) return WValue{ .none = {} }; |
| 1065 | 1004 | |
| 1066 | | const local = try self.allocLocal(elem_type); |
| 1005 | // local, containing the offset to the stack position |
| 1006 | const local = try self.allocLocal(child_type); |
| 1067 | 1007 | try self.moveStack(@intCast(u32, abi_size), local.local); |
| 1068 | 1008 | |
| 1069 | 1009 | return local; |
| ... | ... | @@ -1074,43 +1014,100 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1074 | 1014 | |
| 1075 | 1015 | const lhs = self.resolveInst(bin_op.lhs); |
| 1076 | 1016 | const rhs = self.resolveInst(bin_op.rhs); |
| 1017 | const ty = self.air.typeOf(bin_op.lhs).childType(); |
| 1077 | 1018 | |
| 1078 | | // get lhs stack position |
| 1019 | const offset: u32 = switch (lhs) { |
| 1020 | .local_with_offset => |with_off| with_off.offset, |
| 1021 | else => 0, |
| 1022 | }; |
| 1023 | |
| 1024 | switch (ty.zigTypeTag()) { |
| 1025 | .ErrorUnion, .Optional => { |
| 1026 | var buf: Type.Payload.ElemType = undefined; |
| 1027 | const payload_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionPayload() else ty.optionalChild(&buf); |
| 1028 | const tag_ty = if (ty.zigTypeTag() == .ErrorUnion) ty.errorUnionSet() else Type.initTag(.i8); |
| 1029 | const payload_offset = @intCast(u32, tag_ty.abiSize(self.target) / 8); |
| 1030 | if (rhs == .constant) { |
| 1031 | // constant will contain both tag and payload, |
| 1032 | // so save those in 2 temporary locals before storing them |
| 1033 | // in memory |
| 1034 | try self.emitWValue(rhs); |
| 1035 | const tag_local = try self.allocLocal(Type.initTag(.i32)); |
| 1036 | const payload_local = try self.allocLocal(payload_ty); |
| 1037 | |
| 1038 | try self.addLabel(.local_set, payload_local.local); |
| 1039 | try self.addLabel(.local_set, tag_local.local); |
| 1040 | |
| 1041 | try self.store(lhs, tag_local, tag_ty, 0); |
| 1042 | try self.store(lhs, payload_local, payload_ty, payload_offset); |
| 1043 | } else if (offset == 0) { |
| 1044 | // tag is being set |
| 1045 | try self.store(lhs, rhs, tag_ty, 0); |
| 1046 | } else { |
| 1047 | // payload is being set |
| 1048 | try self.store(lhs, rhs, payload_ty, payload_offset); |
| 1049 | } |
| 1050 | }, |
| 1051 | else => try self.store(lhs, rhs, ty, offset), |
| 1052 | } |
| 1053 | |
| 1054 | return .none; |
| 1055 | } |
| 1056 | |
| 1057 | fn store(self: *Self, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { |
| 1079 | 1058 | try self.emitWValue(lhs); |
| 1080 | | // get rhs value |
| 1081 | 1059 | try self.emitWValue(rhs); |
| 1082 | | |
| 1083 | | const ty = self.air.typeOf(bin_op.lhs); |
| 1084 | 1060 | const valtype = try self.typeToValtype(ty); |
| 1085 | | |
| 1086 | 1061 | const opcode = buildOpcode(.{ |
| 1087 | 1062 | .valtype1 = valtype, |
| 1088 | 1063 | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| 1089 | 1064 | .op = .store, |
| 1090 | 1065 | }); |
| 1066 | |
| 1091 | 1067 | // store rhs value at stack pointer's location in memory |
| 1092 | | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); |
| 1068 | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = offset, .alignment = 0 }); |
| 1093 | 1069 | try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } }); |
| 1094 | | |
| 1095 | | return .none; |
| 1096 | 1070 | } |
| 1097 | 1071 | |
| 1098 | 1072 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1099 | 1073 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1100 | | const lhs = self.resolveInst(ty_op.operand); |
| 1074 | const operand = self.resolveInst(ty_op.operand); |
| 1075 | const ty = self.air.getRefType(ty_op.ty); |
| 1076 | |
| 1077 | return switch (ty.zigTypeTag()) { |
| 1078 | .Struct, .ErrorUnion => operand, |
| 1079 | else => self.load(operand, ty, 0), |
| 1080 | }; |
| 1081 | } |
| 1101 | 1082 | |
| 1083 | fn load(self: *Self, operand: WValue, ty: Type, offset: u32) InnerError!WValue { |
| 1102 | 1084 | // load local's value from memory by its stack position |
| 1103 | | try self.emitWValue(lhs); |
| 1104 | | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); |
| 1105 | | try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = mem_arg_index } }); |
| 1106 | | return .none; |
| 1085 | try self.emitWValue(operand); |
| 1086 | // Build the opcode with the right bitsize |
| 1087 | const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed; |
| 1088 | const opcode = buildOpcode(.{ |
| 1089 | .valtype1 = try self.typeToValtype(ty), |
| 1090 | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| 1091 | .op = .load, |
| 1092 | .signedness = signedness, |
| 1093 | }); |
| 1094 | |
| 1095 | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = offset, .alignment = 0 }); |
| 1096 | try self.addInst(.{ |
| 1097 | .tag = Mir.Inst.Tag.fromOpcode(opcode), |
| 1098 | .data = .{ .payload = mem_arg_index }, |
| 1099 | }); |
| 1100 | |
| 1101 | // store the result in a local |
| 1102 | const result = try self.allocLocal(ty); |
| 1103 | try self.addLabel(.local_set, result.local); |
| 1104 | return result; |
| 1107 | 1105 | } |
| 1108 | 1106 | |
| 1109 | 1107 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1110 | 1108 | _ = inst; |
| 1111 | | // arguments share the index with locals |
| 1112 | | defer self.local_index += 1; |
| 1113 | | return WValue{ .local = self.local_index }; |
| 1109 | defer self.arg_index += 1; |
| 1110 | return self.args[self.arg_index]; |
| 1114 | 1111 | } |
| 1115 | 1112 | |
| 1116 | 1113 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| ... | ... | @@ -1388,14 +1385,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1388 | 1385 | |
| 1389 | 1386 | // insert blocks at the position of `offset` so |
| 1390 | 1387 | // the condition can jump to it |
| 1391 | | const offset = switch (condition) { |
| 1392 | | .mir_offset => |offset| offset, |
| 1393 | | else => blk: { |
| 1394 | | const offset = self.mir_instructions.len; |
| 1395 | | try self.emitWValue(condition); |
| 1396 | | break :blk offset; |
| 1397 | | }, |
| 1398 | | }; |
| 1388 | const offset = self.mir_instructions.len; |
| 1389 | try self.emitWValue(condition); |
| 1399 | 1390 | |
| 1400 | 1391 | // result type is always noreturn, so use `block_empty` as type. |
| 1401 | 1392 | try self.startBlock(.block, wasm.block_empty, offset); |
| ... | ... | @@ -1415,10 +1406,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1415 | 1406 | } |
| 1416 | 1407 | |
| 1417 | 1408 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { |
| 1418 | | // save offset, so potential conditions can insert blocks in front of |
| 1419 | | // the comparison that we can later jump back to |
| 1420 | | const offset = self.mir_instructions.len; |
| 1421 | | |
| 1422 | 1409 | const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; |
| 1423 | 1410 | const lhs = self.resolveInst(data.bin_op.lhs); |
| 1424 | 1411 | const rhs = self.resolveInst(data.bin_op.rhs); |
| ... | ... | @@ -1447,7 +1434,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 1447 | 1434 | .signedness = signedness, |
| 1448 | 1435 | }); |
| 1449 | 1436 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1450 | | return WValue{ .mir_offset = offset }; |
| 1437 | |
| 1438 | const cmp_tmp = try self.allocLocal(lhs_ty); |
| 1439 | try self.addLabel(.local_set, cmp_tmp.local); |
| 1440 | return cmp_tmp; |
| 1451 | 1441 | } |
| 1452 | 1442 | |
| 1453 | 1443 | fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1468,7 +1458,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1468 | 1458 | |
| 1469 | 1459 | fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1470 | 1460 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1471 | | const offset = self.mir_instructions.len; |
| 1472 | 1461 | |
| 1473 | 1462 | const operand = self.resolveInst(ty_op.operand); |
| 1474 | 1463 | try self.emitWValue(operand); |
| ... | ... | @@ -1478,7 +1467,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1478 | 1467 | try self.addImm32(0); |
| 1479 | 1468 | try self.addTag(.i32_eq); |
| 1480 | 1469 | |
| 1481 | | return WValue{ .mir_offset = offset }; |
| 1470 | // save the result in the local |
| 1471 | const not_tmp = try self.allocLocal(self.air.getRefType(ty_op.ty)); |
| 1472 | try self.addLabel(.local_set, not_tmp.local); |
| 1473 | return not_tmp; |
| 1482 | 1474 | } |
| 1483 | 1475 | |
| 1484 | 1476 | fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1504,24 +1496,45 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1504 | 1496 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1505 | 1497 | const extra = self.air.extraData(Air.StructField, ty_pl.payload); |
| 1506 | 1498 | const struct_ptr = self.resolveInst(extra.data.struct_operand); |
| 1507 | | return structFieldPtr(struct_ptr, extra.data.field_index); |
| 1499 | const struct_ty = self.air.typeOf(extra.data.struct_operand).childType(); |
| 1500 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(extra.data.field_index, self.target)) catch { |
| 1501 | return self.fail("Field type '{}' too big to fit into stack frame", .{ |
| 1502 | struct_ty.structFieldType(extra.data.field_index), |
| 1503 | }); |
| 1504 | }; |
| 1505 | return structFieldPtr(struct_ptr, offset); |
| 1508 | 1506 | } |
| 1507 | |
| 1509 | 1508 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { |
| 1510 | 1509 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1511 | 1510 | const struct_ptr = self.resolveInst(ty_op.operand); |
| 1512 | | return structFieldPtr(struct_ptr, index); |
| 1511 | const struct_ty = self.air.typeOf(ty_op.operand).childType(); |
| 1512 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(index, self.target)) catch { |
| 1513 | return self.fail("Field type '{}' too big to fit into stack frame", .{ |
| 1514 | struct_ty.structFieldType(index), |
| 1515 | }); |
| 1516 | }; |
| 1517 | return structFieldPtr(struct_ptr, offset); |
| 1513 | 1518 | } |
| 1514 | | fn structFieldPtr(struct_ptr: WValue, index: u32) InnerError!WValue { |
| 1515 | | return WValue{ .local = struct_ptr.multi_value.index + index }; |
| 1519 | |
| 1520 | fn structFieldPtr(struct_ptr: WValue, offset: u32) InnerError!WValue { |
| 1521 | return WValue{ .local_with_offset = .{ .local = struct_ptr.local, .offset = offset } }; |
| 1516 | 1522 | } |
| 1517 | 1523 | |
| 1518 | 1524 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1519 | 1525 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 1520 | 1526 | |
| 1521 | 1527 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1522 | | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1523 | | const struct_multivalue = self.resolveInst(extra.struct_operand).multi_value; |
| 1524 | | return WValue{ .local = struct_multivalue.index + extra.field_index }; |
| 1528 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1529 | const struct_ty = self.air.typeOf(struct_field.struct_operand); |
| 1530 | const operand = self.resolveInst(struct_field.struct_operand); |
| 1531 | const field_index = struct_field.field_index; |
| 1532 | const field_ty = struct_ty.structFieldType(field_index); |
| 1533 | if (!field_ty.hasCodeGenBits()) return WValue.none; |
| 1534 | const offset = std.math.cast(u32, struct_ty.structFieldOffset(field_index, self.target)) catch { |
| 1535 | return self.fail("Field type '{}' too big to fit into stack frame", .{field_ty}); |
| 1536 | }; |
| 1537 | return try self.load(operand, field_ty, offset); |
| 1525 | 1538 | } |
| 1526 | 1539 | |
| 1527 | 1540 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1675,25 +1688,32 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1675 | 1688 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |
| 1676 | 1689 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1677 | 1690 | const operand = self.resolveInst(un_op); |
| 1678 | | const offset = self.mir_instructions.len; |
| 1691 | const err_ty = self.air.typeOf(un_op).errorUnionSet(); |
| 1692 | |
| 1693 | // load the error tag value |
| 1694 | try self.emitWValue(operand); |
| 1695 | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); |
| 1696 | try self.addInst(.{ |
| 1697 | .tag = .i32_load, |
| 1698 | .data = .{ .payload = mem_arg_index }, |
| 1699 | }); |
| 1679 | 1700 | |
| 1680 | | // load the error value which is positioned at multi_value's index |
| 1681 | | try self.emitWValue(.{ .local = operand.multi_value.index }); |
| 1682 | 1701 | // Compare the error value with '0' |
| 1683 | 1702 | try self.addImm32(0); |
| 1684 | 1703 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1685 | 1704 | |
| 1686 | | return WValue{ .mir_offset = offset }; |
| 1705 | const is_err_tmp = try self.allocLocal(err_ty); |
| 1706 | try self.addLabel(.local_set, is_err_tmp.local); |
| 1707 | return is_err_tmp; |
| 1687 | 1708 | } |
| 1688 | 1709 | |
| 1689 | 1710 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1690 | 1711 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1691 | 1712 | const operand = self.resolveInst(ty_op.operand); |
| 1692 | | // The index of multi_value contains the error code. To get the initial index of the payload we get |
| 1693 | | // the following index. Next, convert it to a `WValue.local` |
| 1694 | | // |
| 1695 | | // TODO: Check if payload is a type that requires a multi_value as well and emit that instead. i.e. a struct. |
| 1696 | | return WValue{ .local = operand.multi_value.index + 1 }; |
| 1713 | const err_ty = self.air.typeOf(ty_op.operand); |
| 1714 | const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target) / 8); |
| 1715 | |
| 1716 | return self.load(operand, self.air.getRefType(ty_op.ty), offset); |
| 1697 | 1717 | } |
| 1698 | 1718 | |
| 1699 | 1719 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| ... | ... | @@ -1728,8 +1748,8 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! |
| 1728 | 1748 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1729 | 1749 | const operand = self.resolveInst(un_op); |
| 1730 | 1750 | |
| 1731 | | // load the null value which is positioned at multi_value's index |
| 1732 | | try self.emitWValue(.{ .local = operand.multi_value.index }); |
| 1751 | // load the null value which is positioned at local_with_offset's index |
| 1752 | try self.emitWValue(.{ .local = operand.local_with_offset.local }); |
| 1733 | 1753 | try self.addImm32(0); |
| 1734 | 1754 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); |
| 1735 | 1755 | |
| ... | ... | @@ -1743,7 +1763,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! |
| 1743 | 1763 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1744 | 1764 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1745 | 1765 | const operand = self.resolveInst(ty_op.operand); |
| 1746 | | return WValue{ .local = operand.multi_value.index + 1 }; |
| 1766 | return WValue{ .local = operand.local_with_offset.local + 1 }; |
| 1747 | 1767 | } |
| 1748 | 1768 | |
| 1749 | 1769 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |