diff --git a/src/code-factory.cc b/src/code-factory.cc index 1553e08da6..df3b1dd503 100644 --- a/src/code-factory.cc +++ b/src/code-factory.cc @@ -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); diff --git a/src/code-factory.h b/src/code-factory.h index 67e9a37bc9..3dea75147a 100644 --- a/src/code-factory.h +++ b/src/code-factory.h @@ -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); diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 2646787e3b..67438e5ac2 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -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, diff --git a/src/code-stubs.h b/src/code-stubs.h index 66d409abbf..54602be6ec 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -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) {} diff --git a/src/compiler/code-stub-assembler.cc b/src/compiler/code-stub-assembler.cc index 0d9168c605..f0ee4e58b9 100644 --- a/src/compiler/code-stub-assembler.cc +++ b/src/compiler/code-stub-assembler.cc @@ -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); diff --git a/src/compiler/code-stub-assembler.h b/src/compiler/code-stub-assembler.h index 43014196b7..288f53a18d 100644 --- a/src/compiler/code-stub-assembler.h +++ b/src/compiler/code-stub-assembler.h @@ -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); diff --git a/src/compiler/js-generic-lowering.cc b/src/compiler/js-generic-lowering.cc index 1ee8de57c7..98b915c11d 100644 --- a/src/compiler/js-generic-lowering.cc +++ b/src/compiler/js-generic-lowering.cc @@ -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) diff --git a/src/interpreter/interpreter.cc b/src/interpreter/interpreter.cc index 1c98dc7eb5..1dbae348b9 100644 --- a/src/interpreter/interpreter.cc +++ b/src/interpreter/interpreter.cc @@ -690,7 +690,7 @@ void Interpreter::DoMod(InterpreterAssembler* assembler) { // // BitwiseOr register 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 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 to accumulator. void Interpreter::DoBitwiseAnd(InterpreterAssembler* assembler) { - DoBinaryOp(Runtime::kBitwiseAnd, assembler); + DoBinaryOp(CodeFactory::BitwiseAnd(isolate_), assembler); }