[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:
parent
55650c0813
commit
e554dec4f8
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user