| ... | @@ -28,16 +28,15 @@ const WValue = union(enum) { | ... | @@ -28,16 +28,15 @@ const WValue = union(enum) { |
| 28 | local: u32, | 28 | local: u32, |
| 29 | /// Holds a memoized typed value | 29 | /// Holds a memoized typed value |
| 30 | constant: TypedValue, | 30 | constant: TypedValue, |
| 31 | /// Offset position in the list of MIR instructions | 31 | /// Used for types that contains of multiple areas within |
| 32 | mir_offset: usize, | 32 | /// a memory region in the stack. |
| 33 | /// Used for variables that create multiple locals on the stack when allocated | 33 | /// The local represents the position in the stack, |
| 34 | /// such as structs and optionals. | 34 | /// whereas the offset represents the offset from that position. |
| 35 | multi_value: struct { | 35 | local_with_offset: struct { |
| 36 | /// The index of the first local variable | 36 | /// Index of the local variable |
| 37 | index: u32, | 37 | local: u32, |
| 38 | /// The count of local variables this `WValue` consists of. | 38 | /// The offset from the local's stack position |
| 39 | /// i.e. an ErrorUnion has a 'count' of 2. | 39 | offset: u32, |
| 40 | count: u32, | | |
| 41 | }, | 40 | }, |
| 42 | }; | 41 | }; |
| 43 | | 42 | |
| ... | @@ -187,7 +186,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -187,7 +186,8 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 187 | }, | 186 | }, |
| 188 | 32 => switch (args.valtype1.?) { | 187 | 32 => switch (args.valtype1.?) { |
| 189 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, | 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 | else => unreachable, | 192 | else => unreachable, |
| 193 | } else switch (args.valtype1.?) { | 193 | } else switch (args.valtype1.?) { |
| ... | @@ -668,9 +668,10 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { | ... | @@ -668,9 +668,10 @@ fn typeToValtype(self: *Self, ty: Type) InnerError!wasm.Valtype { |
| 668 | .Bool, | 668 | .Bool, |
| 669 | .Pointer, | 669 | .Pointer, |
| 670 | .ErrorSet, | 670 | .ErrorSet, |
| | 671 | .Struct, |
| | 672 | .ErrorUnion, |
| 671 | => wasm.Valtype.i32, | 673 | => wasm.Valtype.i32, |
| 672 | .Struct, .ErrorUnion, .Optional => unreachable, // Multi typed, must be handled individually. | 674 | else => self.fail("TODO - Wasm valtype for type '{}'", .{ty}), |
| 673 | else => |tag| self.fail("TODO - Wasm valtype for type '{s}'", .{tag}), | | |
| 674 | }; | 675 | }; |
| 675 | } | 676 | } |
| 676 | | 677 | |
| ... | @@ -692,76 +693,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 { | ... | @@ -692,76 +693,21 @@ fn genBlockType(self: *Self, ty: Type) InnerError!u8 { |
| 692 | /// Writes the bytecode depending on the given `WValue` in `val` | 693 | /// Writes the bytecode depending on the given `WValue` in `val` |
| 693 | fn emitWValue(self: *Self, val: WValue) InnerError!void { | 694 | fn emitWValue(self: *Self, val: WValue) InnerError!void { |
| 694 | switch (val) { | 695 | switch (val) { |
| 695 | .multi_value => unreachable, // multi_value can never be written directly, and must be accessed individually | 696 | .none => {}, // no-op |
| 696 | .none, .mir_offset => {}, // no-op | 697 | .local_with_offset => |with_off| try self.addLabel(.local_get, with_off.local), |
| 697 | .local => |idx| try self.addLabel(.local_get, idx), | 698 | .local => |idx| try self.addLabel(.local_get, idx), |
| 698 | .constant => |tv| try self.emitConstant(tv.val, tv.ty), // Creates a new constant on the stack | 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 | /// Creates one locals for a given `Type`. |
| 703 | /// Returns a corresponding `Wvalue` that can either be of tag | 704 | /// Returns a corresponding `Wvalue` with `local` as active tag |
| 704 | /// local or multi_value | | |
| 705 | fn allocLocal(self: *Self, ty: Type) InnerError!WValue { | 705 | fn allocLocal(self: *Self, ty: Type) InnerError!WValue { |
| 706 | const initial_index = self.local_index; | 706 | const initial_index = self.local_index; |
| 707 | switch (ty.zigTypeTag()) { | 707 | const valtype = try self.genValtype(ty); |
| 708 | .Struct => { | 708 | try self.locals.append(self.gpa, valtype); |
| 709 | // for each struct field, generate a local | 709 | self.local_index += 1; |
| 710 | const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; | 710 | return WValue{ .local = initial_index }; |
| 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 | } | | |
| 765 | } | 711 | } |
| 766 | | 712 | |
| 767 | fn genFunctype(self: *Self) InnerError!void { | 713 | fn genFunctype(self: *Self) InnerError!void { |
| ... | @@ -857,7 +803,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { | ... | @@ -857,7 +803,7 @@ pub fn gen(self: *Self, ty: Type, val: Value) InnerError!Result { |
| 857 | if (ty.sentinel()) |sentinel| { | 803 | if (ty.sentinel()) |sentinel| { |
| 858 | try self.code.appendSlice(payload.data); | 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 | .appended => return Result.appended, | 807 | .appended => return Result.appended, |
| 862 | .externally_managed => |data| { | 808 | .externally_managed => |data| { |
| 863 | try self.code.appendSlice(data); | 809 | try self.code.appendSlice(data); |
| ... | @@ -927,15 +873,8 @@ fn restoreStackPointer(self: *Self) !void { | ... | @@ -927,15 +873,8 @@ fn restoreStackPointer(self: *Self) !void { |
| 927 | /// the result back into the stack pointer. | 873 | /// the result back into the stack pointer. |
| 928 | fn moveStack(self: *Self, offset: u32, local: u32) !void { | 874 | fn moveStack(self: *Self, offset: u32, local: u32) !void { |
| 929 | if (offset == 0) return; | 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 | // TODO: Rather than hardcode the stack pointer to position 0, | 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 | try self.addLabel(.global_get, 0); | 878 | try self.addLabel(.global_get, 0); |
| 940 | try self.addImm32(@bitCast(i32, offset)); | 879 | try self.addImm32(@bitCast(i32, offset)); |
| 941 | try self.addTag(.i32_sub); | 880 | try self.addTag(.i32_sub); |
| ... | @@ -1053,17 +992,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1053,17 +992,18 @@ fn airCall(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1053 | } | 992 | } |
| 1054 | | 993 | |
| 1055 | fn airAlloc(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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 | // Initialize the stack | 997 | // Initialize the stack |
| 1059 | if (self.initial_stack_value == .none) { | 998 | if (self.initial_stack_value == .none) { |
| 1060 | try self.initializeStack(); | 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 | if (abi_size == 0) return WValue{ .none = {} }; | 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 | try self.moveStack(@intCast(u32, abi_size), local.local); | 1007 | try self.moveStack(@intCast(u32, abi_size), local.local); |
| 1068 | | 1008 | |
| 1069 | return local; | 1009 | return local; |
| ... | @@ -1074,43 +1014,100 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1074,43 +1014,100 @@ fn airStore(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1074 | | 1014 | |
| 1075 | const lhs = self.resolveInst(bin_op.lhs); | 1015 | const lhs = self.resolveInst(bin_op.lhs); |
| 1076 | const rhs = self.resolveInst(bin_op.rhs); | 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 | try self.emitWValue(lhs); | 1058 | try self.emitWValue(lhs); |
| 1080 | // get rhs value | | |
| 1081 | try self.emitWValue(rhs); | 1059 | try self.emitWValue(rhs); |
| 1082 | | | |
| 1083 | const ty = self.air.typeOf(bin_op.lhs); | | |
| 1084 | const valtype = try self.typeToValtype(ty); | 1060 | const valtype = try self.typeToValtype(ty); |
| 1085 | | | |
| 1086 | const opcode = buildOpcode(.{ | 1061 | const opcode = buildOpcode(.{ |
| 1087 | .valtype1 = valtype, | 1062 | .valtype1 = valtype, |
| 1088 | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size | 1063 | .width = @intCast(u8, Type.abiSize(ty, self.target) * 8), // use bitsize instead of byte size |
| 1089 | .op = .store, | 1064 | .op = .store, |
| 1090 | }); | 1065 | }); |
| | 1066 | |
| 1091 | // store rhs value at stack pointer's location in memory | 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 | try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } }); | 1069 | try self.addInst(.{ .tag = Mir.Inst.Tag.fromOpcode(opcode), .data = .{ .payload = mem_arg_index } }); |
| 1094 | | | |
| 1095 | return .none; | | |
| 1096 | } | 1070 | } |
| 1097 | | 1071 | |
| 1098 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1072 | fn airLoad(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1099 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | // load local's value from memory by its stack position | 1084 | // load local's value from memory by its stack position |
| 1103 | try self.emitWValue(lhs); | 1085 | try self.emitWValue(operand); |
| 1104 | const mem_arg_index = try self.addExtra(Mir.MemArg{ .offset = 0, .alignment = 0 }); | 1086 | // Build the opcode with the right bitsize |
| 1105 | try self.addInst(.{ .tag = .i32_load, .data = .{ .payload = mem_arg_index } }); | 1087 | const signedness: std.builtin.Signedness = if (ty.isUnsignedInt()) .unsigned else .signed; |
| 1106 | return .none; | 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 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1107 | fn airArg(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1110 | _ = inst; | 1108 | _ = inst; |
| 1111 | // arguments share the index with locals | 1109 | defer self.arg_index += 1; |
| 1112 | defer self.local_index += 1; | 1110 | return self.args[self.arg_index]; |
| 1113 | return WValue{ .local = self.local_index }; | | |
| 1114 | } | 1111 | } |
| 1115 | | 1112 | |
| 1116 | fn airBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { | 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,14 +1385,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1388 | | 1385 | |
| 1389 | // insert blocks at the position of `offset` so | 1386 | // insert blocks at the position of `offset` so |
| 1390 | // the condition can jump to it | 1387 | // the condition can jump to it |
| 1391 | const offset = switch (condition) { | 1388 | const offset = self.mir_instructions.len; |
| 1392 | .mir_offset => |offset| offset, | 1389 | try self.emitWValue(condition); |
| 1393 | else => blk: { | | |
| 1394 | const offset = self.mir_instructions.len; | | |
| 1395 | try self.emitWValue(condition); | | |
| 1396 | break :blk offset; | | |
| 1397 | }, | | |
| 1398 | }; | | |
| 1399 | | 1390 | |
| 1400 | // result type is always noreturn, so use `block_empty` as type. | 1391 | // result type is always noreturn, so use `block_empty` as type. |
| 1401 | try self.startBlock(.block, wasm.block_empty, offset); | 1392 | try self.startBlock(.block, wasm.block_empty, offset); |
| ... | @@ -1415,10 +1406,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1415,10 +1406,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1415 | } | 1406 | } |
| 1416 | | 1407 | |
| 1417 | fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) InnerError!WValue { | 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 | const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; | 1409 | const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; |
| 1423 | const lhs = self.resolveInst(data.bin_op.lhs); | 1410 | const lhs = self.resolveInst(data.bin_op.lhs); |
| 1424 | const rhs = self.resolveInst(data.bin_op.rhs); | 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,7 +1434,10 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: std.math.CompareOperator) Inner |
| 1447 | .signedness = signedness, | 1434 | .signedness = signedness, |
| 1448 | }); | 1435 | }); |
| 1449 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 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 | fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,7 +1458,6 @@ fn airBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1468 | | 1458 | |
| 1469 | fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1459 | fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1470 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1460 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1471 | const offset = self.mir_instructions.len; | | |
| 1472 | | 1461 | |
| 1473 | const operand = self.resolveInst(ty_op.operand); | 1462 | const operand = self.resolveInst(ty_op.operand); |
| 1474 | try self.emitWValue(operand); | 1463 | try self.emitWValue(operand); |
| ... | @@ -1478,7 +1467,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | ... | @@ -1478,7 +1467,10 @@ fn airNot(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1478 | try self.addImm32(0); | 1467 | try self.addImm32(0); |
| 1479 | try self.addTag(.i32_eq); | 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 | fn airBreakpoint(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,24 +1496,45 @@ fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1504 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1496 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1505 | const extra = self.air.extraData(Air.StructField, ty_pl.payload); | 1497 | const extra = self.air.extraData(Air.StructField, ty_pl.payload); |
| 1506 | const struct_ptr = self.resolveInst(extra.data.struct_operand); | 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 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { | 1508 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u32) InnerError!WValue { |
| 1510 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1509 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1511 | const struct_ptr = self.resolveInst(ty_op.operand); | 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 { | 1519 | |
| 1515 | return WValue{ .local = struct_ptr.multi_value.index + index }; | 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 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1524 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1519 | if (self.liveness.isUnused(inst)) return WValue.none; | 1525 | if (self.liveness.isUnused(inst)) return WValue.none; |
| 1520 | | 1526 | |
| 1521 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1527 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 1522 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; | 1528 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1523 | const struct_multivalue = self.resolveInst(extra.struct_operand).multi_value; | 1529 | const struct_ty = self.air.typeOf(struct_field.struct_operand); |
| 1524 | return WValue{ .local = struct_multivalue.index + extra.field_index }; | 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 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,25 +1688,32 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1675 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { | 1688 | fn airIsErr(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |
| 1676 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1689 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1677 | const operand = self.resolveInst(un_op); | 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 | // Compare the error value with '0' | 1701 | // Compare the error value with '0' |
| 1683 | try self.addImm32(0); | 1702 | try self.addImm32(0); |
| 1684 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 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 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1710 | fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1690 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1711 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1691 | const operand = self.resolveInst(ty_op.operand); | 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 | 1713 | const err_ty = self.air.typeOf(ty_op.operand); |
| 1693 | // the following index. Next, convert it to a `WValue.local` | 1714 | const offset = @intCast(u32, err_ty.errorUnionSet().abiSize(self.target) / 8); |
| 1694 | // | 1715 | |
| 1695 | // TODO: Check if payload is a type that requires a multi_value as well and emit that instead. i.e. a struct. | 1716 | return self.load(operand, self.air.getRefType(ty_op.ty), offset); |
| 1696 | return WValue{ .local = operand.multi_value.index + 1 }; | | |
| 1697 | } | 1717 | } |
| 1698 | | 1718 | |
| 1699 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 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,8 +1748,8 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! |
| 1728 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1748 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1729 | const operand = self.resolveInst(un_op); | 1749 | const operand = self.resolveInst(un_op); |
| 1730 | | 1750 | |
| 1731 | // load the null value which is positioned at multi_value's index | 1751 | // load the null value which is positioned at local_with_offset's index |
| 1732 | try self.emitWValue(.{ .local = operand.multi_value.index }); | 1752 | try self.emitWValue(.{ .local = operand.local_with_offset.local }); |
| 1733 | try self.addImm32(0); | 1753 | try self.addImm32(0); |
| 1734 | try self.addTag(Mir.Inst.Tag.fromOpcode(opcode)); | 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,7 +1763,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError! |
| 1743 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1763 | fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |
| 1744 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1764 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1745 | const operand = self.resolveInst(ty_op.operand); | 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 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { | 1769 | fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) InnerError!WValue { |