Commit 6afe7d18 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[Liftoff] Implement f32.copysign and f64.copysign

These are two of the few missing instructions. This CL implements them
for ia32 and x64, and bails out on other platforms.
On x64, we are using the BTR instruction since we cannot have 64-bit
immediates.

Drive-by: Fix naming of existing bt/bts instructions on x64.

R=titzer@chromium.org

Bug: v8:6600
Change-Id: Ib8532ca811160cd61f4ba7c06b04ce093861c872
Reviewed-on: https://chromium-review.googlesource.com/1174383
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55780}
parent a2139261
......@@ -161,6 +161,7 @@ UNIMPLEMENTED_FP_BINOP(f32_mul)
UNIMPLEMENTED_FP_BINOP(f32_div)
UNIMPLEMENTED_FP_BINOP(f32_min)
UNIMPLEMENTED_FP_BINOP(f32_max)
UNIMPLEMENTED_FP_BINOP(f32_copysign)
UNIMPLEMENTED_FP_UNOP(f32_abs)
UNIMPLEMENTED_FP_UNOP(f32_neg)
UNIMPLEMENTED_FP_UNOP(f32_ceil)
......@@ -174,6 +175,7 @@ UNIMPLEMENTED_FP_BINOP(f64_mul)
UNIMPLEMENTED_FP_BINOP(f64_div)
UNIMPLEMENTED_FP_BINOP(f64_min)
UNIMPLEMENTED_FP_BINOP(f64_max)
UNIMPLEMENTED_FP_BINOP(f64_copysign)
UNIMPLEMENTED_FP_UNOP(f64_abs)
UNIMPLEMENTED_FP_UNOP(f64_neg)
UNIMPLEMENTED_FP_UNOP_RETURN_TRUE(f64_ceil)
......
......@@ -626,6 +626,16 @@ void LiftoffAssembler::emit_i32_to_intptr(Register dst, Register src) {
Sxtw(dst, src);
}
void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
BAILOUT("f32_copysign");
}
void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
BAILOUT("f64_copysign");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -1025,6 +1025,20 @@ void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
liftoff::MinOrMax::kMax);
}
void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
static constexpr int kF32SignBit = 1 << 31;
Register scratch = GetUnusedRegister(kGpReg).gp();
Register scratch2 =
GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(scratch)).gp();
Movd(scratch, lhs); // move {lhs} into {scratch}.
and_(scratch, Immediate(~kF32SignBit)); // clear sign bit in {scratch}.
Movd(scratch2, rhs); // move {rhs} into {scratch2}.
and_(scratch2, Immediate(kF32SignBit)); // isolate sign bit in {scratch2}.
or_(scratch, scratch2); // combine {scratch2} into {scratch}.
Movd(dst, scratch); // move result into {dst}.
}
void LiftoffAssembler::emit_f32_abs(DoubleRegister dst, DoubleRegister src) {
static constexpr uint32_t kSignBit = uint32_t{1} << 31;
if (dst == src) {
......@@ -1134,6 +1148,24 @@ void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
liftoff::MinOrMax::kMin);
}
void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
static constexpr int kF32SignBit = 1 << 31;
// On ia32, we cannot hold the whole f64 value in a gp register, so we just
// operate on the upper half (UH).
Register scratch = GetUnusedRegister(kGpReg).gp();
Register scratch2 =
GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(scratch)).gp();
Pextrd(scratch, lhs, 1); // move UH of {lhs} into {scratch}.
and_(scratch, Immediate(~kF32SignBit)); // clear sign bit in {scratch}.
Pextrd(scratch2, rhs, 1); // move UH of {rhs} into {scratch2}.
and_(scratch2, Immediate(kF32SignBit)); // isolate sign bit in {scratch2}.
or_(scratch, scratch2); // combine {scratch2} into {scratch}.
movsd(dst, lhs); // move {lhs} into {dst}.
Pinsrd(dst, scratch, 1, true); // insert {scratch} into UH of {dst}.
}
void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
liftoff::EmitFloatMinOrMax<double>(this, dst, lhs, rhs,
......
......@@ -484,6 +484,8 @@ class LiftoffAssembler : public TurboAssembler {
DoubleRegister rhs);
inline void emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs);
inline void emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs);
// f32 unops.
inline void emit_f32_abs(DoubleRegister dst, DoubleRegister src);
......@@ -507,6 +509,8 @@ class LiftoffAssembler : public TurboAssembler {
DoubleRegister rhs);
inline void emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs);
inline void emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs);
// f64 unops.
inline void emit_f64_abs(DoubleRegister dst, DoubleRegister src);
......
......@@ -888,12 +888,14 @@ class LiftoffCompiler {
CASE_FLOAT_BINOP(F32Div, F32, f32_div)
CASE_FLOAT_BINOP(F32Min, F32, f32_min)
CASE_FLOAT_BINOP(F32Max, F32, f32_max)
CASE_FLOAT_BINOP(F32CopySign, F32, f32_copysign)
CASE_FLOAT_BINOP(F64Add, F64, f64_add)
CASE_FLOAT_BINOP(F64Sub, F64, f64_sub)
CASE_FLOAT_BINOP(F64Mul, F64, f64_mul)
CASE_FLOAT_BINOP(F64Div, F64, f64_div)
CASE_FLOAT_BINOP(F64Min, F64, f64_min)
CASE_FLOAT_BINOP(F64Max, F64, f64_max)
CASE_FLOAT_BINOP(F64CopySign, F64, f64_copysign)
case WasmOpcode::kExprI32DivS:
EmitBinOp<kWasmI32, kWasmI32>([this, decoder](LiftoffRegister dst,
LiftoffRegister lhs,
......
......@@ -83,10 +83,12 @@ class LiftoffRegister {
public:
explicit LiftoffRegister(Register reg) : LiftoffRegister(reg.code()) {
DCHECK_NE(0, kLiftoffAssemblerGpCacheRegs & reg.bit());
DCHECK_EQ(reg, gp());
}
explicit LiftoffRegister(DoubleRegister reg)
: LiftoffRegister(kAfterMaxLiftoffGpRegCode + reg.code()) {
DCHECK_NE(0, kLiftoffAssemblerFpCacheRegs & reg.bit());
DCHECK_EQ(reg, fp());
}
......
......@@ -814,6 +814,11 @@ void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
bind(&done);
}
void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
BAILOUT("f32_copysign");
}
void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
Label ool, done;
......@@ -836,6 +841,11 @@ void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
bind(&done);
}
void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
BAILOUT("f64_copysign");
}
#define FP_BINOP(name, instruction) \
void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister lhs, \
DoubleRegister rhs) { \
......
......@@ -693,6 +693,11 @@ void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
bind(&done);
}
void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
BAILOUT("f32_copysign");
}
void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
Label ool, done;
......@@ -715,6 +720,11 @@ void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
bind(&done);
}
void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
BAILOUT("f64_copysign");
}
#define FP_BINOP(name, instruction) \
void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister lhs, \
DoubleRegister rhs) { \
......
......@@ -166,6 +166,7 @@ UNIMPLEMENTED_FP_BINOP(f32_mul)
UNIMPLEMENTED_FP_BINOP(f32_div)
UNIMPLEMENTED_FP_BINOP(f32_min)
UNIMPLEMENTED_FP_BINOP(f32_max)
UNIMPLEMENTED_FP_BINOP(f32_copysign)
UNIMPLEMENTED_FP_UNOP(f32_abs)
UNIMPLEMENTED_FP_UNOP(f32_neg)
UNIMPLEMENTED_FP_UNOP(f32_ceil)
......@@ -179,6 +180,7 @@ UNIMPLEMENTED_FP_BINOP(f64_mul)
UNIMPLEMENTED_FP_BINOP(f64_div)
UNIMPLEMENTED_FP_BINOP(f64_min)
UNIMPLEMENTED_FP_BINOP(f64_max)
UNIMPLEMENTED_FP_BINOP(f64_copysign)
UNIMPLEMENTED_FP_UNOP(f64_abs)
UNIMPLEMENTED_FP_UNOP(f64_neg)
UNIMPLEMENTED_FP_UNOP_RETURN_TRUE(f64_ceil)
......
......@@ -166,6 +166,7 @@ UNIMPLEMENTED_FP_BINOP(f32_mul)
UNIMPLEMENTED_FP_BINOP(f32_div)
UNIMPLEMENTED_FP_BINOP(f32_min)
UNIMPLEMENTED_FP_BINOP(f32_max)
UNIMPLEMENTED_FP_BINOP(f32_copysign)
UNIMPLEMENTED_FP_UNOP(f32_abs)
UNIMPLEMENTED_FP_UNOP(f32_neg)
UNIMPLEMENTED_FP_UNOP(f32_ceil)
......@@ -179,6 +180,7 @@ UNIMPLEMENTED_FP_BINOP(f64_mul)
UNIMPLEMENTED_FP_BINOP(f64_div)
UNIMPLEMENTED_FP_BINOP(f64_min)
UNIMPLEMENTED_FP_BINOP(f64_max)
UNIMPLEMENTED_FP_BINOP(f64_copysign)
UNIMPLEMENTED_FP_UNOP(f64_abs)
UNIMPLEMENTED_FP_UNOP(f64_neg)
UNIMPLEMENTED_FP_UNOP_RETURN_TRUE(f64_ceil)
......
......@@ -23,9 +23,11 @@ namespace wasm {
namespace liftoff {
constexpr Register kScratchRegister2 = r11;
static_assert(kScratchRegister != kScratchRegister2, "collision");
static_assert((kLiftoffAssemblerGpCacheRegs &
Register::ListOf<kScratchRegister>()) == 0,
"scratch register must not be used as cache registers");
Register::ListOf<kScratchRegister, kScratchRegister2>()) == 0,
"scratch registers must not be used as cache registers");
constexpr DoubleRegister kScratchDoubleReg2 = xmm14;
static_assert(kScratchDoubleReg != kScratchDoubleReg2, "collision");
......@@ -898,6 +900,17 @@ void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
liftoff::MinOrMax::kMax);
}
void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
static constexpr int kF32SignBit = 1 << 31;
Movd(kScratchRegister, lhs);
andl(kScratchRegister, Immediate(~kF32SignBit));
Movd(liftoff::kScratchRegister2, rhs);
andl(liftoff::kScratchRegister2, Immediate(kF32SignBit));
orl(kScratchRegister, liftoff::kScratchRegister2);
Movd(dst, kScratchRegister);
}
void LiftoffAssembler::emit_f32_abs(DoubleRegister dst, DoubleRegister src) {
static constexpr uint32_t kSignBit = uint32_t{1} << 31;
if (dst == src) {
......@@ -1007,6 +1020,20 @@ void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
liftoff::MinOrMax::kMin);
}
void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
// Extract sign bit from {rhs} into {kScratchRegister2}.
Movq(liftoff::kScratchRegister2, rhs);
shrq(liftoff::kScratchRegister2, Immediate(63));
shlq(liftoff::kScratchRegister2, Immediate(63));
// Reset sign bit of {lhs} (in {kScratchRegister}).
Movq(kScratchRegister, lhs);
btrq(kScratchRegister, Immediate(63));
// Combine both values into {kScratchRegister} and move into {dst}.
orq(kScratchRegister, liftoff::kScratchRegister2);
Movq(dst, kScratchRegister);
}
void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
DoubleRegister rhs) {
liftoff::EmitFloatMinOrMax<double>(this, dst, lhs, rhs,
......
......@@ -943,7 +943,7 @@ void Assembler::bswapq(Register dst) {
emit(0xC8 + dst.low_bits());
}
void Assembler::bt(Operand dst, Register src) {
void Assembler::btq(Operand dst, Register src) {
EnsureSpace ensure_space(this);
emit_rex_64(src, dst);
emit(0x0F);
......@@ -951,7 +951,7 @@ void Assembler::bt(Operand dst, Register src) {
emit_operand(src, dst);
}
void Assembler::bts(Operand dst, Register src) {
void Assembler::btsq(Operand dst, Register src) {
EnsureSpace ensure_space(this);
emit_rex_64(src, dst);
emit(0x0F);
......@@ -959,6 +959,23 @@ void Assembler::bts(Operand dst, Register src) {
emit_operand(src, dst);
}
void Assembler::btsq(Register dst, Immediate imm8) {
EnsureSpace ensure_space(this);
emit_rex_64(dst);
emit(0x0F);
emit(0xBA);
emit_modrm(0x5, dst);
emit(imm8.value_);
}
void Assembler::btrq(Register dst, Immediate imm8) {
EnsureSpace ensure_space(this);
emit_rex_64(dst);
emit(0x0F);
emit(0xBA);
emit_modrm(0x6, dst);
emit(imm8.value_);
}
void Assembler::bsrl(Register dst, Register src) {
EnsureSpace ensure_space(this);
......
......@@ -856,8 +856,10 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// Bit operations.
void bswapl(Register dst);
void bswapq(Register dst);
void bt(Operand dst, Register src);
void bts(Operand dst, Register src);
void btq(Operand dst, Register src);
void btsq(Operand dst, Register src);
void btsq(Register dst, Immediate imm8);
void btrq(Register dst, Immediate imm8);
void bsrq(Register dst, Register src);
void bsrq(Register dst, Operand src);
void bsrl(Register dst, Register src);
......
......@@ -1816,7 +1816,7 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
AppendToBuffer(",0x%x", (*current) & 7);
current += 1;
} else {
const char* mnemonic = "?";
const char* mnemonic;
if (opcode == 0x54) {
mnemonic = "andpd";
} else if (opcode == 0x56) {
......@@ -2166,7 +2166,6 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
} else if (opcode == 0xA2) {
// CPUID
AppendToBuffer("%s", mnemonic);
} else if ((opcode & 0xF0) == 0x40) {
// CMOVcc: conditional move.
int condition = opcode & 0x0F;
......@@ -2229,13 +2228,13 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
opcode == 0xB7 || opcode == 0xAF) {
// Size-extending moves, IMUL.
current += PrintOperands(mnemonic, REG_OPER_OP_ORDER, current);
} else if ((opcode & 0xF0) == 0x90) {
// SETcc: Set byte on condition. Needs pointer to beginning of instruction.
current = data + SetCC(data);
} else if (opcode == 0xAB || opcode == 0xA5 || opcode == 0xAD) {
// SHLD, SHRD (double-precision shift), BTS (bit set).
} else if (opcode == 0xA3 || opcode == 0xA5 || opcode == 0xAB ||
opcode == 0xAD) {
// BT (bit test), SHLD, BTS (bit test and set),
// SHRD (double-precision shift)
AppendToBuffer("%s ", mnemonic);
int mod, regop, rm;
get_modrm(*current, &mod, &regop, &rm);
......@@ -2245,6 +2244,14 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
} else {
AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
}
} else if (opcode == 0xBA) {
// BTS / BTR (bit test and set/reset) with immediate
int mod, regop, rm;
get_modrm(*current, &mod, &regop, &rm);
mnemonic = regop == 5 ? "bts" : regop == 6 ? "btr" : "?";
AppendToBuffer("%s ", mnemonic);
current += PrintRightOperand(current);
AppendToBuffer(",%d", *current++);
} else if (opcode == 0xB8 || opcode == 0xBC || opcode == 0xBD) {
// POPCNT, CTZ, CLZ.
AppendToBuffer("%s%c ", mnemonic, operand_size_code());
......@@ -2297,6 +2304,8 @@ const char* DisassemblerX64::TwoByteMnemonic(byte opcode) {
return (group_1_prefix_ == 0xF2) ? "maxsd" : "maxss";
case 0xA2:
return "cpuid";
case 0xA3:
return "bt";
case 0xA5:
return "shld";
case 0xAB:
......
......@@ -142,8 +142,11 @@ TEST(DisasmX64) {
__ shll_cl(Operand(rdi, rax, times_4, 100));
__ shll(rdx, Immediate(1));
__ shll(rdx, Immediate(6));
__ bts(Operand(rdx, 0), rcx);
__ bts(Operand(rbx, rcx, times_4, 0), rcx);
__ btq(Operand(rdx, 0), rcx);
__ btsq(Operand(rdx, 0), rcx);
__ btsq(Operand(rbx, rcx, times_4, 0), rcx);
__ btsq(rcx, Immediate(13));
__ btrq(rcx, Immediate(13));
__ nop();
__ pushq(Immediate(12));
__ pushq(Immediate(23456));
......@@ -267,7 +270,6 @@ TEST(DisasmX64) {
__ xorq(rdx, Immediate(12345));
__ xorq(rdx, Operand(rbx, rcx, times_8, 10000));
__ bts(Operand(rbx, rcx, times_8, 10000), rdx);
__ pshufw(xmm5, xmm1, 3);
__ hlt();
__ int3();
......
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