| ... | @@ -634,7 +634,7 @@ pub const Context = struct { | ... | @@ -634,7 +634,7 @@ pub const Context = struct { |
| 634 | // for each struct field, generate a local | 634 | // for each struct field, generate a local |
| 635 | const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; | 635 | const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; |
| 636 | const fields_len = @intCast(u32, struct_data.fields.count()); | 636 | const fields_len = @intCast(u32, struct_data.fields.count()); |
| 637 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len); | 637 | try self.locals.ensureUnusedCapacity(self.gpa, fields_len); |
| 638 | for (struct_data.fields.values()) |*value| { | 638 | for (struct_data.fields.values()) |*value| { |
| 639 | const val_type = try self.genValtype(value.ty); | 639 | const val_type = try self.genValtype(value.ty); |
| 640 | self.locals.appendAssumeCapacity(val_type); | 640 | self.locals.appendAssumeCapacity(val_type); |
| ... | @@ -653,7 +653,7 @@ pub const Context = struct { | ... | @@ -653,7 +653,7 @@ pub const Context = struct { |
| 653 | // The first local is also used to find the index of the error and payload. | 653 | // The first local is also used to find the index of the error and payload. |
| 654 | // | 654 | // |
| 655 | // TODO: Add support where the payload is a type that contains multiple locals such as a struct. | 655 | // TODO: Add support where the payload is a type that contains multiple locals such as a struct. |
| 656 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2); | 656 | try self.locals.ensureUnusedCapacity(self.gpa, 2); |
| 657 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 | 657 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 |
| 658 | self.locals.appendAssumeCapacity(val_type); | 658 | self.locals.appendAssumeCapacity(val_type); |
| 659 | self.local_index += 2; | 659 | self.local_index += 2; |
| ... | @@ -670,7 +670,7 @@ pub const Context = struct { | ... | @@ -670,7 +670,7 @@ pub const Context = struct { |
| 670 | return self.fail("TODO: wasm optional pointer", .{}); | 670 | return self.fail("TODO: wasm optional pointer", .{}); |
| 671 | } | 671 | } |
| 672 | | 672 | |
| 673 | try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2); | 673 | try self.locals.ensureUnusedCapacity(self.gpa, 2); |
| 674 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32 | 674 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32 |
| 675 | self.locals.appendAssumeCapacity(try self.genValtype(child_type)); | 675 | self.locals.appendAssumeCapacity(try self.genValtype(child_type)); |
| 676 | self.local_index += 2; | 676 | self.local_index += 2; |
| ... | @@ -817,11 +817,11 @@ pub const Context = struct { | ... | @@ -817,11 +817,11 @@ pub const Context = struct { |
| 817 | const air_tags = self.air.instructions.items(.tag); | 817 | const air_tags = self.air.instructions.items(.tag); |
| 818 | return switch (air_tags[inst]) { | 818 | return switch (air_tags[inst]) { |
| 819 | .add => self.airBinOp(inst, .add), | 819 | .add => self.airBinOp(inst, .add), |
| 820 | .addwrap => self.airBinOp(inst, .add), | 820 | .addwrap => self.airWrapBinOp(inst, .add), |
| 821 | .sub => self.airBinOp(inst, .sub), | 821 | .sub => self.airBinOp(inst, .sub), |
| 822 | .subwrap => self.airBinOp(inst, .sub), | 822 | .subwrap => self.airWrapBinOp(inst, .sub), |
| 823 | .mul => self.airBinOp(inst, .mul), | 823 | .mul => self.airBinOp(inst, .mul), |
| 824 | .mulwrap => self.airBinOp(inst, .mul), | 824 | .mulwrap => self.airWrapBinOp(inst, .mul), |
| 825 | .div => self.airBinOp(inst, .div), | 825 | .div => self.airBinOp(inst, .div), |
| 826 | .bit_and => self.airBinOp(inst, .@"and"), | 826 | .bit_and => self.airBinOp(inst, .@"and"), |
| 827 | .bit_or => self.airBinOp(inst, .@"or"), | 827 | .bit_or => self.airBinOp(inst, .@"or"), |
| ... | @@ -1021,6 +1021,62 @@ pub const Context = struct { | ... | @@ -1021,6 +1021,62 @@ pub const Context = struct { |
| 1021 | return WValue{ .code_offset = offset }; | 1021 | return WValue{ .code_offset = offset }; |
| 1022 | } | 1022 | } |
| 1023 | | 1023 | |
| | 1024 | fn airWrapBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| | 1025 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| | 1026 | const lhs = self.resolveInst(bin_op.lhs); |
| | 1027 | const rhs = self.resolveInst(bin_op.rhs); |
| | 1028 | |
| | 1029 | // it's possible for both lhs and/or rhs to return an offset as well, |
| | 1030 | // in which case we return the first offset occurance we find. |
| | 1031 | const offset = blk: { |
| | 1032 | if (lhs == .code_offset) break :blk lhs.code_offset; |
| | 1033 | if (rhs == .code_offset) break :blk rhs.code_offset; |
| | 1034 | break :blk self.code.items.len; |
| | 1035 | }; |
| | 1036 | |
| | 1037 | try self.emitWValue(lhs); |
| | 1038 | try self.emitWValue(rhs); |
| | 1039 | |
| | 1040 | const bin_ty = self.air.typeOf(bin_op.lhs); |
| | 1041 | const opcode: wasm.Opcode = buildOpcode(.{ |
| | 1042 | .op = op, |
| | 1043 | .valtype1 = try self.typeToValtype(bin_ty), |
| | 1044 | .signedness = if (bin_ty.isSignedInt()) .signed else .unsigned, |
| | 1045 | }); |
| | 1046 | try self.code.append(wasm.opcode(opcode)); |
| | 1047 | |
| | 1048 | const int_info = bin_ty.intInfo(self.target); |
| | 1049 | const bitsize = int_info.bits; |
| | 1050 | const is_signed = int_info.signedness == .signed; |
| | 1051 | // if target type bitsize is x < 32 and 32 > x < 64, we perform |
| | 1052 | // result & ((1<<N)-1) where N = bitsize or bitsize -1 incase of signed. |
| | 1053 | if (bitsize != 32 and bitsize < 64) { |
| | 1054 | // first check if we can use a single instruction, |
| | 1055 | // wasm provides those if the integers are signed and 8/16-bit. |
| | 1056 | // For arbitrary integer sizes, we use the algorithm mentioned above. |
| | 1057 | if (is_signed and bitsize == 8) { |
| | 1058 | try self.code.append(wasm.opcode(.i32_extend8_s)); |
| | 1059 | } else if (is_signed and bitsize == 16) { |
| | 1060 | try self.code.append(wasm.opcode(.i32_extend16_s)); |
| | 1061 | } else { |
| | 1062 | const result = (@as(u64, 1) << @intCast(u6, bitsize - @boolToInt(is_signed))) - 1; |
| | 1063 | if (bitsize < 32) { |
| | 1064 | try self.code.append(wasm.opcode(.i32_const)); |
| | 1065 | try leb.writeILEB128(self.code.writer(), @bitCast(i32, @intCast(u32, result))); |
| | 1066 | try self.code.append(wasm.opcode(.i32_and)); |
| | 1067 | } else { |
| | 1068 | try self.code.append(wasm.opcode(.i64_const)); |
| | 1069 | try leb.writeILEB128(self.code.writer(), @bitCast(i64, result)); |
| | 1070 | try self.code.append(wasm.opcode(.i64_and)); |
| | 1071 | } |
| | 1072 | } |
| | 1073 | } else if (int_info.bits > 64) { |
| | 1074 | return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{}); |
| | 1075 | } |
| | 1076 | |
| | 1077 | return WValue{ .code_offset = offset }; |
| | 1078 | } |
| | 1079 | |
| 1024 | fn emitConstant(self: *Context, val: Value, ty: Type) InnerError!void { | 1080 | fn emitConstant(self: *Context, val: Value, ty: Type) InnerError!void { |
| 1025 | const writer = self.code.writer(); | 1081 | const writer = self.code.writer(); |
| 1026 | switch (ty.zigTypeTag()) { | 1082 | switch (ty.zigTypeTag()) { |