| ... | @@ -483,8 +483,8 @@ pub const Result = union(enum) { | ... | @@ -483,8 +483,8 @@ pub const Result = union(enum) { |
| 483 | externally_managed: []const u8, | 483 | externally_managed: []const u8, |
| 484 | }; | 484 | }; |
| 485 | | 485 | |
| 486 | /// Hashmap to store generated `WValue` for each `Inst` | 486 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 487 | pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Index, WValue); | 487 | pub const ValueTable = std.AutoHashMapUnmanaged(Air.Inst.Ref, WValue); |
| 488 | | 488 | |
| 489 | /// Code represents the `Code` section of wasm that | 489 | /// Code represents the `Code` section of wasm that |
| 490 | /// belongs to a function | 490 | /// belongs to a function |
| ... | @@ -495,7 +495,7 @@ pub const Context = struct { | ... | @@ -495,7 +495,7 @@ pub const Context = struct { |
| 495 | air: Air, | 495 | air: Air, |
| 496 | liveness: Liveness, | 496 | liveness: Liveness, |
| 497 | gpa: *mem.Allocator, | 497 | gpa: *mem.Allocator, |
| 498 | /// Table to save `WValue`'s generated by an `Inst` | 498 | /// Table to save `WValue`'s generated by an `Air.Inst` |
| 499 | values: ValueTable, | 499 | values: ValueTable, |
| 500 | /// Mapping from Air.Inst.Index to block ids | 500 | /// Mapping from Air.Inst.Index to block ids |
| 501 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{}, | 501 | blocks: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, u32) = .{}, |
| ... | @@ -547,14 +547,15 @@ pub const Context = struct { | ... | @@ -547,14 +547,15 @@ pub const Context = struct { |
| 547 | | 547 | |
| 548 | /// Resolves the `WValue` for the given instruction `inst` | 548 | /// Resolves the `WValue` for the given instruction `inst` |
| 549 | /// When the given instruction has a `Value`, it returns a constant instead | 549 | /// When the given instruction has a `Value`, it returns a constant instead |
| 550 | fn resolveInst(self: Context, inst: Air.Inst) Index { | 550 | fn resolveInst(self: Context, ref: Air.Inst.Ref) WValue { |
| 551 | if (!inst.ty.hasCodeGenBits()) return .none; | 551 | const ref_type = self.air.getRefType(ref); |
| | 552 | if (ref_type.hasCodeGenBits()) return .none; |
| 552 | | 553 | |
| 553 | if (inst.value()) |_| { | 554 | if (self.air.instructions.items(.tag)[@enumToInt(ref)] == .constant) { |
| 554 | return WValue{ .constant = inst }; | 555 | return WValue{ .constant = @enumToInt(ref) }; |
| 555 | } | 556 | } |
| 556 | | 557 | |
| 557 | return self.values.get(inst).?; // Instruction does not dominate all uses! | 558 | return self.values.get(ref).?; // Instruction does not dominate all uses! |
| 558 | } | 559 | } |
| 559 | | 560 | |
| 560 | /// Using a given `Type`, returns the corresponding wasm Valtype | 561 | /// Using a given `Type`, returns the corresponding wasm Valtype |
| ... | @@ -610,7 +611,12 @@ pub const Context = struct { | ... | @@ -610,7 +611,12 @@ pub const Context = struct { |
| 610 | try writer.writeByte(wasm.opcode(.local_get)); | 611 | try writer.writeByte(wasm.opcode(.local_get)); |
| 611 | try leb.writeULEB128(writer, idx); | 612 | try leb.writeULEB128(writer, idx); |
| 612 | }, | 613 | }, |
| 613 | .constant => |inst| try self.emitConstant(inst.value().?, inst.ty), // creates a new constant onto the stack | 614 | .constant => |index| { |
| | 615 | const ty_pl = self.air.instructions.items(.data)[index].ty_pl; |
| | 616 | const value = self.air.values[ty_pl.payload]; |
| | 617 | // create a new constant onto the stack |
| | 618 | try self.emitConstant(value, self.air.getRefType(ty_pl.ty)); |
| | 619 | }, |
| 614 | } | 620 | } |
| 615 | } | 621 | } |
| 616 | | 622 | |
| ... | @@ -626,10 +632,7 @@ pub const Context = struct { | ... | @@ -626,10 +632,7 @@ pub const Context = struct { |
| 626 | const fields_len = @intCast(u32, struct_data.fields.count()); | 632 | const fields_len = @intCast(u32, struct_data.fields.count()); |
| 627 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len); | 633 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len); |
| 628 | for (struct_data.fields.values()) |*value| { | 634 | for (struct_data.fields.values()) |*value| { |
| 629 | const val_type = try self.genValtype( | 635 | const val_type = try self.genValtype(value.ty); |
| 630 | .{ .node_offset = struct_data.node_offset }, | | |
| 631 | value.ty, | | |
| 632 | ); | | |
| 633 | self.locals.appendAssumeCapacity(val_type); | 636 | self.locals.appendAssumeCapacity(val_type); |
| 634 | self.local_index += 1; | 637 | self.local_index += 1; |
| 635 | } | 638 | } |
| ... | @@ -640,7 +643,7 @@ pub const Context = struct { | ... | @@ -640,7 +643,7 @@ pub const Context = struct { |
| 640 | }, | 643 | }, |
| 641 | .ErrorUnion => { | 644 | .ErrorUnion => { |
| 642 | const payload_type = ty.errorUnionChild(); | 645 | const payload_type = ty.errorUnionChild(); |
| 643 | const val_type = try self.genValtype(.{ .node_offset = 0 }, payload_type); | 646 | const val_type = try self.genValtype(payload_type); |
| 644 | | 647 | |
| 645 | // we emit the error value as the first local, and the payload as the following. | 648 | // 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. | 649 | // The first local is also used to find the index of the error and payload. |
| ... | @@ -657,7 +660,7 @@ pub const Context = struct { | ... | @@ -657,7 +660,7 @@ pub const Context = struct { |
| 657 | } }; | 660 | } }; |
| 658 | }, | 661 | }, |
| 659 | else => { | 662 | else => { |
| 660 | const valtype = try self.genValtype(.{ .node_offset = 0 }, ty); | 663 | const valtype = try self.genValtype(ty); |
| 661 | try self.locals.append(self.gpa, valtype); | 664 | try self.locals.append(self.gpa, valtype); |
| 662 | self.local_index += 1; | 665 | self.local_index += 1; |
| 663 | return WValue{ .local = initial_index }; | 666 | return WValue{ .local = initial_index }; |
| ... | @@ -708,8 +711,7 @@ pub const Context = struct { | ... | @@ -708,8 +711,7 @@ pub const Context = struct { |
| 708 | } | 711 | } |
| 709 | } | 712 | } |
| 710 | | 713 | |
| 711 | pub fn genFunc(self: *Context, func: *Module.Fn) InnerError!Result { | 714 | pub fn genFunc(self: *Context) InnerError!Result { |
| 712 | _ = func; | | |
| 713 | try self.genFunctype(); | 715 | try self.genFunctype(); |
| 714 | // TODO: check for and handle death of instructions | 716 | // TODO: check for and handle death of instructions |
| 715 | | 717 | |
| ... | @@ -790,44 +792,43 @@ pub const Context = struct { | ... | @@ -790,44 +792,43 @@ pub const Context = struct { |
| 790 | fn genInst(self: *Context, inst: Air.Inst.Index) !WValue { | 792 | fn genInst(self: *Context, inst: Air.Inst.Index) !WValue { |
| 791 | const air_tags = self.air.instructions.items(.tag); | 793 | const air_tags = self.air.instructions.items(.tag); |
| 792 | return switch (air_tags[inst]) { | 794 | return switch (air_tags[inst]) { |
| 793 | // .add => self.genBinOp(inst.castTag(.add).?, .add), | 795 | .add => self.genBinOp(inst, .add), |
| 794 | // .alloc => self.genAlloc(inst.castTag(.alloc).?), | 796 | .alloc => self.genAlloc(inst), |
| 795 | // .arg => self.genArg(inst.castTag(.arg).?), | 797 | .arg => self.genArg(inst), |
| 796 | // .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"), | 798 | .bit_and => self.genBinOp(inst, .@"and"), |
| 797 | // .bitcast => self.genBitcast(inst.castTag(.bitcast).?), | 799 | .bitcast => self.genBitcast(inst), |
| 798 | // .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"), | 800 | .bit_or => self.genBinOp(inst, .@"or"), |
| 799 | // .block => self.genBlock(inst.castTag(.block).?), | 801 | .block => self.genBlock(inst), |
| 800 | // .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"), | 802 | .bool_and => self.genBinOp(inst, .@"and"), |
| 801 | // .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"), | 803 | .bool_or => self.genBinOp(inst, .@"or"), |
| 802 | // .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?), | 804 | .breakpoint => self.genBreakpoint(inst), |
| 803 | // .br => self.genBr(inst.castTag(.br).?), | 805 | .br => self.genBr(inst), |
| 804 | // .call => self.genCall(inst.castTag(.call).?), | 806 | .call => self.genCall(inst), |
| 805 | // .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), | 807 | .cmp_eq => self.genCmp(inst, .eq), |
| 806 | // .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte), | 808 | .cmp_gte => self.genCmp(inst, .gte), |
| 807 | // .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt), | 809 | .cmp_gt => self.genCmp(inst, .gt), |
| 808 | // .cmp_lte => self.genCmp(inst.castTag(.cmp_lte).?, .lte), | 810 | .cmp_lte => self.genCmp(inst, .lte), |
| 809 | // .cmp_lt => self.genCmp(inst.castTag(.cmp_lt).?, .lt), | 811 | .cmp_lt => self.genCmp(inst, .lt), |
| 810 | // .cmp_neq => self.genCmp(inst.castTag(.cmp_neq).?, .neq), | 812 | .cmp_neq => self.genCmp(inst, .neq), |
| 811 | // .condbr => self.genCondBr(inst.castTag(.condbr).?), | 813 | .cond_br => self.genCondBr(inst), |
| 812 | // .constant => unreachable, | 814 | .constant => unreachable, |
| 813 | // .dbg_stmt => WValue.none, | 815 | .dbg_stmt => WValue.none, |
| 814 | // .div => self.genBinOp(inst.castTag(.div).?, .div), | 816 | .div => self.genBinOp(inst, .div), |
| 815 | // .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne), | 817 | .is_err => self.genIsErr(inst, .i32_ne), |
| 816 | // .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq), | 818 | .is_non_err => self.genIsErr(inst, .i32_eq), |
| 817 | // .load => self.genLoad(inst.castTag(.load).?), | 819 | .load => self.genLoad(inst), |
| 818 | // .loop => self.genLoop(inst.castTag(.loop).?), | 820 | .loop => self.genLoop(inst), |
| 819 | // .mul => self.genBinOp(inst.castTag(.mul).?, .mul), | 821 | .mul => self.genBinOp(inst, .mul), |
| 820 | // .not => self.genNot(inst.castTag(.not).?), | 822 | .not => self.genNot(inst), |
| 821 | // .ret => self.genRet(inst.castTag(.ret).?), | 823 | .ret => self.genRet(inst), |
| 822 | // .retvoid => WValue.none, | 824 | .store => self.genStore(inst), |
| 823 | // .store => self.genStore(inst.castTag(.store).?), | 825 | .struct_field_ptr => self.genStructFieldPtr(inst), |
| 824 | // .struct_field_ptr => self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?), | 826 | .sub => self.genBinOp(inst, .sub), |
| 825 | // .sub => self.genBinOp(inst.castTag(.sub).?, .sub), | 827 | .switch_br => self.genSwitchBr(inst), |
| 826 | // .switchbr => self.genSwitchBr(inst.castTag(.switchbr).?), | 828 | .unreach => self.genUnreachable(inst), |
| 827 | // .unreach => self.genUnreachable(inst.castTag(.unreach).?), | 829 | .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst), |
| 828 | // .unwrap_errunion_payload => self.genUnwrapErrUnionPayload(inst.castTag(.unwrap_errunion_payload).?), | 830 | .wrap_errunion_payload => self.genWrapErrUnionPayload(inst), |
| 829 | // .wrap_errunion_payload => self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?), | 831 | .xor => self.genBinOp(inst, .xor), |
| 830 | // .xor => self.genBinOp(inst.castTag(.xor).?, .xor), | | |
| 831 | else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), | 832 | else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}), |
| 832 | }; | 833 | }; |
| 833 | } | 834 | } |
| ... | @@ -835,22 +836,27 @@ pub const Context = struct { | ... | @@ -835,22 +836,27 @@ pub const Context = struct { |
| 835 | fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void { | 836 | fn genBody(self: *Context, body: []const Air.Inst.Index) InnerError!void { |
| 836 | for (body) |inst| { | 837 | for (body) |inst| { |
| 837 | const result = try self.genInst(inst); | 838 | const result = try self.genInst(inst); |
| 838 | try self.values.putNoClobber(self.gpa, inst, result); | 839 | try self.values.putNoClobber(self.gpa, @intToEnum(Air.Inst.Ref, inst), result); |
| 839 | } | 840 | } |
| 840 | } | 841 | } |
| 841 | | 842 | |
| 842 | fn genRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 843 | fn genRet(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 843 | // TODO: Implement tail calls | 844 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 844 | const operand = self.resolveInst(inst.operand); | 845 | const operand = self.resolveInst(un_op); |
| 845 | try self.emitWValue(operand); | 846 | try self.emitWValue(operand); |
| 846 | try self.code.append(wasm.opcode(.@"return")); | 847 | try self.code.append(wasm.opcode(.@"return")); |
| 847 | return .none; | 848 | return .none; |
| 848 | } | 849 | } |
| 849 | | 850 | |
| 850 | fn genCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 851 | fn genCall(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 851 | const func_val = inst.func.value().?; | 852 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| | 853 | const extra = self.air.extraData(Air.Call, pl_op.payload); |
| | 854 | const args = self.air.extra[extra.end..][0..extra.data.args_len]; |
| 852 | | 855 | |
| 853 | const target: *Decl = blk: { | 856 | const target: *Decl = blk: { |
| | 857 | const ty_pl = self.air.instructions.items(.data)[@enumToInt(pl_op.operand)].ty_pl; |
| | 858 | const func_val = self.air.values[ty_pl.payload]; |
| | 859 | |
| 854 | if (func_val.castTag(.function)) |func| { | 860 | if (func_val.castTag(.function)) |func| { |
| 855 | break :blk func.data.owner_decl; | 861 | break :blk func.data.owner_decl; |
| 856 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { | 862 | } else if (func_val.castTag(.extern_fn)) |ext_fn| { |
| ... | @@ -859,8 +865,8 @@ pub const Context = struct { | ... | @@ -859,8 +865,8 @@ pub const Context = struct { |
| 859 | return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()}); | 865 | return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()}); |
| 860 | }; | 866 | }; |
| 861 | | 867 | |
| 862 | for (inst.args) |arg| { | 868 | for (args) |arg| { |
| 863 | const arg_val = self.resolveInst(arg); | 869 | const arg_val = self.resolveInst(@intToEnum(Air.Inst.Ref, arg)); |
| 864 | try self.emitWValue(arg_val); | 870 | try self.emitWValue(arg_val); |
| 865 | } | 871 | } |
| 866 | | 872 | |
| ... | @@ -877,15 +883,16 @@ pub const Context = struct { | ... | @@ -877,15 +883,16 @@ pub const Context = struct { |
| 877 | } | 883 | } |
| 878 | | 884 | |
| 879 | fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 885 | fn genAlloc(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 880 | const elem_type = inst.base.ty.elemType(); | 886 | const elem_type = self.air.getType(inst).elemType(); |
| 881 | return self.allocLocal(elem_type); | 887 | return self.allocLocal(elem_type); |
| 882 | } | 888 | } |
| 883 | | 889 | |
| 884 | fn genStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 890 | fn genStore(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| | 891 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 885 | const writer = self.code.writer(); | 892 | const writer = self.code.writer(); |
| 886 | | 893 | |
| 887 | const lhs = self.resolveInst(inst.lhs); | 894 | const lhs = self.resolveInst(bin_op.lhs); |
| 888 | const rhs = self.resolveInst(inst.rhs); | 895 | const rhs = self.resolveInst(bin_op.rhs); |
| 889 | | 896 | |
| 890 | switch (lhs) { | 897 | switch (lhs) { |
| 891 | .multi_value => |multi_value| switch (rhs) { | 898 | .multi_value => |multi_value| switch (rhs) { |
| ... | @@ -893,7 +900,7 @@ pub const Context = struct { | ... | @@ -893,7 +900,7 @@ pub const Context = struct { |
| 893 | // we simply assign the local_index to the rhs one. | 900 | // we simply assign the local_index to the rhs one. |
| 894 | // This allows us to update struct fields without having to individually | 901 | // This allows us to update struct fields without having to individually |
| 895 | // set each local as each field's index will be calculated off the struct's base index | 902 | // set each local as each field's index will be calculated off the struct's base index |
| 896 | .multi_value => self.values.put(self.gpa, inst.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! | 903 | .multi_value => self.values.put(self.gpa, bin_op.lhs, rhs) catch unreachable, // Instruction does not dominate all uses! |
| 897 | .constant, .none => { | 904 | .constant, .none => { |
| 898 | // emit all values onto the stack if constant | 905 | // emit all values onto the stack if constant |
| 899 | try self.emitWValue(rhs); | 906 | try self.emitWValue(rhs); |
| ... | @@ -920,7 +927,8 @@ pub const Context = struct { | ... | @@ -920,7 +927,8 @@ pub const Context = struct { |
| 920 | } | 927 | } |
| 921 | | 928 | |
| 922 | fn genLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 929 | fn genLoad(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 923 | return self.resolveInst(inst.operand); | 930 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 931 | return self.resolveInst(ty_op.operand); |
| 924 | } | 932 | } |
| 925 | | 933 | |
| 926 | fn genArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 934 | fn genArg(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| ... | @@ -931,8 +939,9 @@ pub const Context = struct { | ... | @@ -931,8 +939,9 @@ pub const Context = struct { |
| 931 | } | 939 | } |
| 932 | | 940 | |
| 933 | fn genBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue { | 941 | fn genBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 934 | const lhs = self.resolveInst(inst.lhs); | 942 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 935 | const rhs = self.resolveInst(inst.rhs); | 943 | const lhs = self.resolveInst(bin_op.lhs); |
| | 944 | const rhs = self.resolveInst(bin_op.rhs); |
| 936 | | 945 | |
| 937 | // it's possible for both lhs and/or rhs to return an offset as well, | 946 | // it's possible for both lhs and/or rhs to return an offset as well, |
| 938 | // in which case we return the first offset occurance we find. | 947 | // in which case we return the first offset occurance we find. |
| ... | @@ -945,10 +954,11 @@ pub const Context = struct { | ... | @@ -945,10 +954,11 @@ pub const Context = struct { |
| 945 | try self.emitWValue(lhs); | 954 | try self.emitWValue(lhs); |
| 946 | try self.emitWValue(rhs); | 955 | try self.emitWValue(rhs); |
| 947 | | 956 | |
| | 957 | const bin_ty = self.air.getRefType(bin_op.lhs); |
| 948 | const opcode: wasm.Opcode = buildOpcode(.{ | 958 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 949 | .op = op, | 959 | .op = op, |
| 950 | .valtype1 = try self.typeToValtype(inst.base.ty), | 960 | .valtype1 = try self.typeToValtype(bin_ty), |
| 951 | .signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned, | 961 | .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, |
| 952 | }); | 962 | }); |
| 953 | try self.code.append(wasm.opcode(opcode)); | 963 | try self.code.append(wasm.opcode(opcode)); |
| 954 | return WValue{ .code_offset = offset }; | 964 | return WValue{ .code_offset = offset }; |
| ... | @@ -1064,14 +1074,17 @@ pub const Context = struct { | ... | @@ -1064,14 +1074,17 @@ pub const Context = struct { |
| 1064 | } | 1074 | } |
| 1065 | } | 1075 | } |
| 1066 | | 1076 | |
| 1067 | fn genBlock(self: *Context, block: Air.Inst.Index) InnerError!WValue { | 1077 | fn genBlock(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1068 | const block_ty = try self.genBlockType(block.base.ty); | 1078 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| | 1079 | const block_ty = try self.genBlockType(self.air.getRefType(ty_pl.ty)); |
| | 1080 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| | 1081 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 1069 | | 1082 | |
| 1070 | try self.startBlock(.block, block_ty, null); | 1083 | try self.startBlock(.block, block_ty, null); |
| 1071 | // Here we set the current block idx, so breaks know the depth to jump | 1084 | // Here we set the current block idx, so breaks know the depth to jump |
| 1072 | // to when breaking out. | 1085 | // to when breaking out. |
| 1073 | try self.blocks.putNoClobber(self.gpa, block, self.block_depth); | 1086 | try self.blocks.putNoClobber(self.gpa, inst, self.block_depth); |
| 1074 | try self.genBody(block.body); | 1087 | try self.genBody(body); |
| 1075 | try self.endBlock(); | 1088 | try self.endBlock(); |
| 1076 | | 1089 | |
| 1077 | return .none; | 1090 | return .none; |
| ... | @@ -1095,11 +1108,15 @@ pub const Context = struct { | ... | @@ -1095,11 +1108,15 @@ pub const Context = struct { |
| 1095 | self.block_depth -= 1; | 1108 | self.block_depth -= 1; |
| 1096 | } | 1109 | } |
| 1097 | | 1110 | |
| 1098 | fn genLoop(self: *Context, loop: Air.Inst.Index) InnerError!WValue { | 1111 | fn genLoop(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1099 | const loop_ty = try self.genBlockType(loop.base.ty); | 1112 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| | 1113 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| | 1114 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 1100 | | 1115 | |
| 1101 | try self.startBlock(.loop, loop_ty, null); | 1116 | // result type of loop is always 'noreturn', meaning we can always |
| 1102 | try self.genBody(loop.body); | 1117 | // emit the wasm type 'block_empty'. |
| | 1118 | try self.startBlock(.loop, wasm.block_empty, null); |
| | 1119 | try self.genBody(body); |
| 1103 | | 1120 | |
| 1104 | // breaking to the index of a loop block will continue the loop instead | 1121 | // breaking to the index of a loop block will continue the loop instead |
| 1105 | try self.code.append(wasm.opcode(.br)); | 1122 | try self.code.append(wasm.opcode(.br)); |
| ... | @@ -1110,8 +1127,12 @@ pub const Context = struct { | ... | @@ -1110,8 +1127,12 @@ pub const Context = struct { |
| 1110 | return .none; | 1127 | return .none; |
| 1111 | } | 1128 | } |
| 1112 | | 1129 | |
| 1113 | fn genCondBr(self: *Context, condbr: Air.Inst.Index) InnerError!WValue { | 1130 | fn genCondBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1114 | const condition = self.resolveInst(condbr.condition); | 1131 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| | 1132 | const condition = self.resolveInst(pl_op.operand); |
| | 1133 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); |
| | 1134 | const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; |
| | 1135 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 1115 | const writer = self.code.writer(); | 1136 | const writer = self.code.writer(); |
| 1116 | | 1137 | |
| 1117 | // TODO: Handle death instructions for then and else body | 1138 | // TODO: Handle death instructions for then and else body |
| ... | @@ -1126,8 +1147,9 @@ pub const Context = struct { | ... | @@ -1126,8 +1147,9 @@ pub const Context = struct { |
| 1126 | break :blk offset; | 1147 | break :blk offset; |
| 1127 | }, | 1148 | }, |
| 1128 | }; | 1149 | }; |
| 1129 | const block_ty = try self.genBlockType(condbr.base.ty); | 1150 | |
| 1130 | try self.startBlock(.block, block_ty, offset); | 1151 | // result type is always noreturn, so use `block_empty` as type. |
| | 1152 | try self.startBlock(.block, wasm.block_empty, offset); |
| 1131 | | 1153 | |
| 1132 | // we inserted the block in front of the condition | 1154 | // we inserted the block in front of the condition |
| 1133 | // so now check if condition matches. If not, break outside this block | 1155 | // so now check if condition matches. If not, break outside this block |
| ... | @@ -1135,11 +1157,11 @@ pub const Context = struct { | ... | @@ -1135,11 +1157,11 @@ pub const Context = struct { |
| 1135 | try writer.writeByte(wasm.opcode(.br_if)); | 1157 | try writer.writeByte(wasm.opcode(.br_if)); |
| 1136 | try leb.writeULEB128(writer, @as(u32, 0)); | 1158 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1137 | | 1159 | |
| 1138 | try self.genBody(condbr.else_body); | 1160 | try self.genBody(else_body); |
| 1139 | try self.endBlock(); | 1161 | try self.endBlock(); |
| 1140 | | 1162 | |
| 1141 | // Outer block that matches the condition | 1163 | // Outer block that matches the condition |
| 1142 | try self.genBody(condbr.then_body); | 1164 | try self.genBody(then_body); |
| 1143 | | 1165 | |
| 1144 | return .none; | 1166 | return .none; |
| 1145 | } | 1167 | } |
| ... | @@ -1149,21 +1171,23 @@ pub const Context = struct { | ... | @@ -1149,21 +1171,23 @@ pub const Context = struct { |
| 1149 | // the comparison that we can later jump back to | 1171 | // the comparison that we can later jump back to |
| 1150 | const offset = self.code.items.len; | 1172 | const offset = self.code.items.len; |
| 1151 | | 1173 | |
| 1152 | const lhs = self.resolveInst(inst.lhs); | 1174 | const data: Air.Inst.Data = self.air.instructions.items(.data)[inst]; |
| 1153 | const rhs = self.resolveInst(inst.rhs); | 1175 | const lhs = self.resolveInst(data.bin_op.lhs); |
| | 1176 | const rhs = self.resolveInst(data.bin_op.rhs); |
| | 1177 | const lhs_ty = self.air.getRefType(data.bin_op.lhs); |
| 1154 | | 1178 | |
| 1155 | try self.emitWValue(lhs); | 1179 | try self.emitWValue(lhs); |
| 1156 | try self.emitWValue(rhs); | 1180 | try self.emitWValue(rhs); |
| 1157 | | 1181 | |
| 1158 | const signedness: std.builtin.Signedness = blk: { | 1182 | const signedness: std.builtin.Signedness = blk: { |
| 1159 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 1183 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| 1160 | if (inst.lhs.ty.zigTypeTag() != .Int) break :blk .unsigned; | 1184 | if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned; |
| 1161 | | 1185 | |
| 1162 | // incase of an actual integer, we emit the correct signedness | 1186 | // incase of an actual integer, we emit the correct signedness |
| 1163 | break :blk inst.lhs.ty.intInfo(self.target).signedness; | 1187 | break :blk lhs_ty.intInfo(self.target).signedness; |
| 1164 | }; | 1188 | }; |
| 1165 | const opcode: wasm.Opcode = buildOpcode(.{ | 1189 | const opcode: wasm.Opcode = buildOpcode(.{ |
| 1166 | .valtype1 = try self.typeToValtype(inst.lhs.ty), | 1190 | .valtype1 = try self.typeToValtype(lhs_ty), |
| 1167 | .op = switch (op) { | 1191 | .op = switch (op) { |
| 1168 | .lt => .lt, | 1192 | .lt => .lt, |
| 1169 | .lte => .le, | 1193 | .lte => .le, |
| ... | @@ -1178,16 +1202,17 @@ pub const Context = struct { | ... | @@ -1178,16 +1202,17 @@ pub const Context = struct { |
| 1178 | return WValue{ .code_offset = offset }; | 1202 | return WValue{ .code_offset = offset }; |
| 1179 | } | 1203 | } |
| 1180 | | 1204 | |
| 1181 | fn genBr(self: *Context, br: Air.Inst.Index) InnerError!WValue { | 1205 | fn genBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| | 1206 | const br = self.air.instructions.items(.data)[inst].br; |
| | 1207 | |
| 1182 | // if operand has codegen bits we should break with a value | 1208 | // if operand has codegen bits we should break with a value |
| 1183 | if (br.operand.ty.hasCodeGenBits()) { | 1209 | if (self.air.getRefType(br.operand).hasCodeGenBits()) { |
| 1184 | const operand = self.resolveInst(br.operand); | 1210 | try self.emitWValue(self.resolveInst(br.operand)); |
| 1185 | try self.emitWValue(operand); | | |
| 1186 | } | 1211 | } |
| 1187 | | 1212 | |
| 1188 | // We map every block to its block index. | 1213 | // We map every block to its block index. |
| 1189 | // We then determine how far we have to jump to it by substracting it from current block depth | 1214 | // We then determine how far we have to jump to it by substracting it from current block depth |
| 1190 | const idx: u32 = self.block_depth - self.blocks.get(br.block).?; | 1215 | const idx: u32 = self.block_depth - self.blocks.get(br.block_inst).?; |
| 1191 | const writer = self.code.writer(); | 1216 | const writer = self.code.writer(); |
| 1192 | try writer.writeByte(wasm.opcode(.br)); | 1217 | try writer.writeByte(wasm.opcode(.br)); |
| 1193 | try leb.writeULEB128(writer, idx); | 1218 | try leb.writeULEB128(writer, idx); |
| ... | @@ -1195,10 +1220,11 @@ pub const Context = struct { | ... | @@ -1195,10 +1220,11 @@ pub const Context = struct { |
| 1195 | return .none; | 1220 | return .none; |
| 1196 | } | 1221 | } |
| 1197 | | 1222 | |
| 1198 | fn genNot(self: *Context, not: Air.Inst.Index) InnerError!WValue { | 1223 | fn genNot(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| | 1224 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1199 | const offset = self.code.items.len; | 1225 | const offset = self.code.items.len; |
| 1200 | | 1226 | |
| 1201 | const operand = self.resolveInst(not.operand); | 1227 | const operand = self.resolveInst(ty_op.operand); |
| 1202 | try self.emitWValue(operand); | 1228 | try self.emitWValue(operand); |
| 1203 | | 1229 | |
| 1204 | // wasm does not have booleans nor the `not` instruction, therefore compare with 0 | 1230 | // wasm does not have booleans nor the `not` instruction, therefore compare with 0 |
| ... | @@ -1212,35 +1238,44 @@ pub const Context = struct { | ... | @@ -1212,35 +1238,44 @@ pub const Context = struct { |
| 1212 | return WValue{ .code_offset = offset }; | 1238 | return WValue{ .code_offset = offset }; |
| 1213 | } | 1239 | } |
| 1214 | | 1240 | |
| 1215 | fn genBreakpoint(self: *Context, breakpoint: Air.Inst.Index) InnerError!WValue { | 1241 | fn genBreakpoint(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1216 | _ = self; | 1242 | _ = self; |
| 1217 | _ = breakpoint; | 1243 | _ = inst; |
| 1218 | // unsupported by wasm itself. Can be implemented once we support DWARF | 1244 | // unsupported by wasm itself. Can be implemented once we support DWARF |
| 1219 | // for wasm | 1245 | // for wasm |
| 1220 | return .none; | 1246 | return .none; |
| 1221 | } | 1247 | } |
| 1222 | | 1248 | |
| 1223 | fn genUnreachable(self: *Context, unreach: Air.Inst.Index) InnerError!WValue { | 1249 | fn genUnreachable(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1224 | _ = unreach; | 1250 | _ = inst; |
| 1225 | try self.code.append(wasm.opcode(.@"unreachable")); | 1251 | try self.code.append(wasm.opcode(.@"unreachable")); |
| 1226 | return .none; | 1252 | return .none; |
| 1227 | } | 1253 | } |
| 1228 | | 1254 | |
| 1229 | fn genBitcast(self: *Context, bitcast: Air.Inst.Index) InnerError!WValue { | 1255 | fn genBitcast(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1230 | return self.resolveInst(bitcast.operand); | 1256 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 1257 | return self.resolveInst(ty_op.operand); |
| 1231 | } | 1258 | } |
| 1232 | | 1259 | |
| 1233 | fn genStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 1260 | fn genStructFieldPtr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1234 | const struct_ptr = self.resolveInst(inst.struct_ptr); | 1261 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| | 1262 | const extra = self.air.extraData(Air.StructField, ty_pl.payload); |
| | 1263 | const struct_ptr = self.resolveInst(extra.data.struct_ptr); |
| 1235 | | 1264 | |
| 1236 | return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, inst.field_index) }; | 1265 | return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) }; |
| 1237 | } | 1266 | } |
| 1238 | | 1267 | |
| 1239 | fn genSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 1268 | fn genSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1240 | const target = self.resolveInst(inst.target); | 1269 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 1241 | const target_ty = inst.target.ty; | 1270 | const extra = self.air.extraData(Air.SwitchBr, pl_op.payload); |
| 1242 | const valtype = try self.typeToValtype(.{ .node_offset = 0 }, target_ty); | 1271 | const cases = self.air.extra[extra.end..][0..extra.data.cases_len]; |
| 1243 | const blocktype = try self.genBlockType(inst.base.ty); | 1272 | const else_body = self.air.extra[extra.end + cases.len ..][0..extra.data.else_body_len]; |
| | 1273 | |
| | 1274 | const target = self.resolveInst(pl_op.operand); |
| | 1275 | const target_ty = self.air.getRefType(pl_op.operand); |
| | 1276 | const valtype = try self.typeToValtype(target_ty); |
| | 1277 | // result type is always 'noreturn' |
| | 1278 | const blocktype = wasm.block_empty; |
| 1244 | | 1279 | |
| 1245 | const signedness: std.builtin.Signedness = blk: { | 1280 | const signedness: std.builtin.Signedness = blk: { |
| 1246 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 1281 | // by default we tell the operand type is unsigned (i.e. bools and enum values) |
| ... | @@ -1249,11 +1284,18 @@ pub const Context = struct { | ... | @@ -1249,11 +1284,18 @@ pub const Context = struct { |
| 1249 | // incase of an actual integer, we emit the correct signedness | 1284 | // incase of an actual integer, we emit the correct signedness |
| 1250 | break :blk target_ty.intInfo(self.target).signedness; | 1285 | break :blk target_ty.intInfo(self.target).signedness; |
| 1251 | }; | 1286 | }; |
| 1252 | for (inst.cases) |case| { | 1287 | for (cases) |case_idx| { |
| | 1288 | const case = self.air.extraData(Air.SwitchBr.Case, case_idx); |
| | 1289 | const case_body = self.air.extra[case.end..][0..case.data.body_len]; |
| | 1290 | |
| 1253 | // create a block for each case, when the condition does not match we break out of it | 1291 | // create a block for each case, when the condition does not match we break out of it |
| 1254 | try self.startBlock(.block, blocktype, null); | 1292 | try self.startBlock(.block, blocktype, null); |
| 1255 | try self.emitWValue(target); | 1293 | try self.emitWValue(target); |
| 1256 | try self.emitConstant(.{ .node_offset = 0 }, case.item, target_ty); | 1294 | |
| | 1295 | // cases must represent a constant of which its type is in the `typed_value_map` |
| | 1296 | // Therefore we can simply retrieve it. |
| | 1297 | const ty_val = Air.Inst.Ref.typed_value_map[@enumToInt(case.data.item)]; |
| | 1298 | try self.emitConstant(ty_val.val, target_ty); |
| 1257 | const opcode = buildOpcode(.{ | 1299 | const opcode = buildOpcode(.{ |
| 1258 | .valtype1 = valtype, | 1300 | .valtype1 = valtype, |
| 1259 | .op = .ne, // not equal because we jump out the block if it does not match the condition | 1301 | .op = .ne, // not equal because we jump out the block if it does not match the condition |
| ... | @@ -1264,7 +1306,7 @@ pub const Context = struct { | ... | @@ -1264,7 +1306,7 @@ pub const Context = struct { |
| 1264 | try leb.writeULEB128(self.code.writer(), @as(u32, 0)); | 1306 | try leb.writeULEB128(self.code.writer(), @as(u32, 0)); |
| 1265 | | 1307 | |
| 1266 | // emit our block code | 1308 | // emit our block code |
| 1267 | try self.genBody(case.body); | 1309 | try self.genBody(case_body); |
| 1268 | | 1310 | |
| 1269 | // end the block we created earlier | 1311 | // end the block we created earlier |
| 1270 | try self.endBlock(); | 1312 | try self.endBlock(); |
| ... | @@ -1272,13 +1314,14 @@ pub const Context = struct { | ... | @@ -1272,13 +1314,14 @@ pub const Context = struct { |
| 1272 | | 1314 | |
| 1273 | // finally, emit the else case if it exists. Here we will not have to | 1315 | // finally, emit the else case if it exists. Here we will not have to |
| 1274 | // check for a condition, so also no need to emit a block. | 1316 | // check for a condition, so also no need to emit a block. |
| 1275 | try self.genBody(inst.else_body); | 1317 | try self.genBody(else_body); |
| 1276 | | 1318 | |
| 1277 | return .none; | 1319 | return .none; |
| 1278 | } | 1320 | } |
| 1279 | | 1321 | |
| 1280 | fn genIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { | 1322 | fn genIsErr(self: *Context, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!WValue { |
| 1281 | const operand = self.resolveInst(inst.operand); | 1323 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| | 1324 | const operand = self.resolveInst(un_op); |
| 1282 | const offset = self.code.items.len; | 1325 | const offset = self.code.items.len; |
| 1283 | const writer = self.code.writer(); | 1326 | const writer = self.code.writer(); |
| 1284 | | 1327 | |
| ... | @@ -1294,7 +1337,8 @@ pub const Context = struct { | ... | @@ -1294,7 +1337,8 @@ pub const Context = struct { |
| 1294 | } | 1337 | } |
| 1295 | | 1338 | |
| 1296 | fn genUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 1339 | fn genUnwrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1297 | const operand = self.resolveInst(inst.operand); | 1340 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 1341 | const operand = self.resolveInst(ty_op.operand); |
| 1298 | // The index of multi_value contains the error code. To get the initial index of the payload we get | 1342 | // The index of multi_value contains the error code. To get the initial index of the payload we get |
| 1299 | // the following index. Next, convert it to a `WValue.local` | 1343 | // the following index. Next, convert it to a `WValue.local` |
| 1300 | // | 1344 | // |
| ... | @@ -1303,6 +1347,7 @@ pub const Context = struct { | ... | @@ -1303,6 +1347,7 @@ pub const Context = struct { |
| 1303 | } | 1347 | } |
| 1304 | | 1348 | |
| 1305 | fn genWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue { | 1349 | fn genWrapErrUnionPayload(self: *Context, inst: Air.Inst.Index) InnerError!WValue { |
| 1306 | return self.resolveInst(inst.operand); | 1350 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| | 1351 | return self.resolveInst(ty_op.operand); |
| 1307 | } | 1352 | } |
| 1308 | }; | 1353 | }; |