[Liftoff] Implement i64.clz and i64.ctz

These are two of the remaining missing instructions from the MVP.
This CL adds support to {LiftoffCompiler} and provides assembly
implementations for ia32, x64, arm, and arm64.

R=jkummerow@chromium.org

Bug: v8:9919
Change-Id: I4d00d2030e1c5c03ee3afaa536697d3847e26ef0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1893343
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64752}
This commit is contained in:
Clemens Backes 2019-11-04 15:51:21 +01:00 committed by Commit Bot
parent 55650c0813
commit e554dec4f8
6 changed files with 129 additions and 6 deletions

View File

@ -964,6 +964,45 @@ void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
LsrPair(dst.low_gp(), dst.high_gp(), src.low_gp(), src_high, amount);
}
void LiftoffAssembler::emit_i64_clz(LiftoffRegister dst, LiftoffRegister src) {
// return high == 0 ? 32 + CLZ32(low) : CLZ32(high);
Label done;
Label high_is_zero;
cmp(src.high_gp(), Operand(0));
b(&high_is_zero, eq);
clz(dst.low_gp(), src.high_gp());
jmp(&done);
bind(&high_is_zero);
clz(dst.low_gp(), src.low_gp());
add(dst.low_gp(), dst.low_gp(), Operand(32));
bind(&done);
mov(dst.high_gp(), Operand(0)); // High word of result is always 0.
}
void LiftoffAssembler::emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src) {
// return low == 0 ? 32 + CTZ32(high) : CTZ32(low);
// CTZ32(x) = CLZ(RBIT(x))
Label done;
Label low_is_zero;
cmp(src.low_gp(), Operand(0));
b(&low_is_zero, eq);
rbit(dst.low_gp(), src.low_gp());
clz(dst.low_gp(), dst.low_gp());
jmp(&done);
bind(&low_is_zero);
rbit(dst.low_gp(), src.high_gp());
clz(dst.low_gp(), dst.low_gp());
add(dst.low_gp(), dst.low_gp(), Operand(32));
bind(&done);
mov(dst.high_gp(), Operand(0)); // High word of result is always 0.
}
bool LiftoffAssembler::emit_f32_ceil(DoubleRegister dst, DoubleRegister src) {
if (CpuFeatures::IsSupported(ARMv8)) {
CpuFeatureScope scope(this, ARMv8);

View File

@ -582,6 +582,15 @@ bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) {
return true;
}
void LiftoffAssembler::emit_i64_clz(LiftoffRegister dst, LiftoffRegister src) {
Clz(dst.gp().X(), src.gp().X());
}
void LiftoffAssembler::emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src) {
Rbit(dst.gp().X(), src.gp().X());
Clz(dst.gp().X(), dst.gp().X());
}
void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {

View File

@ -1015,6 +1015,69 @@ void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
ShrPair(dst.high_gp(), dst.low_gp(), amount);
}
void LiftoffAssembler::emit_i64_clz(LiftoffRegister dst, LiftoffRegister src) {
// return high == 0 ? 32 + CLZ32(low) : CLZ32(high);
Label done;
Register safe_dst = dst.low_gp();
if (src.low_gp() == safe_dst) safe_dst = dst.high_gp();
if (CpuFeatures::IsSupported(LZCNT)) {
CpuFeatureScope scope(this, LZCNT);
lzcnt(safe_dst, src.high_gp()); // Sets CF if high == 0.
j(not_carry, &done, Label::kNear);
lzcnt(safe_dst, src.low_gp());
add(safe_dst, Immediate(32)); // 32 + CLZ32(low)
} else {
// CLZ32(x) =^ x == 0 ? 32 : 31 - BSR32(x)
Label high_is_zero;
bsr(safe_dst, src.high_gp()); // Sets ZF is high == 0.
j(zero, &high_is_zero, Label::kNear);
xor_(safe_dst, Immediate(31)); // for x in [0..31], 31^x == 31-x.
jmp(&done, Label::kNear);
bind(&high_is_zero);
Label low_not_zero;
bsr(safe_dst, src.low_gp());
j(not_zero, &low_not_zero, Label::kNear);
mov(safe_dst, Immediate(64 ^ 63)); // 64, after the xor below.
bind(&low_not_zero);
xor_(safe_dst, 63); // for x in [0..31], 63^x == 63-x.
}
bind(&done);
if (safe_dst != dst.low_gp()) mov(dst.low_gp(), safe_dst);
xor_(dst.high_gp(), dst.high_gp()); // High word of result is always 0.
}
void LiftoffAssembler::emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src) {
// return low == 0 ? 32 + CTZ32(high) : CTZ32(low);
Label done;
Register safe_dst = dst.low_gp();
if (src.high_gp() == safe_dst) safe_dst = dst.high_gp();
if (CpuFeatures::IsSupported(BMI1)) {
CpuFeatureScope scope(this, BMI1);
tzcnt(safe_dst, src.low_gp()); // Sets CF if low == 0.
j(not_carry, &done, Label::kNear);
tzcnt(safe_dst, src.high_gp());
add(safe_dst, Immediate(32)); // 32 + CTZ32(high)
} else {
// CTZ32(x) =^ x == 0 ? 32 : BSF32(x)
bsf(safe_dst, src.low_gp()); // Sets ZF is low == 0.
j(not_zero, &done, Label::kNear);
Label high_not_zero;
bsf(safe_dst, src.high_gp());
j(not_zero, &high_not_zero, Label::kNear);
mov(safe_dst, 64); // low == 0 and high == 0
jmp(&done);
bind(&high_not_zero);
add(safe_dst, Immediate(32)); // 32 + CTZ32(high)
}
bind(&done);
if (safe_dst != dst.low_gp()) mov(dst.low_gp(), safe_dst);
xor_(dst.high_gp(), dst.high_gp()); // High word of result is always 0.
}
void LiftoffAssembler::emit_i32_to_intptr(Register dst, Register src) {
// This is a nop on ia32.
}

View File

@ -460,6 +460,10 @@ class LiftoffAssembler : public TurboAssembler {
inline void emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
int amount);
// i64 unops.
inline void emit_i64_clz(LiftoffRegister dst, LiftoffRegister src);
inline void emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src);
inline void emit_i32_to_intptr(Register dst, Register src);
inline void emit_ptrsize_add(Register dst, Register lhs, Register rhs) {

View File

@ -821,6 +821,8 @@ class LiftoffCompiler {
CASE_I64_UNOP(I64SExtendI8, i64_signextend_i8)
CASE_I64_UNOP(I64SExtendI16, i64_signextend_i16)
CASE_I64_UNOP(I64SExtendI32, i64_signextend_i32)
CASE_I64_UNOP(I64Clz, i64_clz)
CASE_I64_UNOP(I64Ctz, i64_ctz)
case kExprI32Eqz:
DCHECK(decoder->lookahead(0, kExprI32Eqz));
if (decoder->lookahead(1, kExprBrIf)) {
@ -833,18 +835,16 @@ class LiftoffCompiler {
__ emit_i32_eqz(dst.gp(), src.gp());
});
break;
case kExprI32Popcnt:
EmitI32UnOpWithCFallback(&LiftoffAssembler::emit_i32_popcnt,
&ExternalReference::wasm_word32_popcnt);
break;
case kExprI64Eqz:
EmitUnOp<kWasmI64, kWasmI32>(
[=](LiftoffRegister dst, LiftoffRegister src) {
__ emit_i64_eqz(dst.gp(), src);
});
break;
case kExprI64Clz:
case kExprI64Ctz:
case kExprI32Popcnt:
EmitI32UnOpWithCFallback(&LiftoffAssembler::emit_i32_popcnt,
&ExternalReference::wasm_word32_popcnt);
break;
case kExprI64Popcnt:
return unsupported(decoder, kComplexOperation,
WasmOpcodes::OpcodeName(opcode));

View File

@ -885,6 +885,14 @@ void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
shrq(dst.gp(), Immediate(amount));
}
void LiftoffAssembler::emit_i64_clz(LiftoffRegister dst, LiftoffRegister src) {
Lzcntq(dst.gp(), src.gp());
}
void LiftoffAssembler::emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src) {
Tzcntq(dst.gp(), src.gp());
}
void LiftoffAssembler::emit_i32_to_intptr(Register dst, Register src) {
movsxlq(dst, src);
}