[Liftoff] Implement i32 shift operations
This adds support for i32.shl, i32.shr_u and i32.shr_s. These are the first instructions implemented which have constraints on the registers they use (rcx in this case), so the implementation is a bit more involved. It's still worth trying to emit good code here, as shifts are quite common in our benchmarks. This code will later have to be extended to use i32 immediates directly instead of loading them into a register first. This will result in smaller code and better performance. R=titzer@chromium.org Bug: v8:6600 Change-Id: I45b41ab062b58a9b2bc7e14a68663180307b900d Reviewed-on: https://chromium-review.googlesource.com/859761 Commit-Queue: Clemens Hammacher <clemensh@chromium.org> Reviewed-by: Ben Titzer <titzer@chromium.org> Cr-Commit-Position: refs/heads/master@{#50481}
This commit is contained in:
parent
be9c5fd982
commit
ef2a4a08bb
@ -69,22 +69,25 @@ void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
#define DEFAULT_I32_BINOP(name, internal_name) \
|
||||
#define UNIMPLEMENTED_I32_BINOP(name) \
|
||||
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
|
||||
Register rhs) { \
|
||||
UNIMPLEMENTED(); \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
DEFAULT_I32_BINOP(add, add)
|
||||
DEFAULT_I32_BINOP(sub, sub)
|
||||
DEFAULT_I32_BINOP(mul, imul)
|
||||
DEFAULT_I32_BINOP(and, and)
|
||||
DEFAULT_I32_BINOP(or, or)
|
||||
DEFAULT_I32_BINOP(xor, xor)
|
||||
UNIMPLEMENTED_I32_BINOP(add)
|
||||
UNIMPLEMENTED_I32_BINOP(sub)
|
||||
UNIMPLEMENTED_I32_BINOP(mul)
|
||||
UNIMPLEMENTED_I32_BINOP(and)
|
||||
UNIMPLEMENTED_I32_BINOP(or)
|
||||
UNIMPLEMENTED_I32_BINOP(xor)
|
||||
UNIMPLEMENTED_I32_BINOP(shl)
|
||||
UNIMPLEMENTED_I32_BINOP(sar)
|
||||
UNIMPLEMENTED_I32_BINOP(shr)
|
||||
// clang-format on
|
||||
|
||||
#undef DEFAULT_I32_BINOP
|
||||
#undef UNIMPLEMENTED_I32_BINOP
|
||||
|
||||
void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -69,22 +69,25 @@ void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
#define DEFAULT_I32_BINOP(name, internal_name) \
|
||||
#define UNIMPLEMENTED_I32_BINOP(name) \
|
||||
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
|
||||
Register rhs) { \
|
||||
UNIMPLEMENTED(); \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
DEFAULT_I32_BINOP(add, add)
|
||||
DEFAULT_I32_BINOP(sub, sub)
|
||||
DEFAULT_I32_BINOP(mul, imul)
|
||||
DEFAULT_I32_BINOP(and, and)
|
||||
DEFAULT_I32_BINOP(or, or)
|
||||
DEFAULT_I32_BINOP(xor, xor)
|
||||
UNIMPLEMENTED_I32_BINOP(add)
|
||||
UNIMPLEMENTED_I32_BINOP(sub)
|
||||
UNIMPLEMENTED_I32_BINOP(mul)
|
||||
UNIMPLEMENTED_I32_BINOP(and)
|
||||
UNIMPLEMENTED_I32_BINOP(or)
|
||||
UNIMPLEMENTED_I32_BINOP(xor)
|
||||
UNIMPLEMENTED_I32_BINOP(shl)
|
||||
UNIMPLEMENTED_I32_BINOP(sar)
|
||||
UNIMPLEMENTED_I32_BINOP(shr)
|
||||
// clang-format on
|
||||
|
||||
#undef DEFAULT_I32_BINOP
|
||||
#undef UNIMPLEMENTED_I32_BINOP
|
||||
|
||||
void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -274,6 +274,54 @@ COMMUTATIVE_I32_BINOP(xor, xor_)
|
||||
|
||||
#undef COMMUTATIVE_I32_BINOP
|
||||
|
||||
namespace liftoff {
|
||||
inline void EmitShiftOperation(LiftoffAssembler* assm, Register dst,
|
||||
Register lhs, Register rhs,
|
||||
void (Assembler::*emit_shift)(Register)) {
|
||||
LiftoffRegList pinned = LiftoffRegList::ForRegs(dst, lhs, rhs);
|
||||
// If dst is ecx, compute into a tmp register first, then move to ecx.
|
||||
if (dst == ecx) {
|
||||
Register tmp = assm->GetUnusedRegister(kGpReg, pinned).gp();
|
||||
assm->mov(tmp, lhs);
|
||||
if (rhs != ecx) assm->mov(ecx, rhs);
|
||||
(assm->*emit_shift)(tmp);
|
||||
assm->mov(ecx, tmp);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move rhs into ecx. If ecx is in use, move its content to a tmp register
|
||||
// first. If lhs is ecx, lhs is now the tmp register.
|
||||
Register tmp_reg = no_reg;
|
||||
if (rhs != ecx) {
|
||||
if (lhs == ecx || assm->cache_state()->is_used(LiftoffRegister(ecx))) {
|
||||
tmp_reg = assm->GetUnusedRegister(kGpReg, pinned).gp();
|
||||
assm->mov(tmp_reg, ecx);
|
||||
if (lhs == ecx) lhs = tmp_reg;
|
||||
}
|
||||
assm->mov(ecx, rhs);
|
||||
}
|
||||
|
||||
// Do the actual shift.
|
||||
if (dst != lhs) assm->mov(dst, lhs);
|
||||
(assm->*emit_shift)(dst);
|
||||
|
||||
// Restore ecx if needed.
|
||||
if (tmp_reg.is_valid()) assm->mov(ecx, tmp_reg);
|
||||
}
|
||||
} // namespace liftoff
|
||||
|
||||
void LiftoffAssembler::emit_i32_shl(Register dst, Register lhs, Register rhs) {
|
||||
liftoff::EmitShiftOperation(this, dst, lhs, rhs, &Assembler::shl_cl);
|
||||
}
|
||||
|
||||
void LiftoffAssembler::emit_i32_sar(Register dst, Register lhs, Register rhs) {
|
||||
liftoff::EmitShiftOperation(this, dst, lhs, rhs, &Assembler::sar_cl);
|
||||
}
|
||||
|
||||
void LiftoffAssembler::emit_i32_shr(Register dst, Register lhs, Register rhs) {
|
||||
liftoff::EmitShiftOperation(this, dst, lhs, rhs, &Assembler::shr_cl);
|
||||
}
|
||||
|
||||
void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
|
||||
test(src, src);
|
||||
setcc(zero, dst);
|
||||
|
@ -303,6 +303,9 @@ class LiftoffAssembler : public TurboAssembler {
|
||||
inline void emit_i32_and(Register dst, Register lhs, Register rhs);
|
||||
inline void emit_i32_or(Register dst, Register lhs, Register rhs);
|
||||
inline void emit_i32_xor(Register dst, Register lhs, Register rhs);
|
||||
inline void emit_i32_shl(Register dst, Register lhs, Register rhs);
|
||||
inline void emit_i32_sar(Register dst, Register lhs, Register rhs);
|
||||
inline void emit_i32_shr(Register dst, Register lhs, Register rhs);
|
||||
|
||||
// i32 unops.
|
||||
inline void emit_i32_eqz(Register dst, Register src);
|
||||
|
@ -484,6 +484,9 @@ class LiftoffCompiler {
|
||||
CASE_BINOP(I32And, I32, i32_and)
|
||||
CASE_BINOP(I32Ior, I32, i32_or)
|
||||
CASE_BINOP(I32Xor, I32, i32_xor)
|
||||
CASE_BINOP(I32Shl, I32, i32_shl)
|
||||
CASE_BINOP(I32ShrS, I32, i32_sar)
|
||||
CASE_BINOP(I32ShrU, I32, i32_shr)
|
||||
CASE_BINOP(F32Add, F32, f32_add)
|
||||
CASE_BINOP(F32Sub, F32, f32_sub)
|
||||
CASE_BINOP(F32Mul, F32, f32_mul)
|
||||
|
@ -208,6 +208,15 @@ class LiftoffRegList {
|
||||
return LiftoffRegList(bits);
|
||||
}
|
||||
|
||||
template <typename... Regs>
|
||||
static LiftoffRegList ForRegs(Regs... regs) {
|
||||
std::array<LiftoffRegister, sizeof...(regs)> regs_arr{
|
||||
LiftoffRegister(regs)...};
|
||||
LiftoffRegList list;
|
||||
for (LiftoffRegister reg : regs_arr) list.set(reg);
|
||||
return list;
|
||||
}
|
||||
|
||||
private:
|
||||
storage_t regs_ = 0;
|
||||
|
||||
|
@ -69,22 +69,25 @@ void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
#define DEFAULT_I32_BINOP(name, internal_name) \
|
||||
#define UNIMPLEMENTED_I32_BINOP(name) \
|
||||
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
|
||||
Register rhs) { \
|
||||
UNIMPLEMENTED(); \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
DEFAULT_I32_BINOP(add, add)
|
||||
DEFAULT_I32_BINOP(sub, sub)
|
||||
DEFAULT_I32_BINOP(mul, imul)
|
||||
DEFAULT_I32_BINOP(and, and)
|
||||
DEFAULT_I32_BINOP(or, or)
|
||||
DEFAULT_I32_BINOP(xor, xor)
|
||||
UNIMPLEMENTED_I32_BINOP(add)
|
||||
UNIMPLEMENTED_I32_BINOP(sub)
|
||||
UNIMPLEMENTED_I32_BINOP(mul)
|
||||
UNIMPLEMENTED_I32_BINOP(and)
|
||||
UNIMPLEMENTED_I32_BINOP(or)
|
||||
UNIMPLEMENTED_I32_BINOP(xor)
|
||||
UNIMPLEMENTED_I32_BINOP(shl)
|
||||
UNIMPLEMENTED_I32_BINOP(sar)
|
||||
UNIMPLEMENTED_I32_BINOP(shr)
|
||||
// clang-format on
|
||||
|
||||
#undef DEFAULT_I32_BINOP
|
||||
#undef UNIMPLEMENTED_I32_BINOP
|
||||
|
||||
void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -69,22 +69,25 @@ void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
#define DEFAULT_I32_BINOP(name, internal_name) \
|
||||
#define UNIMPLEMENTED_I32_BINOP(name) \
|
||||
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
|
||||
Register rhs) { \
|
||||
UNIMPLEMENTED(); \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
DEFAULT_I32_BINOP(add, add)
|
||||
DEFAULT_I32_BINOP(sub, sub)
|
||||
DEFAULT_I32_BINOP(mul, imul)
|
||||
DEFAULT_I32_BINOP(and, and)
|
||||
DEFAULT_I32_BINOP(or, or)
|
||||
DEFAULT_I32_BINOP(xor, xor)
|
||||
UNIMPLEMENTED_I32_BINOP(add)
|
||||
UNIMPLEMENTED_I32_BINOP(sub)
|
||||
UNIMPLEMENTED_I32_BINOP(mul)
|
||||
UNIMPLEMENTED_I32_BINOP(and)
|
||||
UNIMPLEMENTED_I32_BINOP(or)
|
||||
UNIMPLEMENTED_I32_BINOP(xor)
|
||||
UNIMPLEMENTED_I32_BINOP(shl)
|
||||
UNIMPLEMENTED_I32_BINOP(sar)
|
||||
UNIMPLEMENTED_I32_BINOP(shr)
|
||||
// clang-format on
|
||||
|
||||
#undef DEFAULT_I32_BINOP
|
||||
#undef UNIMPLEMENTED_I32_BINOP
|
||||
|
||||
void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -69,22 +69,25 @@ void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
#define DEFAULT_I32_BINOP(name, internal_name) \
|
||||
#define UNIMPLEMENTED_I32_BINOP(name) \
|
||||
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
|
||||
Register rhs) { \
|
||||
UNIMPLEMENTED(); \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
DEFAULT_I32_BINOP(add, add)
|
||||
DEFAULT_I32_BINOP(sub, sub)
|
||||
DEFAULT_I32_BINOP(mul, imul)
|
||||
DEFAULT_I32_BINOP(and, and)
|
||||
DEFAULT_I32_BINOP(or, or)
|
||||
DEFAULT_I32_BINOP(xor, xor)
|
||||
UNIMPLEMENTED_I32_BINOP(add)
|
||||
UNIMPLEMENTED_I32_BINOP(sub)
|
||||
UNIMPLEMENTED_I32_BINOP(mul)
|
||||
UNIMPLEMENTED_I32_BINOP(and)
|
||||
UNIMPLEMENTED_I32_BINOP(or)
|
||||
UNIMPLEMENTED_I32_BINOP(xor)
|
||||
UNIMPLEMENTED_I32_BINOP(shl)
|
||||
UNIMPLEMENTED_I32_BINOP(sar)
|
||||
UNIMPLEMENTED_I32_BINOP(shr)
|
||||
// clang-format on
|
||||
|
||||
#undef DEFAULT_I32_BINOP
|
||||
#undef UNIMPLEMENTED_I32_BINOP
|
||||
|
||||
void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -69,22 +69,25 @@ void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
#define DEFAULT_I32_BINOP(name, internal_name) \
|
||||
#define UNIMPLEMENTED_I32_BINOP(name) \
|
||||
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
|
||||
Register rhs) { \
|
||||
UNIMPLEMENTED(); \
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
DEFAULT_I32_BINOP(add, add)
|
||||
DEFAULT_I32_BINOP(sub, sub)
|
||||
DEFAULT_I32_BINOP(mul, imul)
|
||||
DEFAULT_I32_BINOP(and, and)
|
||||
DEFAULT_I32_BINOP(or, or)
|
||||
DEFAULT_I32_BINOP(xor, xor)
|
||||
UNIMPLEMENTED_I32_BINOP(add)
|
||||
UNIMPLEMENTED_I32_BINOP(sub)
|
||||
UNIMPLEMENTED_I32_BINOP(mul)
|
||||
UNIMPLEMENTED_I32_BINOP(and)
|
||||
UNIMPLEMENTED_I32_BINOP(or)
|
||||
UNIMPLEMENTED_I32_BINOP(xor)
|
||||
UNIMPLEMENTED_I32_BINOP(shl)
|
||||
UNIMPLEMENTED_I32_BINOP(sar)
|
||||
UNIMPLEMENTED_I32_BINOP(shr)
|
||||
// clang-format on
|
||||
|
||||
#undef DEFAULT_I32_BINOP
|
||||
#undef UNIMPLEMENTED_I32_BINOP
|
||||
|
||||
void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
|
||||
UNIMPLEMENTED();
|
||||
|
@ -266,6 +266,51 @@ COMMUTATIVE_I32_BINOP(xor, xor)
|
||||
|
||||
#undef COMMUTATIVE_I32_BINOP
|
||||
|
||||
namespace liftoff {
|
||||
inline void EmitShiftOperation(LiftoffAssembler* assm, Register dst,
|
||||
Register lhs, Register rhs,
|
||||
void (Assembler::*emit_shift)(Register)) {
|
||||
// If dst is rcx, compute into the scratch register first, then move to rcx.
|
||||
if (dst == rcx) {
|
||||
assm->movl(kScratchRegister, lhs);
|
||||
if (rhs != rcx) assm->movl(rcx, rhs);
|
||||
(assm->*emit_shift)(kScratchRegister);
|
||||
assm->movl(rcx, kScratchRegister);
|
||||
return;
|
||||
}
|
||||
|
||||
// Move rhs into rcx. If rcx is in use, move its content into the scratch
|
||||
// register. If lhs is rcx, lhs is now the scratch register.
|
||||
bool use_scratch = false;
|
||||
if (rhs != rcx) {
|
||||
use_scratch =
|
||||
lhs == rcx || assm->cache_state()->is_used(LiftoffRegister(rcx));
|
||||
if (use_scratch) assm->movl(kScratchRegister, rcx);
|
||||
if (lhs == rcx) lhs = kScratchRegister;
|
||||
assm->movl(rcx, rhs);
|
||||
}
|
||||
|
||||
// Do the actual shift.
|
||||
if (dst != lhs) assm->movl(dst, lhs);
|
||||
(assm->*emit_shift)(dst);
|
||||
|
||||
// Restore rcx if needed.
|
||||
if (use_scratch) assm->movl(rcx, kScratchRegister);
|
||||
}
|
||||
} // namespace liftoff
|
||||
|
||||
void LiftoffAssembler::emit_i32_shl(Register dst, Register lhs, Register rhs) {
|
||||
liftoff::EmitShiftOperation(this, dst, lhs, rhs, &Assembler::shll_cl);
|
||||
}
|
||||
|
||||
void LiftoffAssembler::emit_i32_sar(Register dst, Register lhs, Register rhs) {
|
||||
liftoff::EmitShiftOperation(this, dst, lhs, rhs, &Assembler::sarl_cl);
|
||||
}
|
||||
|
||||
void LiftoffAssembler::emit_i32_shr(Register dst, Register lhs, Register rhs) {
|
||||
liftoff::EmitShiftOperation(this, dst, lhs, rhs, &Assembler::shrl_cl);
|
||||
}
|
||||
|
||||
void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
|
||||
testl(src, src);
|
||||
setcc(zero, dst);
|
||||
|
Loading…
Reference in New Issue
Block a user