[stubs] Introduce code stubs for bitwise binary operations.

Add BitwiseAndStub, BitwiseOrStub and BitwiseXorStub, and hook them up
with Ignition and TurboFan.

R=epertoso@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#35029}
This commit is contained in:
bmeurer 2016-03-23 06:44:32 -07:00 committed by Commit bot
parent d30d861a0c
commit db18219e4b
8 changed files with 269 additions and 6 deletions

View File

@ -218,6 +218,24 @@ Callable CodeFactory::Subtract(Isolate* isolate) {
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::BitwiseAnd(Isolate* isolate) {
BitwiseAndStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::BitwiseOr(Isolate* isolate) {
BitwiseOrStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::BitwiseXor(Isolate* isolate) {
BitwiseXorStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::LessThan(Isolate* isolate) {
LessThanStub stub(isolate);

View File

@ -81,6 +81,9 @@ class CodeFactory final {
static Callable Add(Isolate* isolate);
static Callable Subtract(Isolate* isolate);
static Callable BitwiseAnd(Isolate* isolate);
static Callable BitwiseOr(Isolate* isolate);
static Callable BitwiseXor(Isolate* isolate);
static Callable LessThan(Isolate* isolate);
static Callable LessThanOrEqual(Isolate* isolate);
static Callable GreaterThan(Isolate* isolate);

View File

@ -1035,6 +1035,182 @@ void SubtractStub::GenerateAssembly(
namespace {
void GenerateBitwiseOperation(
compiler::CodeStubAssembler* assembler,
compiler::Node* (compiler::CodeStubAssembler::*bitop)(compiler::Node*,
compiler::Node*)) {
typedef compiler::CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef compiler::CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(2);
// Shared entry for word32 bitwise operation.
Label do_bitop(assembler);
Variable var_bitop_lhs(assembler, MachineRepresentation::kWord32),
var_bitop_rhs(assembler, MachineRepresentation::kWord32);
// We might need to loop several times due to ToNumber conversions.
Variable var_lhs(assembler, MachineRepresentation::kTagged),
var_rhs(assembler, MachineRepresentation::kTagged);
Variable* loop_vars[2] = {&var_lhs, &var_rhs};
Label loop(assembler, 2, loop_vars);
var_lhs.Bind(assembler->Parameter(0));
var_rhs.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {lhs} and {rhs} values.
Node* lhs = var_lhs.value();
Node* rhs = var_rhs.value();
// Check if the {lhs} is a Smi or a HeapObject.
Label if_lhsissmi(assembler), if_lhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi);
assembler->Bind(&if_lhsissmi);
{
// Check if the {rhs} is also a Smi.
Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi,
&if_rhsisnotsmi);
assembler->Bind(&if_rhsissmi);
{
// Perform the word32 bitwise operation.
var_bitop_lhs.Bind(assembler->SmiToInt32(lhs));
var_bitop_rhs.Bind(assembler->SmiToInt32(rhs));
assembler->Goto(&do_bitop);
}
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of the {rhs}.
Node* rhs_map = assembler->LoadMap(rhs);
// Check if {rhs} is a HeapNumber.
Label if_rhsisnumber(assembler),
if_rhsisnotnumber(assembler, Label::kDeferred);
Node* number_map = assembler->HeapNumberMapConstant();
assembler->Branch(assembler->WordEqual(rhs_map, number_map),
&if_rhsisnumber, &if_rhsisnotnumber);
assembler->Bind(&if_rhsisnumber);
{
// Perform the word32 bitwise operation.
var_bitop_lhs.Bind(assembler->SmiToInt32(lhs));
var_bitop_rhs.Bind(assembler->TruncateHeapNumberValueToInt32(rhs));
assembler->Goto(&do_bitop);
}
assembler->Bind(&if_rhsisnotnumber);
{
// Convert the {rhs} to a Number first.
Callable callable =
CodeFactory::NonNumberToNumber(assembler->isolate());
var_rhs.Bind(assembler->CallStub(callable, context, rhs));
assembler->Goto(&loop);
}
}
}
assembler->Bind(&if_lhsisnotsmi);
{
// Load the map of the {lhs}.
Node* lhs_map = assembler->LoadMap(lhs);
// Check if the {lhs} is a HeapNumber.
Label if_lhsisnumber(assembler),
if_lhsisnotnumber(assembler, Label::kDeferred);
Node* number_map = assembler->HeapNumberMapConstant();
assembler->Branch(assembler->WordEqual(lhs_map, number_map),
&if_lhsisnumber, &if_lhsisnotnumber);
assembler->Bind(&if_lhsisnumber);
{
// Check if the {rhs} is a Smi.
Label if_rhsissmi(assembler), if_rhsisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(rhs), &if_rhsissmi,
&if_rhsisnotsmi);
assembler->Bind(&if_rhsissmi);
{
// Perform the word32 bitwise operation.
var_bitop_lhs.Bind(assembler->TruncateHeapNumberValueToInt32(lhs));
var_bitop_rhs.Bind(assembler->SmiToInt32(rhs));
assembler->Goto(&do_bitop);
}
assembler->Bind(&if_rhsisnotsmi);
{
// Load the map of the {rhs}.
Node* rhs_map = assembler->LoadMap(rhs);
// Check if the {rhs} is a HeapNumber.
Label if_rhsisnumber(assembler),
if_rhsisnotnumber(assembler, Label::kDeferred);
assembler->Branch(assembler->WordEqual(rhs_map, number_map),
&if_rhsisnumber, &if_rhsisnotnumber);
assembler->Bind(&if_rhsisnumber);
{
// Perform the word32 bitwise operation.
var_bitop_lhs.Bind(assembler->TruncateHeapNumberValueToInt32(lhs));
var_bitop_rhs.Bind(assembler->TruncateHeapNumberValueToInt32(rhs));
assembler->Goto(&do_bitop);
}
assembler->Bind(&if_rhsisnotnumber);
{
// Convert the {rhs} to a Number first.
Callable callable =
CodeFactory::NonNumberToNumber(assembler->isolate());
var_rhs.Bind(assembler->CallStub(callable, context, rhs));
assembler->Goto(&loop);
}
}
}
assembler->Bind(&if_lhsisnotnumber);
{
// Convert the {lhs} to a Number first.
Callable callable =
CodeFactory::NonNumberToNumber(assembler->isolate());
var_lhs.Bind(assembler->CallStub(callable, context, lhs));
assembler->Goto(&loop);
}
}
}
assembler->Bind(&do_bitop);
{
Node* lhs_value = var_bitop_lhs.value();
Node* rhs_value = var_bitop_rhs.value();
Node* value = (assembler->*bitop)(lhs_value, rhs_value);
Node* result = assembler->ChangeInt32ToTagged(value);
assembler->Return(result);
}
}
} // namespace
void BitwiseAndStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateBitwiseOperation(assembler, &compiler::CodeStubAssembler::Word32And);
}
void BitwiseOrStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateBitwiseOperation(assembler, &compiler::CodeStubAssembler::Word32Or);
}
void BitwiseXorStub::GenerateAssembly(
compiler::CodeStubAssembler* assembler) const {
GenerateBitwiseOperation(assembler, &compiler::CodeStubAssembler::Word32Xor);
}
namespace {
enum RelationalComparisonMode {
kLessThan,
kLessThanOrEqual,

View File

@ -113,6 +113,9 @@ namespace internal {
V(StringLength) \
V(Add) \
V(Subtract) \
V(BitwiseAnd) \
V(BitwiseOr) \
V(BitwiseXor) \
V(LessThan) \
V(LessThanOrEqual) \
V(GreaterThan) \
@ -685,6 +688,30 @@ class SubtractStub final : public TurboFanCodeStub {
DEFINE_TURBOFAN_CODE_STUB(Subtract, TurboFanCodeStub);
};
class BitwiseAndStub final : public TurboFanCodeStub {
public:
explicit BitwiseAndStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(BinaryOp);
DEFINE_TURBOFAN_CODE_STUB(BitwiseAnd, TurboFanCodeStub);
};
class BitwiseOrStub final : public TurboFanCodeStub {
public:
explicit BitwiseOrStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(BinaryOp);
DEFINE_TURBOFAN_CODE_STUB(BitwiseOr, TurboFanCodeStub);
};
class BitwiseXorStub final : public TurboFanCodeStub {
public:
explicit BitwiseXorStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
DEFINE_CALL_INTERFACE_DESCRIPTOR(BinaryOp);
DEFINE_TURBOFAN_CODE_STUB(BitwiseXor, TurboFanCodeStub);
};
class LessThanStub final : public TurboFanCodeStub {
public:
explicit LessThanStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}

View File

@ -259,6 +259,12 @@ Node* CodeStubAssembler::StoreHeapNumberValue(Node* object, Node* value) {
IntPtrConstant(HeapNumber::kValueOffset - kHeapObjectTag), value);
}
Node* CodeStubAssembler::TruncateHeapNumberValueToInt32(Node* object) {
Node* value = LoadHeapNumberValue(object);
return raw_assembler_->TruncateFloat64ToInt32(TruncationMode::kJavaScript,
value);
}
Node* CodeStubAssembler::LoadMapBitField(Node* map) {
return Load(MachineType::Uint8(), map,
IntPtrConstant(Map::kBitFieldOffset - kHeapObjectTag));
@ -504,6 +510,33 @@ Node* CodeStubAssembler::BitFieldDecode(Node* word32, uint32_t shift,
raw_assembler_->Int32Constant(shift));
}
Node* CodeStubAssembler::ChangeInt32ToTagged(Node* value) {
if (raw_assembler_->machine()->Is64()) {
return SmiTag(ChangeInt32ToInt64(value));
}
Variable var_result(this, MachineRepresentation::kTagged);
Node* pair = Int32AddWithOverflow(value, value);
Node* overflow = Projection(1, pair);
Label if_overflow(this, Label::kDeferred), if_notoverflow(this),
if_join(this);
Branch(overflow, &if_overflow, &if_notoverflow);
Bind(&if_overflow);
{
Node* value64 = ChangeInt32ToFloat64(value);
Node* result = AllocateHeapNumberWithValue(value64);
var_result.Bind(result);
}
Goto(&if_join);
Bind(&if_notoverflow);
{
Node* result = Projection(0, pair);
var_result.Bind(result);
}
Goto(&if_join);
Bind(&if_join);
return var_result.value();
}
void CodeStubAssembler::BranchIf(Node* condition, Label* if_true,
Label* if_false) {
Label if_condition_is_true(this), if_condition_is_false(this);

View File

@ -70,6 +70,7 @@ class Schedule;
V(IntPtrSub) \
V(IntPtrSubWithOverflow) \
V(Int32Add) \
V(Int32AddWithOverflow) \
V(Int32Sub) \
V(Int32Mul) \
V(WordOr) \
@ -294,6 +295,8 @@ class CodeStubAssembler {
Node* LoadHeapNumberValue(Node* object);
// Store the floating point value of a HeapNumber.
Node* StoreHeapNumberValue(Node* object, Node* value);
// Truncate the floating point value of a HeapNumber to an Int32.
Node* TruncateHeapNumberValueToInt32(Node* object);
// Load the bit field of a Map.
Node* LoadMapBitField(Node* map);
// Load the instance type of a Map.
@ -329,6 +332,9 @@ class CodeStubAssembler {
Node* BitFieldDecode(Node* word32, uint32_t shift, uint32_t mask);
// Conversions.
Node* ChangeInt32ToTagged(Node* value);
// Branching helpers.
// TODO(danno): Can we be more cleverish wrt. edge-split?
void BranchIf(Node* condition, Label* if_true, Label* if_false);

View File

@ -70,9 +70,6 @@ Reduction JSGenericLowering::Reduce(Node* node) {
ReplaceWithStubCall(node, CodeFactory::BinaryOpIC(isolate(), token), \
CallDescriptor::kPatchableCallSiteWithNop | flags); \
}
REPLACE_BINARY_OP_IC_CALL(JSBitwiseOr, Token::BIT_OR)
REPLACE_BINARY_OP_IC_CALL(JSBitwiseXor, Token::BIT_XOR)
REPLACE_BINARY_OP_IC_CALL(JSBitwiseAnd, Token::BIT_AND)
REPLACE_BINARY_OP_IC_CALL(JSShiftLeft, Token::SHL)
REPLACE_BINARY_OP_IC_CALL(JSShiftRight, Token::SAR)
REPLACE_BINARY_OP_IC_CALL(JSShiftRightLogical, Token::SHR)
@ -98,6 +95,9 @@ REPLACE_RUNTIME_CALL(JSConvertReceiver, Runtime::kConvertReceiver)
}
REPLACE_STUB_CALL(Add)
REPLACE_STUB_CALL(Subtract)
REPLACE_STUB_CALL(BitwiseAnd)
REPLACE_STUB_CALL(BitwiseOr)
REPLACE_STUB_CALL(BitwiseXor)
REPLACE_STUB_CALL(LessThan)
REPLACE_STUB_CALL(LessThanOrEqual)
REPLACE_STUB_CALL(GreaterThan)

View File

@ -690,7 +690,7 @@ void Interpreter::DoMod(InterpreterAssembler* assembler) {
//
// BitwiseOr register <src> to accumulator.
void Interpreter::DoBitwiseOr(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kBitwiseOr, assembler);
DoBinaryOp(CodeFactory::BitwiseOr(isolate_), assembler);
}
@ -698,7 +698,7 @@ void Interpreter::DoBitwiseOr(InterpreterAssembler* assembler) {
//
// BitwiseXor register <src> to accumulator.
void Interpreter::DoBitwiseXor(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kBitwiseXor, assembler);
DoBinaryOp(CodeFactory::BitwiseXor(isolate_), assembler);
}
@ -706,7 +706,7 @@ void Interpreter::DoBitwiseXor(InterpreterAssembler* assembler) {
//
// BitwiseAnd register <src> to accumulator.
void Interpreter::DoBitwiseAnd(InterpreterAssembler* assembler) {
DoBinaryOp(Runtime::kBitwiseAnd, assembler);
DoBinaryOp(CodeFactory::BitwiseAnd(isolate_), assembler);
}