| ... | ... | @@ -634,7 +634,7 @@ pub const Context = struct { |
| 634 | 634 | // for each struct field, generate a local |
| 635 | 635 | const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data; |
| 636 | 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 | 638 | for (struct_data.fields.values()) |*value| { |
| 639 | 639 | const val_type = try self.genValtype(value.ty); |
| 640 | 640 | self.locals.appendAssumeCapacity(val_type); |
| ... | ... | @@ -653,7 +653,7 @@ pub const Context = struct { |
| 653 | 653 | // The first local is also used to find the index of the error and payload. |
| 654 | 654 | // |
| 655 | 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 | 657 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32 |
| 658 | 658 | self.locals.appendAssumeCapacity(val_type); |
| 659 | 659 | self.local_index += 2; |
| ... | ... | @@ -670,7 +670,7 @@ pub const Context = struct { |
| 670 | 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 | 674 | self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32 |
| 675 | 675 | self.locals.appendAssumeCapacity(try self.genValtype(child_type)); |
| 676 | 676 | self.local_index += 2; |
| ... | ... | @@ -817,11 +817,11 @@ pub const Context = struct { |
| 817 | 817 | const air_tags = self.air.instructions.items(.tag); |
| 818 | 818 | return switch (air_tags[inst]) { |
| 819 | 819 | .add => self.airBinOp(inst, .add), |
| 820 | | .addwrap => self.airBinOp(inst, .add), |
| 820 | .addwrap => self.airWrapBinOp(inst, .add), |
| 821 | 821 | .sub => self.airBinOp(inst, .sub), |
| 822 | | .subwrap => self.airBinOp(inst, .sub), |
| 822 | .subwrap => self.airWrapBinOp(inst, .sub), |
| 823 | 823 | .mul => self.airBinOp(inst, .mul), |
| 824 | | .mulwrap => self.airBinOp(inst, .mul), |
| 824 | .mulwrap => self.airWrapBinOp(inst, .mul), |
| 825 | 825 | .div => self.airBinOp(inst, .div), |
| 826 | 826 | .bit_and => self.airBinOp(inst, .@"and"), |
| 827 | 827 | .bit_or => self.airBinOp(inst, .@"or"), |
| ... | ... | @@ -1021,6 +1021,62 @@ pub const Context = struct { |
| 1021 | 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 | 1080 | fn emitConstant(self: *Context, val: Value, ty: Type) InnerError!void { |
| 1025 | 1081 | const writer = self.code.writer(); |
| 1026 | 1082 | switch (ty.zigTypeTag()) { |