Commit bd4fb28e authored by ahaas's avatar ahaas Committed by Commit bot

[wasm] Int64Lowering of Word64Ror and Word64Rol.

R=titzer@chromium.org

Review URL: https://codereview.chromium.org/1843123002

Cr-Commit-Position: refs/heads/master@{#35173}
parent 5a09c0c0
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "src/compiler/graph.h" #include "src/compiler/graph.h"
#include "src/compiler/linkage.h" #include "src/compiler/linkage.h"
#include "src/compiler/machine-operator.h" #include "src/compiler/machine-operator.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h" #include "src/compiler/node-properties.h"
#include "src/compiler/node.h" #include "src/compiler/node.h"
...@@ -550,6 +551,114 @@ void Int64Lowering::LowerNode(Node* node) { ...@@ -550,6 +551,114 @@ void Int64Lowering::LowerNode(Node* node) {
ReplaceNode(node, low_node, high_node); ReplaceNode(node, low_node, high_node);
break; break;
} }
case IrOpcode::kWord64Ror: {
DCHECK(node->InputCount() == 2);
Node* input = node->InputAt(0);
Node* shift = HasReplacementLow(node->InputAt(1))
? GetReplacementLow(node->InputAt(1))
: node->InputAt(1);
Int32Matcher m(shift);
if (m.HasValue()) {
// Precondition: 0 <= shift < 64.
int32_t shift_value = m.Value() & 0x3f;
if (shift_value == 0) {
ReplaceNode(node, GetReplacementLow(input),
GetReplacementHigh(input));
} else if (shift_value == 32) {
ReplaceNode(node, GetReplacementHigh(input),
GetReplacementLow(input));
} else {
Node* low_input;
Node* high_input;
if (shift_value < 32) {
low_input = GetReplacementLow(input);
high_input = GetReplacementHigh(input);
} else {
low_input = GetReplacementHigh(input);
high_input = GetReplacementLow(input);
}
int32_t masked_shift_value = shift_value & 0x1f;
Node* masked_shift =
graph()->NewNode(common()->Int32Constant(masked_shift_value));
Node* inv_shift = graph()->NewNode(
common()->Int32Constant(32 - masked_shift_value));
Node* low_node = graph()->NewNode(
machine()->Word32Or(),
graph()->NewNode(machine()->Word32Shr(), low_input, masked_shift),
graph()->NewNode(machine()->Word32Shl(), high_input, inv_shift));
Node* high_node = graph()->NewNode(
machine()->Word32Or(), graph()->NewNode(machine()->Word32Shr(),
high_input, masked_shift),
graph()->NewNode(machine()->Word32Shl(), low_input, inv_shift));
ReplaceNode(node, low_node, high_node);
}
} else {
Node* safe_shift = shift;
if (!machine()->Word32ShiftIsSafe()) {
safe_shift =
graph()->NewNode(machine()->Word32And(), shift,
graph()->NewNode(common()->Int32Constant(0x1f)));
}
// By creating this bit-mask with SAR and SHL we do not have to deal
// with shift == 0 as a special case.
Node* inv_mask = graph()->NewNode(
machine()->Word32Shl(),
graph()->NewNode(machine()->Word32Sar(),
graph()->NewNode(common()->Int32Constant(
std::numeric_limits<int32_t>::min())),
safe_shift),
graph()->NewNode(common()->Int32Constant(1)));
Node* bit_mask =
graph()->NewNode(machine()->Word32Xor(), inv_mask,
graph()->NewNode(common()->Int32Constant(-1)));
// We have to mask the shift value for this comparison. If
// !machine()->Word32ShiftIsSafe() then the masking should already be
// part of the graph.
Node* masked_shift6 = shift;
if (machine()->Word32ShiftIsSafe()) {
masked_shift6 =
graph()->NewNode(machine()->Word32And(), shift,
graph()->NewNode(common()->Int32Constant(0x3f)));
}
Diamond lt32(
graph(), common(),
graph()->NewNode(machine()->Int32LessThan(), masked_shift6,
graph()->NewNode(common()->Int32Constant(32))));
// The low word and the high word can be swapped either at the input or
// at the output. We swap the inputs so that shift does not have to be
// kept for so long in a register.
Node* input_low =
lt32.Phi(MachineRepresentation::kWord32, GetReplacementLow(input),
GetReplacementHigh(input));
Node* input_high =
lt32.Phi(MachineRepresentation::kWord32, GetReplacementHigh(input),
GetReplacementLow(input));
Node* rotate_low =
graph()->NewNode(machine()->Word32Ror(), input_low, safe_shift);
Node* rotate_high =
graph()->NewNode(machine()->Word32Ror(), input_high, safe_shift);
Node* low_node = graph()->NewNode(
machine()->Word32Or(),
graph()->NewNode(machine()->Word32And(), rotate_low, bit_mask),
graph()->NewNode(machine()->Word32And(), rotate_high, inv_mask));
Node* high_node = graph()->NewNode(
machine()->Word32Or(),
graph()->NewNode(machine()->Word32And(), rotate_high, bit_mask),
graph()->NewNode(machine()->Word32And(), rotate_low, inv_mask));
ReplaceNode(node, low_node, high_node);
}
break;
}
// kExprI64Clz: // kExprI64Clz:
case IrOpcode::kWord64Clz: { case IrOpcode::kWord64Clz: {
DCHECK(node->InputCount() == 1); DCHECK(node->InputCount() == 1);
...@@ -624,7 +733,7 @@ void Int64Lowering::LowerNode(Node* node) { ...@@ -624,7 +733,7 @@ void Int64Lowering::LowerNode(Node* node) {
default: { DefaultLowering(node); } default: { DefaultLowering(node); }
} }
} } // NOLINT(readability/fn_size)
void Int64Lowering::LowerComparison(Node* node, const Operator* high_word_op, void Int64Lowering::LowerComparison(Node* node, const Operator* high_word_op,
const Operator* low_word_op) { const Operator* low_word_op) {
......
...@@ -549,19 +549,12 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, ...@@ -549,19 +549,12 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left,
op = m->Uint64LessThanOrEqual(); op = m->Uint64LessThanOrEqual();
std::swap(left, right); std::swap(left, right);
break; break;
#if WASM_64
// Opcodes only supported on 64-bit platforms.
// TODO(titzer): query the machine operator builder here instead of #ifdef.
case wasm::kExprI64Ror: case wasm::kExprI64Ror:
op = m->Word64Ror(); op = m->Word64Ror();
right = MaskShiftCount64(right); right = MaskShiftCount64(right);
break; break;
case wasm::kExprI64Rol: case wasm::kExprI64Rol:
right = MaskShiftCount64(right);
return BuildI64Rol(left, right); return BuildI64Rol(left, right);
#endif
case wasm::kExprF32CopySign: case wasm::kExprF32CopySign:
return BuildF32CopySign(left, right); return BuildF32CopySign(left, right);
case wasm::kExprF64CopySign: case wasm::kExprF64CopySign:
......
...@@ -274,7 +274,7 @@ class ValueHelper { ...@@ -274,7 +274,7 @@ class ValueHelper {
0x00003fffffffffff, 0x00001fffffffffff, 0x00000fffffffffff, 0x00003fffffffffff, 0x00001fffffffffff, 0x00000fffffffffff,
0x000007ffffffffff, 0x000003ffffffffff, 0x000001ffffffffff, 0x000007ffffffffff, 0x000003ffffffffff, 0x000001ffffffffff,
0x8000008000000000, 0x8000008000000001, 0x8000000000000400, 0x8000008000000000, 0x8000008000000001, 0x8000000000000400,
0x8000000000000401}; 0x8000000000000401, 0x0000000000000020};
return std::vector<uint64_t>(&kValues[0], &kValues[arraysize(kValues)]); return std::vector<uint64_t>(&kValues[0], &kValues[arraysize(kValues)]);
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "src/base/bits.h"
#include "src/wasm/wasm-macro-gen.h" #include "src/wasm/wasm-macro-gen.h"
#include "test/cctest/cctest.h" #include "test/cctest/cctest.h"
...@@ -85,8 +86,8 @@ ...@@ -85,8 +86,8 @@
V(F64UConvertI64, true) \ V(F64UConvertI64, true) \
V(F64ReinterpretI64, true) \ V(F64ReinterpretI64, true) \
V(I64ReinterpretF64, true) \ V(I64ReinterpretF64, true) \
V(I64Ror, false) \ V(I64Ror, true) \
V(I64Rol, false) V(I64Rol, true)
#define DECLARE_CONST(name, cond) static const bool kSupported_##name = cond; #define DECLARE_CONST(name, cond) static const bool kSupported_##name = cond;
FOREACH_I64_OPERATOR(DECLARE_CONST) FOREACH_I64_OPERATOR(DECLARE_CONST)
...@@ -317,11 +318,13 @@ TEST(Run_Wasm_I64Xor) { ...@@ -317,11 +318,13 @@ TEST(Run_Wasm_I64Xor) {
TEST(Run_Wasm_I64Shl) { TEST(Run_Wasm_I64Shl) {
REQUIRE(I64Shl); REQUIRE(I64Shl);
{ {
WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64()); WasmRunner<uint64_t> r(MachineType::Uint64(), MachineType::Uint64());
BUILD(r, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); BUILD(r, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) { FOR_UINT64_INPUTS(i) {
for (int64_t j = 1; j < 64; j++) { FOR_UINT64_INPUTS(j) {
CHECK_EQ(*i << j, r.Call(*i, j)); uint64_t expected = (*i) << (*j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j));
} }
} }
} }
...@@ -350,11 +353,13 @@ TEST(Run_Wasm_I64Shl) { ...@@ -350,11 +353,13 @@ TEST(Run_Wasm_I64Shl) {
TEST(Run_Wasm_I64ShrU) { TEST(Run_Wasm_I64ShrU) {
REQUIRE(I64ShrU); REQUIRE(I64ShrU);
{ {
WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64()); WasmRunner<uint64_t> r(MachineType::Uint64(), MachineType::Uint64());
BUILD(r, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); BUILD(r, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) { FOR_UINT64_INPUTS(i) {
for (int64_t j = 1; j < 64; j++) { FOR_UINT64_INPUTS(j) {
CHECK_EQ(*i >> j, r.Call(*i, j)); uint64_t expected = (*i) >> (*j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j));
} }
} }
} }
...@@ -385,9 +390,11 @@ TEST(Run_Wasm_I64ShrS) { ...@@ -385,9 +390,11 @@ TEST(Run_Wasm_I64ShrS) {
{ {
WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64()); WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64());
BUILD(r, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); BUILD(r, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) { FOR_INT64_INPUTS(i) {
for (int64_t j = 1; j < 64; j++) { FOR_INT64_INPUTS(j) {
CHECK_EQ(*i >> j, r.Call(*i, j)); int64_t expected = (*i) >> (*j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j));
} }
} }
} }
...@@ -1314,7 +1321,7 @@ TEST(Run_Wasm_I64Global) { ...@@ -1314,7 +1321,7 @@ TEST(Run_Wasm_I64Global) {
} }
} }
TEST(Run_WasmI64Eqz) { TEST(Run_Wasm_I64Eqz) {
REQUIRE(I64Eq); REQUIRE(I64Eq);
WasmRunner<int32_t> r(MachineType::Int64()); WasmRunner<int32_t> r(MachineType::Int64());
...@@ -1326,40 +1333,27 @@ TEST(Run_WasmI64Eqz) { ...@@ -1326,40 +1333,27 @@ TEST(Run_WasmI64Eqz) {
} }
} }
TEST(Run_WasmI64Shl) { TEST(Run_Wasm_I64Ror) {
REQUIRE(I64Shl); REQUIRE(I64Ror);
WasmRunner<uint64_t> r(MachineType::Uint64(), MachineType::Uint64()); WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64());
BUILD(r, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); BUILD(r, WASM_I64_ROR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) { FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) { FOR_UINT64_INPUTS(j) {
uint64_t expected = (*i) << (*j & 0x3f); int64_t expected = bits::RotateRight64(*i, *j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j)); CHECK_EQ(expected, r.Call(*i, *j));
} }
} }
} }
TEST(Run_WasmI64Shr) { TEST(Run_Wasm_I64Rol) {
REQUIRE(I64ShrU); REQUIRE(I64Rol);
WasmRunner<uint64_t> r(MachineType::Uint64(), MachineType::Uint64()); WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64());
BUILD(r, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))); BUILD(r, WASM_I64_ROL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) { FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) { FOR_UINT64_INPUTS(j) {
uint64_t expected = (*i) >> (*j & 0x3f); int64_t expected = bits::RotateRight64(*i, 64 - (*j & 0x3f));
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
TEST(Run_WasmI64Sar) {
REQUIRE(I64ShrS);
WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64());
BUILD(r, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
int64_t expected = (*i) >> (*j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j)); CHECK_EQ(expected, r.Call(*i, *j));
} }
} }
......
...@@ -660,6 +660,105 @@ TEST_F(Int64LoweringTest, I64Popcnt) { ...@@ -660,6 +660,105 @@ TEST_F(Int64LoweringTest, I64Popcnt) {
IsInt32Constant(0), start(), start())); IsInt32Constant(0), start(), start()));
} }
TEST_F(Int64LoweringTest, I64Ror) {
LowerGraph(graph()->NewNode(machine()->Word64Ror(), Int64Constant(value(0)),
Parameter(0)),
MachineRepresentation::kWord64, MachineRepresentation::kWord64, 1);
Matcher<Node*> branch_lt32_matcher =
IsBranch(IsInt32LessThan(IsParameter(0), IsInt32Constant(32)), start());
Matcher<Node*> low_input_matcher = IsPhi(
MachineRepresentation::kWord32, IsInt32Constant(low_word_value(0)),
IsInt32Constant(high_word_value(0)),
IsMerge(IsIfTrue(branch_lt32_matcher), IsIfFalse(branch_lt32_matcher)));
Matcher<Node*> high_input_matcher = IsPhi(
MachineRepresentation::kWord32, IsInt32Constant(high_word_value(0)),
IsInt32Constant(low_word_value(0)),
IsMerge(IsIfTrue(branch_lt32_matcher), IsIfFalse(branch_lt32_matcher)));
Matcher<Node*> shift_matcher =
IsWord32And(IsParameter(0), IsInt32Constant(0x1f));
Matcher<Node*> bit_mask_matcher = IsWord32Shl(
IsWord32Sar(IsInt32Constant(std::numeric_limits<int32_t>::min()),
shift_matcher),
IsInt32Constant(1));
Matcher<Node*> inv_mask_matcher =
IsWord32Xor(bit_mask_matcher, IsInt32Constant(-1));
EXPECT_THAT(
graph()->end()->InputAt(1),
IsReturn2(
IsWord32Or(IsWord32And(IsWord32Ror(low_input_matcher, shift_matcher),
inv_mask_matcher),
IsWord32And(IsWord32Ror(high_input_matcher, shift_matcher),
bit_mask_matcher)),
IsWord32Or(IsWord32And(IsWord32Ror(high_input_matcher, shift_matcher),
inv_mask_matcher),
IsWord32And(IsWord32Ror(low_input_matcher, shift_matcher),
bit_mask_matcher)),
start(), start()));
}
TEST_F(Int64LoweringTest, I64Ror_0) {
LowerGraph(graph()->NewNode(machine()->Word64Ror(), Int64Constant(value(0)),
Int32Constant(0)),
MachineRepresentation::kWord64);
EXPECT_THAT(graph()->end()->InputAt(1),
IsReturn2(IsInt32Constant(low_word_value(0)),
IsInt32Constant(high_word_value(0)), start(), start()));
}
TEST_F(Int64LoweringTest, I64Ror_32) {
LowerGraph(graph()->NewNode(machine()->Word64Ror(), Int64Constant(value(0)),
Int32Constant(32)),
MachineRepresentation::kWord64);
EXPECT_THAT(graph()->end()->InputAt(1),
IsReturn2(IsInt32Constant(high_word_value(0)),
IsInt32Constant(low_word_value(0)), start(), start()));
}
TEST_F(Int64LoweringTest, I64Ror_11) {
LowerGraph(graph()->NewNode(machine()->Word64Ror(), Int64Constant(value(0)),
Int32Constant(11)),
MachineRepresentation::kWord64);
EXPECT_THAT(
graph()->end()->InputAt(1),
IsReturn2(IsWord32Or(IsWord32Shr(IsInt32Constant(low_word_value(0)),
IsInt32Constant(11)),
IsWord32Shl(IsInt32Constant(high_word_value(0)),
IsInt32Constant(21))),
IsWord32Or(IsWord32Shr(IsInt32Constant(high_word_value(0)),
IsInt32Constant(11)),
IsWord32Shl(IsInt32Constant(low_word_value(0)),
IsInt32Constant(21))),
start(), start()));
}
TEST_F(Int64LoweringTest, I64Ror_43) {
LowerGraph(graph()->NewNode(machine()->Word64Ror(), Int64Constant(value(0)),
Int32Constant(43)),
MachineRepresentation::kWord64);
EXPECT_THAT(
graph()->end()->InputAt(1),
IsReturn2(IsWord32Or(IsWord32Shr(IsInt32Constant(high_word_value(0)),
IsInt32Constant(11)),
IsWord32Shl(IsInt32Constant(low_word_value(0)),
IsInt32Constant(21))),
IsWord32Or(IsWord32Shr(IsInt32Constant(low_word_value(0)),
IsInt32Constant(11)),
IsWord32Shl(IsInt32Constant(high_word_value(0)),
IsInt32Constant(21))),
start(), start()));
}
TEST_F(Int64LoweringTest, I64PhiWord64) { TEST_F(Int64LoweringTest, I64PhiWord64) {
LowerGraph(graph()->NewNode(common()->Phi(MachineRepresentation::kWord64, 2), LowerGraph(graph()->NewNode(common()->Phi(MachineRepresentation::kWord64, 2),
Int64Constant(value(0)), Int64Constant(value(1)), Int64Constant(value(0)), Int64Constant(value(1)),
......
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