| ... | @@ -30,7 +30,13 @@ const WValue = union(enum) { | ... | @@ -30,7 +30,13 @@ const WValue = union(enum) { |
| 30 | code_offset: usize, | 30 | code_offset: usize, |
| 31 | /// Used for variables that create multiple locals on the stack when allocated | 31 | /// Used for variables that create multiple locals on the stack when allocated |
| 32 | /// such as structs and optionals. | 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 | /// Wasm ops, but without input/output/signedness information | 42 | /// Wasm ops, but without input/output/signedness information |
| ... | @@ -510,6 +516,10 @@ pub const Context = struct { | ... | @@ -510,6 +516,10 @@ pub const Context = struct { |
| 510 | locals: std.ArrayListUnmanaged(u8), | 516 | locals: std.ArrayListUnmanaged(u8), |
| 511 | /// The Target we're emitting (used to call intInfo) | 517 | /// The Target we're emitting (used to call intInfo) |
| 512 | target: std.Target, | 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 | const InnerError = error{ | 524 | const InnerError = error{ |
| 515 | OutOfMemory, | 525 | OutOfMemory, |
| ... | @@ -559,7 +569,6 @@ pub const Context = struct { | ... | @@ -559,7 +569,6 @@ pub const Context = struct { |
| 559 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; | 569 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; |
| 560 | return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits}); | 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 | .Enum => switch (ty.tag()) { | 572 | .Enum => switch (ty.tag()) { |
| 564 | .enum_simple => wasm.Valtype.i32, | 573 | .enum_simple => wasm.Valtype.i32, |
| 565 | else => self.typeToValtype( | 574 | else => self.typeToValtype( |
| ... | @@ -567,6 +576,11 @@ pub const Context = struct { | ... | @@ -567,6 +576,11 @@ pub const Context = struct { |
| 567 | ty.cast(Type.Payload.EnumFull).?.data.tag_ty, | 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 | else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.zigTypeTag()}), | 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,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 | fn genFunctype(self: *Context) InnerError!void { | 668 | fn genFunctype(self: *Context) InnerError!void { |
| 604 | assert(self.decl.has_tv); | 669 | assert(self.decl.has_tv); |
| 605 | const ty = self.decl.ty; | 670 | const ty = self.decl.ty; |
| ... | @@ -622,8 +687,21 @@ pub const Context = struct { | ... | @@ -622,8 +687,21 @@ pub const Context = struct { |
| 622 | | 687 | |
| 623 | // return type | 688 | // return type |
| 624 | const return_type = ty.fnReturnType(); | 689 | const return_type = ty.fnReturnType(); |
| 625 | switch (return_type.tag()) { | 690 | switch (return_type.zigTypeTag()) { |
| 626 | .void, .noreturn => try leb.writeULEB128(writer, @as(u32, 0)), | 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 | else => |ret_type| { | 705 | else => |ret_type| { |
| 628 | try leb.writeULEB128(writer, @as(u32, 1)); | 706 | try leb.writeULEB128(writer, @as(u32, 1)); |
| 629 | // Can we maybe get the source index of the return type? | 707 | // Can we maybe get the source index of the return type? |
| ... | @@ -736,6 +814,7 @@ pub const Context = struct { | ... | @@ -736,6 +814,7 @@ pub const Context = struct { |
| 736 | .constant => unreachable, | 814 | .constant => unreachable, |
| 737 | .dbg_stmt => WValue.none, | 815 | .dbg_stmt => WValue.none, |
| 738 | .div => self.genBinOp(inst.castTag(.div).?, .div), | 816 | .div => self.genBinOp(inst.castTag(.div).?, .div), |
| | 817 | .is_err => self.genIsErr(inst.castTag(.is_err).?), |
| 739 | .load => self.genLoad(inst.castTag(.load).?), | 818 | .load => self.genLoad(inst.castTag(.load).?), |
| 740 | .loop => self.genLoop(inst.castTag(.loop).?), | 819 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 741 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), | 820 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), |
| ... | @@ -747,6 +826,8 @@ pub const Context = struct { | ... | @@ -747,6 +826,8 @@ pub const Context = struct { |
| 747 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), | 826 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), |
| 748 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), | 827 | .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), |
| 749 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), | 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 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), | 831 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), |
| 751 | else => self.fail(.{ .node_offset = 0 }, "TODO: Implement wasm inst: {s}", .{inst.tag}), | 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,7 +852,7 @@ pub const Context = struct { |
| 771 | const func_inst = inst.func.castTag(.constant).?; | 852 | const func_inst = inst.func.castTag(.constant).?; |
| 772 | const func_val = inst.func.value().?; | 853 | const func_val = inst.func.value().?; |
| 773 | | 854 | |
| 774 | const target = blk: { | 855 | const target: *Decl = blk: { |
| 775 | if (func_val.castTag(.function)) |func| { | 856 | if (func_val.castTag(.function)) |func| { |
| 776 | break :blk func.data.owner_decl; | 857 | break :blk func.data.owner_decl; |
| 777 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { | 858 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { |
| ... | @@ -799,30 +880,7 @@ pub const Context = struct { | ... | @@ -799,30 +880,7 @@ pub const Context = struct { |
| 799 | | 880 | |
| 800 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { | 881 | fn genAlloc(self: *Context, inst: *Inst.NoOp) InnerError!WValue { |
| 801 | const elem_type = inst.base.ty.elemType(); | 882 | const elem_type = inst.base.ty.elemType(); |
| 802 | const initial_index = self.local_index; | 883 | return self.allocLocal(elem_type); |
| 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 | } | | |
| 826 | } | 884 | } |
| 827 | | 885 | |
| 828 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { | 886 | fn genStore(self: *Context, inst: *Inst.BinOp) InnerError!WValue { |
| ... | @@ -832,11 +890,27 @@ pub const Context = struct { | ... | @@ -832,11 +890,27 @@ pub const Context = struct { |
| 832 | const rhs = self.resolveInst(inst.rhs); | 890 | const rhs = self.resolveInst(inst.rhs); |
| 833 | | 891 | |
| 834 | switch (lhs) { | 892 | switch (lhs) { |
| 835 | // When assigning a value to a multi_value such as a struct, | 893 | .multi_value => |multi_value| switch (rhs) { |
| 836 | // we simply assign the local_index to the rhs one. | 894 | // When assigning a value to a multi_value such as a struct, |
| 837 | // This allows us to update struct fields without having to individually | 895 | // we simply assign the local_index to the rhs one. |
| 838 | // set each local as each field's index will be calculated off the struct's base index | 896 | // This allows us to update struct fields without having to individually |
| 839 | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! | 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 | .local => |local| { | 914 | .local => |local| { |
| 841 | try self.emitWValue(rhs); | 915 | try self.emitWValue(rhs); |
| 842 | try writer.writeByte(wasm.opcode(.local_set)); | 916 | try writer.writeByte(wasm.opcode(.local_set)); |
| ... | @@ -959,6 +1033,34 @@ pub const Context = struct { | ... | @@ -959,6 +1033,34 @@ pub const Context = struct { |
| 959 | try self.emitConstant(src, value, int_tag_ty); | 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 | else => |zig_type| return self.fail(src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}), | 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,7 +1233,7 @@ pub const Context = struct { |
| 1131 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { | 1233 | fn genStructFieldPtr(self: *Context, inst: *Inst.StructFieldPtr) InnerError!WValue { |
| 1132 | const struct_ptr = self.resolveInst(inst.struct_ptr); | 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 | fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue { | 1239 | fn genSwitchBr(self: *Context, inst: *Inst.SwitchBr) InnerError!WValue { |
| ... | @@ -1174,4 +1276,35 @@ pub const Context = struct { | ... | @@ -1174,4 +1276,35 @@ pub const Context = struct { |
| 1174 | | 1276 | |
| 1175 | return .none; | 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 | }; |