mirror of
https://github.com/ziglang/zig.git
synced 2026-02-07 06:57:13 +00:00
wasm: Resolve feedback (wrapping arbitrary int sizes)
- This ensures we honor the user's integer size when performing wrapping operations. - Also, instead of using ensureCapacity, we now use ensureUnusedCapacity.
This commit is contained in:
parent
a861b7d160
commit
6e139d124b
@ -634,7 +634,7 @@ pub const Context = struct {
|
||||
// for each struct field, generate a local
|
||||
const struct_data: *Module.Struct = ty.castTag(.@"struct").?.data;
|
||||
const fields_len = @intCast(u32, struct_data.fields.count());
|
||||
try self.locals.ensureCapacity(self.gpa, self.locals.items.len + fields_len);
|
||||
try self.locals.ensureUnusedCapacity(self.gpa, fields_len);
|
||||
for (struct_data.fields.values()) |*value| {
|
||||
const val_type = try self.genValtype(value.ty);
|
||||
self.locals.appendAssumeCapacity(val_type);
|
||||
@ -653,7 +653,7 @@ pub const Context = struct {
|
||||
// The first local is also used to find the index of the error and payload.
|
||||
//
|
||||
// TODO: Add support where the payload is a type that contains multiple locals such as a struct.
|
||||
try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2);
|
||||
try self.locals.ensureUnusedCapacity(self.gpa, 2);
|
||||
self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // error values are always i32
|
||||
self.locals.appendAssumeCapacity(val_type);
|
||||
self.local_index += 2;
|
||||
@ -670,7 +670,7 @@ pub const Context = struct {
|
||||
return self.fail("TODO: wasm optional pointer", .{});
|
||||
}
|
||||
|
||||
try self.locals.ensureCapacity(self.gpa, self.locals.items.len + 2);
|
||||
try self.locals.ensureUnusedCapacity(self.gpa, 2);
|
||||
self.locals.appendAssumeCapacity(wasm.valtype(.i32)); // optional 'tag' for null-checking is always i32
|
||||
self.locals.appendAssumeCapacity(try self.genValtype(child_type));
|
||||
self.local_index += 2;
|
||||
@ -817,11 +817,11 @@ pub const Context = struct {
|
||||
const air_tags = self.air.instructions.items(.tag);
|
||||
return switch (air_tags[inst]) {
|
||||
.add => self.airBinOp(inst, .add),
|
||||
.addwrap => self.airBinOp(inst, .add),
|
||||
.addwrap => self.airWrapBinOp(inst, .add),
|
||||
.sub => self.airBinOp(inst, .sub),
|
||||
.subwrap => self.airBinOp(inst, .sub),
|
||||
.subwrap => self.airWrapBinOp(inst, .sub),
|
||||
.mul => self.airBinOp(inst, .mul),
|
||||
.mulwrap => self.airBinOp(inst, .mul),
|
||||
.mulwrap => self.airWrapBinOp(inst, .mul),
|
||||
.div => self.airBinOp(inst, .div),
|
||||
.bit_and => self.airBinOp(inst, .@"and"),
|
||||
.bit_or => self.airBinOp(inst, .@"or"),
|
||||
@ -1021,6 +1021,62 @@ pub const Context = struct {
|
||||
return WValue{ .code_offset = offset };
|
||||
}
|
||||
|
||||
fn airWrapBinOp(self: *Context, inst: Air.Inst.Index, op: Op) InnerError!WValue {
|
||||
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
|
||||
const lhs = self.resolveInst(bin_op.lhs);
|
||||
const rhs = self.resolveInst(bin_op.rhs);
|
||||
|
||||
// it's possible for both lhs and/or rhs to return an offset as well,
|
||||
// in which case we return the first offset occurance we find.
|
||||
const offset = blk: {
|
||||
if (lhs == .code_offset) break :blk lhs.code_offset;
|
||||
if (rhs == .code_offset) break :blk rhs.code_offset;
|
||||
break :blk self.code.items.len;
|
||||
};
|
||||
|
||||
try self.emitWValue(lhs);
|
||||
try self.emitWValue(rhs);
|
||||
|
||||
const bin_ty = self.air.typeOf(bin_op.lhs);
|
||||
const opcode: wasm.Opcode = buildOpcode(.{
|
||||
.op = op,
|
||||
.valtype1 = try self.typeToValtype(bin_ty),
|
||||
.signedness = if (bin_ty.isSignedInt()) .signed else .unsigned,
|
||||
});
|
||||
try self.code.append(wasm.opcode(opcode));
|
||||
|
||||
const int_info = bin_ty.intInfo(self.target);
|
||||
const bitsize = int_info.bits;
|
||||
const is_signed = int_info.signedness == .signed;
|
||||
// if target type bitsize is x < 32 and 32 > x < 64, we perform
|
||||
// result & ((1<<N)-1) where N = bitsize or bitsize -1 incase of signed.
|
||||
if (bitsize != 32 and bitsize < 64) {
|
||||
// first check if we can use a single instruction,
|
||||
// wasm provides those if the integers are signed and 8/16-bit.
|
||||
// For arbitrary integer sizes, we use the algorithm mentioned above.
|
||||
if (is_signed and bitsize == 8) {
|
||||
try self.code.append(wasm.opcode(.i32_extend8_s));
|
||||
} else if (is_signed and bitsize == 16) {
|
||||
try self.code.append(wasm.opcode(.i32_extend16_s));
|
||||
} else {
|
||||
const result = (@as(u64, 1) << @intCast(u6, bitsize - @boolToInt(is_signed))) - 1;
|
||||
if (bitsize < 32) {
|
||||
try self.code.append(wasm.opcode(.i32_const));
|
||||
try leb.writeILEB128(self.code.writer(), @bitCast(i32, @intCast(u32, result)));
|
||||
try self.code.append(wasm.opcode(.i32_and));
|
||||
} else {
|
||||
try self.code.append(wasm.opcode(.i64_const));
|
||||
try leb.writeILEB128(self.code.writer(), @bitCast(i64, result));
|
||||
try self.code.append(wasm.opcode(.i64_and));
|
||||
}
|
||||
}
|
||||
} else if (int_info.bits > 64) {
|
||||
return self.fail("TODO wasm: Integer wrapping for bitsizes larger than 64", .{});
|
||||
}
|
||||
|
||||
return WValue{ .code_offset = offset };
|
||||
}
|
||||
|
||||
fn emitConstant(self: *Context, val: Value, ty: Type) InnerError!void {
|
||||
const writer = self.code.writer();
|
||||
switch (ty.zigTypeTag()) {
|
||||
|
||||
@ -120,6 +120,20 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\}
|
||||
, "-2147483648\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() i32 {
|
||||
\\ var i: i4 = 7;
|
||||
\\ return i +% 1;
|
||||
\\}
|
||||
, "0\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var i: u8 = 255;
|
||||
\\ return i +% 1;
|
||||
\\}
|
||||
, "0\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var i: u32 = 5;
|
||||
@ -147,6 +161,20 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\}
|
||||
, "2147483647\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() i32 {
|
||||
\\ var i: i7 = -64;
|
||||
\\ return i -% 1;
|
||||
\\}
|
||||
, "63\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var i: u4 = 0;
|
||||
\\ return i -% 1;
|
||||
\\}
|
||||
, "15\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var i: u32 = 5;
|
||||
@ -178,6 +206,20 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\}
|
||||
, "-2\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var i: u3 = 3;
|
||||
\\ return i *% 3;
|
||||
\\}
|
||||
, "1\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() i32 {
|
||||
\\ var i: i4 = 3;
|
||||
\\ return i *% 3;
|
||||
\\}
|
||||
, "1\n");
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn _start() u32 {
|
||||
\\ var i: u32 = 352;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user