| ... | ... | @@ -30,7 +30,13 @@ const WValue = union(enum) { |
| 30 | 30 | code_offset: usize, |
| 31 | 31 | /// Used for variables that create multiple locals on the stack when allocated |
| 32 | 32 | /// such as structs and optionals. |
| 33 | | multi_value: u32, |
| 33 | multi_value: struct { |
| 34 | /// The index of the first local variable |
| 35 | index: u32, |
| 36 | /// The count of local variables this `WValue` consists of. |
| 37 | /// i.e. an ErrorUnion has a 'count' of 2. |
| 38 | count: u32, |
| 39 | }, |
| 34 | 40 | }; |
| 35 | 41 | |
| 36 | 42 | /// Wasm ops, but without input/output/signedness information |
| ... | ... | @@ -510,6 +516,10 @@ pub const Context = struct { |
| 510 | 516 | locals: std.ArrayListUnmanaged(u8), |
| 511 | 517 | /// The Target we're emitting (used to call intInfo) |
| 512 | 518 | target: std.Target, |
| 519 | /// Table with the global error set. Consists of every error found in |
| 520 | /// the compiled code. Each error name maps to a `Module.ErrorInt` which is emitted |
| 521 | /// during codegen to determine the error value. |
| 522 | global_error_set: std.StringHashMapUnmanaged(Module.ErrorInt), |
| 513 | 523 | |
| 514 | 524 | const InnerError = error{ |
| 515 | 525 | OutOfMemory, |
| ... | ... | @@ -559,7 +569,6 @@ pub const Context = struct { |
| 559 | 569 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; |
| 560 | 570 | return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits}); |
| 561 | 571 | }, |
| 562 | | .Bool, .Pointer, .Struct => wasm.Valtype.i32, |
| 563 | 572 | .Enum => switch (ty.tag()) { |
| 564 | 573 | .enum_simple => wasm.Valtype.i32, |
| 565 | 574 | else => self.typeToValtype( |
| ... | ... | @@ -567,6 +576,11 @@ pub const Context = struct { |
| 567 | 576 | ty.cast(Type.Payload.EnumFull).?.data.tag_ty, |
| 568 | 577 | ), |
| 569 | 578 | }, |
| 579 | .Bool, |
| 580 | .Pointer, |
| 581 | .ErrorSet, |
| 582 | => wasm.Valtype.i32, |
| 583 | .Struct, .ErrorUnion => unreachable, // Multi typed, must be handled individually. |
| 570 | 584 | else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}), |
| 571 | 585 | }; |
| 572 | 586 | } |
| ... | ... | @@ -600,6 +614,57 @@ pub const Context = struct { |
| 600 | 614 | } |
| 601 | 615 | } |
| 602 | 616 | |
| 617 | /// Creates one or multiple locals for a given `Type`. |
| 618 | /// Returns a corresponding `Wvalue` that can either be of tag |
| 619 | /// local or multi_value |
| 620 | fn allocLocal(self: *Context, ty: Type) InnerError!WValue { |
| 621 | const initial_index = self.local_index; |
| 622 | switch (ty.zigTypeTag()) { |
| 623 | .Struct => { |
| 624 | // for each struct field, generate a local |
| 625 | const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; |
| 626 | const fields_len = @intCast(u32, struct_data.fields.count()); |
| 627 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len); |
| 628 | for (struct_data.fields.items()) |entry| { |
| 629 | const val_type = try self.genValtype( |
| 630 | .{ .node_offset = struct_data.node_offset }, |
| 631 | entry.value.ty, |
| 632 | ); |
| 633 | self.locals.appendAssumeCapacity(val_type); |
| 634 | self.local_index += 1; |
| 635 | } |
| 636 | return WValue{ .multi_value = .{ |
| 637 | .index = initial_index, |
| 638 | .count = fields_len, |
| 639 | } }; |
| 640 | }, |
| 641 | .ErrorUnion => { |
| 642 | const payload_type = ty.errorUnionChild(); |
| 643 | const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type); |
| 644 | |
| 645 | // we emit the error value as the first local, and the payload as the following. |
| 646 | // The first local is also used to find the index of the error and payload. |
| 647 | // |
| 648 | // TODO: Add support where the payload is a type that contains multiple locals such as a struct. |
| 649 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2); |
| 650 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 |
| 651 | self.locals.appendAssumeCapacity(val_type); |
| 652 | self.local_index += 2; |
| 653 | |
| 654 | return WValue{ .multi_value = .{ |
| 655 | .index = initial_index, |
| 656 | .count = 2, |
| 657 | } }; |
| 658 | }, |
| 659 | else => { |
| 660 | const valtype = try self.genValtype(.{ .node_offset = 0 }, ty); |
| 661 | try self.locals.append(self.gpa, valtype); |
| 662 | self.local_index += 1; |
| 663 | return WValue{ .local = initial_index }; |
| 664 | }, |
| 665 | } |
| 666 | } |
| 667 | |
| 603 | 668 | fn genFunctype(self: *Context) InnerError!void { |
| 604 | 669 | assert(self.decl.has_tv); |
| 605 | 670 | const ty = self.decl.ty; |
| ... | ... | @@ -622,8 +687,21 @@ pub const Context = struct { |
| 622 | 687 | |
| 623 | 688 | // return type |
| 624 | 689 | const return_type = ty.fnReturnType(); |
| 625 | | switch (return_type.tag()) { |
| 626 | | .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)), |
| 690 | switch (return_type.zigTypeTag()) { |
| 691 | .Void, .NoReturn => try leb.writeULEB128(writer, @as(u32, 0)), |
| 692 | .Struct => return self.fail(.{ .node_offset = 0 }, "TODO: Implement struct as return type for wasm", .{}), |
| 693 | .Optional => return self.fail(.{ .node_offset = 0 }, "TODO: Implement optionals as return type for wasm", .{}), |
| 694 | .ErrorUnion => { |
| 695 | const val_type = try self.genValtype( |
| 696 | .{ .node_offset = 0 }, |
| 697 | return_type.errorUnionChild(), |
| 698 | ); |
| 699 | |
| 700 | // write down the amount of return values |
| 701 | try leb.writeULEB128(writer, @as(u32, 2)); |
| 702 | try writer.writeByte(wasm.valtype(.i32)); // error code is always an i32 integer. |
| 703 | try writer.writeByte(val_type); |
| 704 | }, |
| 627 | 705 | else => |ret_type| { |
| 628 | 706 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 629 | 707 | // Can we maybe get the source index of the return type? |
| ... | ... | @@ -736,6 +814,7 @@ pub const Context = struct { |
| 736 | 814 | .constant => unreachable, |
| 737 | 815 | .dbg_stmt => WValue.none, |
| 738 | 816 | .div => self.genBinOp(inst.castTag(.div).?, .div), |
| 817 | .is_err => self.genIsErr(inst.castTag(.is_err).?), |
| 739 | 818 | .load => self.genLoad(inst.castTag(.load).?), |
| 740 | 819 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 741 | 820 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| ... | ... | @@ -747,6 +826,8 @@ pub const Context = struct { |
| 747 | 826 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), |
| 748 | 827 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), |
| 749 | 828 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| 829 | .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?), |
| 830 | .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?), |
| 750 | 831 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), |
| 751 | 832 | else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 752 | 833 | }; |
| ... | ... | @@ -771,7 +852,7 @@ pub const Context = struct { |
| 771 | 852 | const func_inst = inst.func.castTag(.constant).?; |
| 772 | 853 | const func_val = inst.func.value().?; |
| 773 | 854 | |
| 774 | | const target = blk: { |
| 855 | const target: *Decl = blk: { |
| 775 | 856 | if (func_val.castTag(.function)) |func| { |
| 776 | 857 | break :blk func.data.owner_decl; |
| 777 | 858 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { |
| ... | ... | @@ -799,30 +880,7 @@ pub const Context = struct { |
| 799 | 880 | |
| 800 | 881 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { |
| 801 | 882 | const elem_type = inst.base.ty.elemType(); |
| 802 | | const initial_index = self.local_index; |
| 803 | | |
| 804 | | switch (elem_type.zigTypeTag()) { |
| 805 | | .Struct => { |
| 806 | | // for each struct field, generate a local |
| 807 | | const struct_data: *Module.Struct = elem_type.castTag(.@"struct").?.data; |
| 808 | | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + struct_data.fields.count()); |
| 809 | | for (struct_data.fields.items()) |entry| { |
| 810 | | const val_type = try self.genValtype( |
| 811 | | .{ .node_offset = struct_data.node_offset }, |
| 812 | | entry.value.ty, |
| 813 | | ); |
| 814 | | self.locals.appendAssumeCapacity(val_type); |
| 815 | | self.local_index += 1; |
| 816 | | } |
| 817 | | return WValue{ .multi_value = initial_index }; |
| 818 | | }, |
| 819 | | else => { |
| 820 | | const valtype = try self.genValtype(inst.base.src, elem_type); |
| 821 | | try self.locals.append(self.gpa, valtype); |
| 822 | | self.local_index += 1; |
| 823 | | return WValue{ .local = initial_index }; |
| 824 | | }, |
| 825 | | } |
| 883 | return self.allocLocal(elem_type); |
| 826 | 884 | } |
| 827 | 885 | |
| 828 | 886 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { |
| ... | ... | @@ -832,11 +890,27 @@ pub const Context = struct { |
| 832 | 890 | const rhs = self.resolveInst(inst.rhs); |
| 833 | 891 | |
| 834 | 892 | switch (lhs) { |
| 835 | | // When assigning a value to a multi_value such as a struct, |
| 836 | | // we simply assign the local_index to the rhs one. |
| 837 | | // This allows us to update struct fields without having to individually |
| 838 | | // set each local as each field's index will be calculated off the struct's base index |
| 839 | | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 893 | .multi_value => |multi_value| switch (rhs) { |
| 894 | // When assigning a value to a multi_value such as a struct, |
| 895 | // we simply assign the local_index to the rhs one. |
| 896 | // This allows us to update struct fields without having to individually |
| 897 | // set each local as each field's index will be calculated off the struct's base index |
| 898 | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 899 | .constant, .none => { |
| 900 | // emit all values onto the stack if constant |
| 901 | try self.emitWValue(rhs); |
| 902 | |
| 903 | // for each local, pop the stack value into the local |
| 904 | // As the last element is on top of the stack, we must populate the locals |
| 905 | // in reverse. |
| 906 | var i: u32 = multi_value.count; |
| 907 | while (i > 0) : (i -= 1) { |
| 908 | try writer.writeByte(wasm.opcode(.local_set)); |
| 909 | try leb.writeULEB128(writer, multi_value.index + i - 1); |
| 910 | } |
| 911 | }, |
| 912 | else => unreachable, |
| 913 | }, |
| 840 | 914 | .local => |local| { |
| 841 | 915 | try self.emitWValue(rhs); |
| 842 | 916 | try writer.writeByte(wasm.opcode(.local_set)); |
| ... | ... | @@ -959,6 +1033,34 @@ pub const Context = struct { |
| 959 | 1033 | try self.emitConstant(src, value, int_tag_ty); |
| 960 | 1034 | } |
| 961 | 1035 | }, |
| 1036 | .ErrorSet => { |
| 1037 | const error_index = self.global_error_set.get(value.getError().?).?; |
| 1038 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 1039 | try leb.writeULEB128(writer, error_index); |
| 1040 | }, |
| 1041 | .ErrorUnion => { |
| 1042 | const data = value.castTag(.error_union).?.data; |
| 1043 | const error_type = ty.errorUnionSet(); |
| 1044 | const payload_type = ty.errorUnionChild(); |
| 1045 | if (value.getError()) |_| { |
| 1046 | // write the error value |
| 1047 | try self.emitConstant(src, data, error_type); |
| 1048 | |
| 1049 | // no payload, so write a '0' const |
| 1050 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 1051 | .op = .@"const", |
| 1052 | .valtype1 = try self.typeToValtype(src, payload_type), |
| 1053 | }); |
| 1054 | try writer.writeByte(wasm.opcode(opcode)); |
| 1055 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1056 | } else { |
| 1057 | // no error, so write a '0' const |
| 1058 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 1059 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1060 | // after the error code, we emit the payload |
| 1061 | try self.emitConstant(src, data, payload_type); |
| 1062 | } |
| 1063 | }, |
| 962 | 1064 | else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), |
| 963 | 1065 | } |
| 964 | 1066 | } |
| ... | ... | @@ -1131,7 +1233,7 @@ pub const Context = struct { |
| 1131 | 1233 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { |
| 1132 | 1234 | const struct_ptr = self.resolveInst(inst.struct_ptr); |
| 1133 | 1235 | |
| 1134 | | return WValue{ .local = struct_ptr.multi_value + @intCast(u32, inst.field_index) }; |
| 1236 | return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) }; |
| 1135 | 1237 | } |
| 1136 | 1238 | |
| 1137 | 1239 | fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue { |
| ... | ... | @@ -1174,4 +1276,35 @@ pub const Context = struct { |
| 1174 | 1276 | |
| 1175 | 1277 | return .none; |
| 1176 | 1278 | } |
| 1279 | |
| 1280 | fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue { |
| 1281 | const operand = self.resolveInst(inst.operand); |
| 1282 | const offset = self.code.items.len; |
| 1283 | const writer = self.code.writer(); |
| 1284 | |
| 1285 | // load the error value which is positioned at multi_value's index |
| 1286 | try self.emitWValue(.{ .local = operand.multi_value.index }); |
| 1287 | // Compare the error value with '0' |
| 1288 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 1289 | try leb.writeILEB128(writer, @as(i32, 0)); |
| 1290 | |
| 1291 | // we want to break out of the condition if they're *not* equal, |
| 1292 | // because that means there's an error. |
| 1293 | try writer.writeByte(wasm.opcode(.i32_ne)); |
| 1294 | |
| 1295 | return WValue{ .code_offset = offset }; |
| 1296 | } |
| 1297 | |
| 1298 | fn genUnwrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue { |
| 1299 | const operand = self.resolveInst(inst.operand); |
| 1300 | // The index of multi_value contains the error code. To get the initial index of the payload we get |
| 1301 | // the following index. Next, convert it to a `WValue.local` |
| 1302 | // |
| 1303 | // TODO: Check if payload is a type that requires a multi_value as well and emit that instead. i.e. a struct. |
| 1304 | return WValue{ .local = operand.multi_value.index + 1 }; |
| 1305 | } |
| 1306 | |
| 1307 | fn genWrapErrUnionPayload(self: *Context, inst: *Inst.UnOp) InnerError!WValue { |
| 1308 | return self.resolveInst(inst.operand); |
| 1309 | } |
| 1177 | 1310 | }; |