Commit ef2a4a08 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[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: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50481}
parent be9c5fd9
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment