From 6e139d124be92cfddef01adaa166ce09691cd5cc Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 1 Aug 2021 11:05:15 +0200 Subject: [PATCH] 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. --- src/codegen/wasm.zig | 68 ++++++++++++++++++++++++++++++++++++++++---- test/stage2/wasm.zig | 42 +++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 5178c43c00..e21645d1ee 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -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< 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()) { diff --git a/test/stage2/wasm.zig b/test/stage2/wasm.zig index a9f7984893..ab400f0a53 100644 --- a/test/stage2/wasm.zig +++ b/test/stage2/wasm.zig @@ -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;