authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-06-10 21:54:23+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-06-11 19:38:00+02:00
logf05e09a0cf7870fda463d9a0ceb42be4c3100825
tree858b5107d90a97392b5765a318672b276531feb7
parent18afcc34c61a18ada7bda0fc50f48e929866ab82

wasm: optimize & simplify sign extension

Rather than storing all the shifts in temporaries, we perform the correct shifting without temporaries. This makes the runtime code more performant and also the backend code is simplified as we have a singular abstraction.

1 files changed, 12 insertions(+), 39 deletions(-)

src/arch/wasm/CodeGen.zig+12-39
......@@ -4162,22 +4162,14 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
41624162 64 => WValue{ .imm64 = 0 },
41634163 else => unreachable,
41644164 };
4165 const shift_amt = wasm_bits - int_info.bits;
4166 const shift_val = switch (wasm_bits) {
4167 32 => WValue{ .imm32 = shift_amt },
4168 64 => WValue{ .imm64 = shift_amt },
4169 else => unreachable,
4170 };
41714165
41724166 // for signed integers, we first apply signed shifts by the difference in bits
41734167 // to get the signed value, as we store it internally as 2's complement.
41744168 const lhs = if (wasm_bits != int_info.bits and is_signed) blk: {
4175 const shl = try self.binOp(lhs_op, shift_val, lhs_ty, .shl);
4176 break :blk try self.binOp(shl, shift_val, lhs_ty, .shr);
4169 break :blk try self.signAbsValue(lhs_op, lhs_ty);
41774170 } else lhs_op;
41784171 const rhs = if (wasm_bits != int_info.bits and is_signed) blk: {
4179 const shl = try self.binOp(rhs_op, shift_val, lhs_ty, .shl);
4180 break :blk try self.binOp(shl, shift_val, lhs_ty, .shr);
4172 break :blk try self.signAbsValue(rhs_op, lhs_ty);
41814173 } else rhs_op;
41824174
41834175 const bin_op = try self.binOp(lhs, rhs, lhs_ty, op);
......@@ -4192,9 +4184,8 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!W
41924184 const lt = try self.cmp(bin_op, lhs, lhs_ty, .lt);
41934185 break :blk try self.binOp(cmp_zero, lt, Type.u32, .xor); // result of cmp_zero and lt is always 32bit
41944186 }
4195 const shl = try self.binOp(bin_op, shift_val, lhs_ty, .shl);
4196 const shr = try self.binOp(shl, shift_val, lhs_ty, .shr);
4197 break :blk try self.cmp(shr, bin_op, lhs_ty, .neq);
4187 const abs = try self.signAbsValue(bin_op, lhs_ty);
4188 break :blk try self.cmp(abs, bin_op, lhs_ty, .neq);
41984189 } else if (wasm_bits == int_info.bits)
41994190 try self.cmp(bin_op, lhs, lhs_ty, cmp_op)
42004191 else
......@@ -4289,17 +4280,9 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
42894280 } else shl;
42904281
42914282 const overflow_bit = if (wasm_bits != int_info.bits and is_signed) blk: {
4292 const shift_amt = wasm_bits - int_info.bits;
4293 const shift_val = switch (wasm_bits) {
4294 32 => WValue{ .imm32 = shift_amt },
4295 64 => WValue{ .imm64 = shift_amt },
4296 else => unreachable,
4297 };
4298
4299 const secondary_shl = try self.binOp(shl, shift_val, lhs_ty, .shl);
4300 const initial_shr = try self.binOp(secondary_shl, shift_val, lhs_ty, .shr);
4301 const shr = try self.wrapBinOp(initial_shr, rhs, lhs_ty, .shr);
4302 break :blk try self.cmp(lhs, shr, lhs_ty, .neq);
4283 const abs = try self.signAbsValue(shl, lhs_ty);
4284 const wrapped = try self.wrapBinOp(abs, rhs, lhs_ty, .shr);
4285 break :blk try self.cmp(lhs, wrapped, lhs_ty, .neq);
43034286 } else blk: {
43044287 const shr = try self.binOp(result, rhs, lhs_ty, .shr);
43054288 break :blk try self.cmp(lhs, shr, lhs_ty, .neq);
......@@ -4367,21 +4350,11 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
43674350 break :blk down_cast;
43684351 }
43694352 } else if (int_info.signedness == .signed) blk: {
4370 const shift_imm = if (wasm_bits == 32)
4371 WValue{ .imm32 = wasm_bits - int_info.bits }
4372 else
4373 WValue{ .imm64 = wasm_bits - int_info.bits };
4374
4375 const lhs_shl = try self.binOp(lhs, shift_imm, lhs_ty, .shl);
4376 const lhs_shr = try self.binOp(lhs_shl, shift_imm, lhs_ty, .shr);
4377 const rhs_shl = try self.binOp(rhs, shift_imm, lhs_ty, .shl);
4378 const rhs_shr = try self.binOp(rhs_shl, shift_imm, lhs_ty, .shr);
4379
4380 const bin_op = try self.binOp(lhs_shr, rhs_shr, lhs_ty, .mul);
4381 const shl = try self.binOp(bin_op, shift_imm, lhs_ty, .shl);
4382 const shr = try self.binOp(shl, shift_imm, lhs_ty, .shr);
4383
4384 const cmp_op = try self.cmp(shr, bin_op, lhs_ty, .neq);
4353 const lhs_abs = try self.signAbsValue(lhs, lhs_ty);
4354 const rhs_abs = try self.signAbsValue(rhs, lhs_ty);
4355 const bin_op = try self.binOp(lhs_abs, rhs_abs, lhs_ty, .mul);
4356 const mul_abs = try self.signAbsValue(bin_op, lhs_ty);
4357 const cmp_op = try self.cmp(mul_abs, bin_op, lhs_ty, .neq);
43854358 try self.emitWValue(cmp_op);
43864359 try self.addLabel(.local_set, overflow_bit.local);
43874360 break :blk try self.wrapOperand(bin_op, lhs_ty);