| // Copyright 2019 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/compiler/decompression-optimizer.h" |
| |
| #include "test/unittests/compiler/graph-unittest.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| class DecompressionOptimizerTest : public GraphTest { |
| public: |
| DecompressionOptimizerTest() |
| : GraphTest(), |
| machine_(zone(), MachineType::PointerRepresentation(), |
| MachineOperatorBuilder::kNoFlags) {} |
| ~DecompressionOptimizerTest() override = default; |
| |
| protected: |
| void Reduce() { |
| DecompressionOptimizer decompression_optimizer(zone(), graph(), common(), |
| machine()); |
| decompression_optimizer.Reduce(); |
| } |
| |
| MachineRepresentation CompressedMachRep(MachineRepresentation mach_rep) { |
| if (mach_rep == MachineRepresentation::kTagged) { |
| return MachineRepresentation::kCompressed; |
| } else { |
| DCHECK_EQ(mach_rep, MachineRepresentation::kTaggedPointer); |
| return MachineRepresentation::kCompressedPointer; |
| } |
| } |
| |
| MachineRepresentation CompressedMachRep(MachineType type) { |
| return CompressedMachRep(type.representation()); |
| } |
| |
| MachineRepresentation LoadMachRep(Node* node) { |
| return LoadRepresentationOf(node->op()).representation(); |
| } |
| StoreRepresentation CreateStoreRep(MachineType type) { |
| return StoreRepresentation(type.representation(), |
| WriteBarrierKind::kFullWriteBarrier); |
| } |
| |
| const MachineType types[2] = {MachineType::AnyTagged(), |
| MachineType::TaggedPointer()}; |
| |
| const Handle<HeapNumber> heap_constants[15] = { |
| factory()->NewHeapNumber(0.0), |
| factory()->NewHeapNumber(-0.0), |
| factory()->NewHeapNumber(11.2), |
| factory()->NewHeapNumber(-11.2), |
| factory()->NewHeapNumber(3.1415 + 1.4142), |
| factory()->NewHeapNumber(3.1415 - 1.4142), |
| factory()->NewHeapNumber(0x0000000000000000), |
| factory()->NewHeapNumber(0x0000000000000001), |
| factory()->NewHeapNumber(0x0000FFFFFFFF0000), |
| factory()->NewHeapNumber(0x7FFFFFFFFFFFFFFF), |
| factory()->NewHeapNumber(0x8000000000000000), |
| factory()->NewHeapNumber(0x8000000000000001), |
| factory()->NewHeapNumber(0x8000FFFFFFFF0000), |
| factory()->NewHeapNumber(0x8FFFFFFFFFFFFFFF), |
| factory()->NewHeapNumber(0xFFFFFFFFFFFFFFFF)}; |
| |
| MachineOperatorBuilder* machine() { return &machine_; } |
| |
| private: |
| MachineOperatorBuilder machine_; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // Direct Load into Store. |
| |
| TEST_F(DecompressionOptimizerTest, DirectLoadStore) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test for both AnyTagged and TaggedPointer. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| // Create the graph. |
| Node* base_pointer = graph()->NewNode(machine()->Load(types[i]), object, |
| index, effect, control); |
| Node* value = graph()->NewNode(machine()->Load(types[i]), base_pointer, |
| index, effect, control); |
| graph()->SetEnd(graph()->NewNode(machine()->Store(CreateStoreRep(types[i])), |
| object, index, value, effect, control)); |
| |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(base_pointer), types[i].representation()); |
| EXPECT_EQ(LoadMachRep(value), CompressedMachRep(types[i])); |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // Word32 Operations. |
| |
| TEST_F(DecompressionOptimizerTest, Word32EqualTwoDecompresses) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test for both AnyTagged and TaggedPointer, for both loads. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| for (size_t j = 0; j < arraysize(types); ++j) { |
| // Create the graph. |
| Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* load_2 = graph()->NewNode(machine()->Load(types[j]), object, index, |
| effect, control); |
| Node* equal = graph()->NewNode(machine()->Word32Equal(), load_1, load_2); |
| graph()->SetEnd(equal); |
| |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load_1), CompressedMachRep(types[i])); |
| EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[j])); |
| } |
| } |
| } |
| |
| TEST_F(DecompressionOptimizerTest, Word32EqualDecompressAndConstant) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test for both AnyTagged and TaggedPointer. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| for (size_t j = 0; j < arraysize(heap_constants); ++j) { |
| // Create the graph. |
| Node* load = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* constant = |
| graph()->NewNode(common()->HeapConstant(heap_constants[j])); |
| Node* equal = graph()->NewNode(machine()->Word32Equal(), load, constant); |
| graph()->SetEnd(equal); |
| |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load), CompressedMachRep(types[i])); |
| EXPECT_EQ(constant->opcode(), IrOpcode::kCompressedHeapConstant); |
| } |
| } |
| } |
| |
| TEST_F(DecompressionOptimizerTest, Word32AndSmiCheck) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test for both AnyTagged and TaggedPointer. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| // Create the graph. |
| Node* load = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* smi_tag_mask = graph()->NewNode(common()->Int32Constant(kSmiTagMask)); |
| Node* word32_and = |
| graph()->NewNode(machine()->Word32And(), load, smi_tag_mask); |
| Node* smi_tag = graph()->NewNode(common()->Int32Constant(kSmiTag)); |
| graph()->SetEnd( |
| graph()->NewNode(machine()->Word32Equal(), word32_and, smi_tag)); |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load), CompressedMachRep(types[i])); |
| } |
| } |
| |
| TEST_F(DecompressionOptimizerTest, Word32ShlSmiTag) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test only for AnyTagged, since TaggedPointer can't be Smi tagged. |
| // Create the graph. |
| Node* load = graph()->NewNode(machine()->Load(MachineType::AnyTagged()), |
| object, index, effect, control); |
| Node* smi_shift_bits = |
| graph()->NewNode(common()->Int32Constant(kSmiShiftSize + kSmiTagSize)); |
| Node* word32_shl = |
| graph()->NewNode(machine()->Word32Shl(), load, smi_shift_bits); |
| graph()->SetEnd( |
| graph()->NewNode(machine()->BitcastWord32ToWord64(), word32_shl)); |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load), CompressedMachRep(MachineType::AnyTagged())); |
| } |
| |
| TEST_F(DecompressionOptimizerTest, Word32SarSmiUntag) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test only for AnyTagged, since TaggedPointer can't be Smi tagged. |
| // Create the graph. |
| Node* load = graph()->NewNode(machine()->Load(MachineType::AnyTagged()), |
| object, index, effect, control); |
| Node* truncation = graph()->NewNode(machine()->TruncateInt64ToInt32(), load); |
| Node* smi_shift_bits = |
| graph()->NewNode(common()->Int32Constant(kSmiShiftSize + kSmiTagSize)); |
| Node* word32_sar = |
| graph()->NewNode(machine()->Word32Sar(), truncation, smi_shift_bits); |
| graph()->SetEnd( |
| graph()->NewNode(machine()->ChangeInt32ToInt64(), word32_sar)); |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load), CompressedMachRep(MachineType::AnyTagged())); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // FrameState and TypedStateValues interaction. |
| |
| TEST_F(DecompressionOptimizerTest, TypedStateValues) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| const int number_of_inputs = 2; |
| const ZoneVector<MachineType>* types_for_state_values = |
| graph()->zone()->New<ZoneVector<MachineType>>(number_of_inputs, |
| graph()->zone()); |
| SparseInputMask dense = SparseInputMask::Dense(); |
| |
| // Test for both AnyTagged and TaggedPointer. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| for (size_t j = 0; j < arraysize(heap_constants); ++j) { |
| // Create the graph. |
| Node* load = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* constant_1 = |
| graph()->NewNode(common()->HeapConstant(heap_constants[j])); |
| Node* typed_state_values = graph()->NewNode( |
| common()->TypedStateValues(types_for_state_values, dense), load, |
| constant_1); |
| Node* constant_2 = |
| graph()->NewNode(common()->HeapConstant(heap_constants[j])); |
| graph()->SetEnd(graph()->NewNode( |
| common()->FrameState(BailoutId::None(), |
| OutputFrameStateCombine::Ignore(), nullptr), |
| typed_state_values, typed_state_values, typed_state_values, |
| constant_2, UndefinedConstant(), graph()->start())); |
| |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load), CompressedMachRep(types[i])); |
| EXPECT_EQ(constant_1->opcode(), IrOpcode::kCompressedHeapConstant); |
| EXPECT_EQ(constant_2->opcode(), IrOpcode::kCompressedHeapConstant); |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // Phi |
| |
| TEST_F(DecompressionOptimizerTest, PhiDecompressOrNot) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| const int number_of_inputs = 2; |
| |
| // Test for both AnyTagged and TaggedPointer. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| for (size_t j = 0; j < arraysize(heap_constants); ++j) { |
| // Create the graph. |
| // Base pointer |
| Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* constant_1 = |
| graph()->NewNode(common()->HeapConstant(heap_constants[j])); |
| Node* phi_1 = graph()->NewNode( |
| common()->Phi(types[i].representation(), number_of_inputs), load_1, |
| constant_1, control); |
| |
| // Value |
| Node* load_2 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* constant_2 = |
| graph()->NewNode(common()->HeapConstant(heap_constants[j])); |
| Node* phi_2 = graph()->NewNode( |
| common()->Phi(types[i].representation(), number_of_inputs), load_2, |
| constant_2, control); |
| |
| graph()->SetEnd( |
| graph()->NewNode(machine()->Store(CreateStoreRep(types[i])), phi_1, |
| index, phi_2, effect, control)); |
| |
| // Change the nodes, and test the change. |
| Reduce(); |
| // Base pointer should not be compressed. |
| EXPECT_EQ(LoadMachRep(load_1), types[i].representation()); |
| EXPECT_EQ(constant_1->opcode(), IrOpcode::kHeapConstant); |
| EXPECT_EQ(PhiRepresentationOf(phi_1->op()), types[i].representation()); |
| // Value should be compressed. |
| EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[i])); |
| EXPECT_EQ(constant_2->opcode(), IrOpcode::kCompressedHeapConstant); |
| EXPECT_EQ(PhiRepresentationOf(phi_2->op()), CompressedMachRep(types[i])); |
| } |
| } |
| } |
| |
| TEST_F(DecompressionOptimizerTest, CascadingPhi) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| const int number_of_inputs = 2; |
| |
| // Test for both AnyTagged and TaggedPointer. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| // Create the graph. |
| Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* load_2 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* load_3 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* load_4 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| |
| Node* phi_1 = graph()->NewNode( |
| common()->Phi(types[i].representation(), number_of_inputs), load_1, |
| load_2, control); |
| Node* phi_2 = graph()->NewNode( |
| common()->Phi(types[i].representation(), number_of_inputs), load_3, |
| load_4, control); |
| |
| Node* final_phi = graph()->NewNode( |
| common()->Phi(types[i].representation(), number_of_inputs), phi_1, |
| phi_2, control); |
| |
| // Value |
| graph()->SetEnd(final_phi); |
| // Change the nodes, and test the change. |
| Reduce(); |
| // Loads are all compressed |
| EXPECT_EQ(LoadMachRep(load_1), CompressedMachRep(types[i])); |
| EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[i])); |
| EXPECT_EQ(LoadMachRep(load_3), CompressedMachRep(types[i])); |
| EXPECT_EQ(LoadMachRep(load_4), CompressedMachRep(types[i])); |
| // Phis too |
| EXPECT_EQ(PhiRepresentationOf(phi_1->op()), CompressedMachRep(types[i])); |
| EXPECT_EQ(PhiRepresentationOf(phi_2->op()), CompressedMachRep(types[i])); |
| EXPECT_EQ(PhiRepresentationOf(final_phi->op()), |
| CompressedMachRep(types[i])); |
| } |
| } |
| |
| TEST_F(DecompressionOptimizerTest, PhiWithOneCompressedAndOneTagged) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| const int number_of_inputs = 2; |
| |
| // Test for both AnyTagged and TaggedPointer. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| for (size_t j = 0; j < arraysize(heap_constants); ++j) { |
| // Create the graph. |
| // Base pointer in load_2, and phi input for value |
| Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| |
| // load_2 blocks load_1 from being compressed. |
| Node* load_2 = graph()->NewNode(machine()->Load(types[i]), load_1, index, |
| effect, control); |
| Node* phi = graph()->NewNode( |
| common()->Phi(types[i].representation(), number_of_inputs), load_1, |
| load_2, control); |
| |
| graph()->SetEnd( |
| graph()->NewNode(machine()->Store(CreateStoreRep(types[i])), object, |
| index, phi, effect, control)); |
| |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load_1), types[i].representation()); |
| EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[i])); |
| EXPECT_EQ(PhiRepresentationOf(phi->op()), CompressedMachRep(types[i])); |
| } |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // Int cases. |
| |
| TEST_F(DecompressionOptimizerTest, Int32LessThanOrEqualFromSpeculative) { |
| // This case tests for what SpeculativeNumberLessThanOrEqual is lowered to. |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test only for AnyTagged, since TaggedPointer can't be a Smi. |
| // Create the graph. |
| Node* load = graph()->NewNode(machine()->Load(MachineType::AnyTagged()), |
| object, index, effect, control); |
| Node* constant = graph()->NewNode(common()->Int64Constant(5)); |
| graph()->SetEnd( |
| graph()->NewNode(machine()->Int32LessThanOrEqual(), load, constant)); |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load), CompressedMachRep(MachineType::AnyTagged())); |
| } |
| |
| // ----------------------------------------------------------------------------- |
| // Bitcast cases. |
| |
| TEST_F(DecompressionOptimizerTest, BitcastTaggedToWord) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test for both AnyTagged and TaggedPointer, for both loads. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| for (size_t j = 0; j < arraysize(types); ++j) { |
| // Create the graph. |
| Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* bitcast_1 = graph()->NewNode(machine()->BitcastTaggedToWord(), |
| load_1, effect, control); |
| Node* load_2 = graph()->NewNode(machine()->Load(types[j]), object, index, |
| effect, control); |
| Node* bitcast_2 = graph()->NewNode(machine()->BitcastTaggedToWord(), |
| load_2, effect, control); |
| Node* equal = |
| graph()->NewNode(machine()->Word32Equal(), bitcast_1, bitcast_2); |
| graph()->SetEnd(equal); |
| |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load_1), CompressedMachRep(types[i])); |
| EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[j])); |
| } |
| } |
| } |
| |
| TEST_F(DecompressionOptimizerTest, BitcastTaggedToWordForTagAndSmiBits) { |
| // Define variables. |
| Node* const control = graph()->start(); |
| Node* object = Parameter(Type::Any(), 0); |
| Node* effect = graph()->start(); |
| Node* index = Parameter(Type::UnsignedSmall(), 1); |
| |
| // Test for both AnyTagged and TaggedPointer, for both loads. |
| for (size_t i = 0; i < arraysize(types); ++i) { |
| for (size_t j = 0; j < arraysize(types); ++j) { |
| // Create the graph. |
| Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index, |
| effect, control); |
| Node* bitcast_1 = graph()->NewNode( |
| machine()->BitcastTaggedToWordForTagAndSmiBits(), load_1); |
| Node* load_2 = graph()->NewNode(machine()->Load(types[j]), object, index, |
| effect, control); |
| Node* bitcast_2 = graph()->NewNode( |
| machine()->BitcastTaggedToWordForTagAndSmiBits(), load_2); |
| Node* equal = |
| graph()->NewNode(machine()->Word32Equal(), bitcast_1, bitcast_2); |
| graph()->SetEnd(equal); |
| |
| // Change the nodes, and test the change. |
| Reduce(); |
| EXPECT_EQ(LoadMachRep(load_1), CompressedMachRep(types[i])); |
| EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[j])); |
| } |
| } |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |