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