| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "vm/compiler/backend/linearscan.h" | |
| #include <utility> | |
| #include "vm/compiler/backend/block_builder.h" | |
| #include "vm/compiler/backend/il_printer.h" | |
| #include "vm/compiler/backend/il_test_helper.h" | |
| #include "vm/unit_test.h" | |
| #include "vm/zone_text_buffer.h" | |
| namespace dart { | |
| class DummyDef : public Definition { | |
| public: | |
| explicit DummyDef( | |
| Zone* zone, | |
| std::initializer_list<std::pair<Definition*, Location>> inputs, | |
| Location output, | |
| LocationSummary::ContainsCall contains_call = LocationSummary::kNoCall) | |
| : inputs_(inputs.size()), | |
| summary_(new LocationSummary(zone, | |
| inputs.size(), | |
| /*temp_count=*/0, | |
| contains_call)) { | |
| intptr_t index = 0; | |
| for (auto [defn, loc] : inputs) { | |
| auto v = new Value(defn); | |
| summary_->set_in(index, loc); | |
| v->set_use_index(index); | |
| v->set_instruction(this); | |
| inputs_.Add(v); | |
| index++; | |
| } | |
| summary_->set_out(0, output); | |
| } | |
| LocationSummary* MakeLocationSummary(Zone* zone, bool opt) const { | |
| return summary_; | |
| } | |
| virtual void Accept(InstructionVisitor* visitor) { UNREACHABLE(); } | |
| virtual Tag tag() const { return Instruction::kRedefinition; } | |
| virtual const char* DebugName() const { return "DummyDef"; } | |
| virtual intptr_t InputCount() const { return inputs_.length(); } | |
| virtual Value* InputAt(intptr_t i) const { return inputs_[i]; } | |
| virtual bool MayThrow() const { return false; } | |
| virtual bool ComputeCanDeoptimize() const { return false; } | |
| virtual bool HasUnknownSideEffects() const { return false; } | |
| private: | |
| virtual void RawSetInputAt(intptr_t i, Value* value) { inputs_[i] = value; } | |
| GrowableArray<Value*> inputs_; | |
| LocationSummary* const summary_; | |
| DISALLOW_COPY_AND_ASSIGN(DummyDef); | |
| }; | |
| ISOLATE_UNIT_TEST_CASE(LinearScan_TestSameAsFirstOrSecondFlip) { | |
| using compiler::BlockBuilder; | |
| CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true); | |
| FlowGraphBuilderHelper H; | |
| auto zone = H.flow_graph()->zone(); | |
| auto b1 = H.flow_graph()->graph_entry()->normal_entry(); | |
| DummyDef* lhs; | |
| DummyDef* rhs; | |
| DummyDef* binop; | |
| { | |
| BlockBuilder builder(H.flow_graph(), b1); | |
| lhs = builder.AddDefinition( | |
| new DummyDef(zone, {}, Location::RequiresRegister())); | |
| rhs = builder.AddDefinition( | |
| new DummyDef(zone, {}, Location::RequiresRegister())); | |
| binop = builder.AddDefinition( | |
| new DummyDef(zone, | |
| {{lhs, Location::RequiresRegister()}, | |
| {rhs, Location::RequiresRegister()}}, | |
| Location::SameAsFirstOrSecondInput())); | |
| // Left hand side of the binary operation is still needed after it. | |
| builder.AddInstruction( | |
| new DummyDef(zone, {{lhs, Location::RequiresRegister()}}, Location())); | |
| builder.AddInstruction(new DartReturnInstr( | |
| InstructionSource(), new Value(binop), S.GetNextDeoptId())); | |
| } | |
| H.FinishGraph(); | |
| FlowGraphPrinter::PrintGraph("before regalloc", H.flow_graph()); | |
| H.flow_graph()->InsertMoveArguments(); | |
| // Ensure loop hierarchy has been computed. | |
| H.flow_graph()->GetLoopHierarchy(); | |
| // Perform register allocation on the SSA graph. | |
| FlowGraphAllocator allocator(*H.flow_graph()); | |
| allocator.AllocateRegisters(); | |
| // There should be no parallel move between binop and rhs and inputs | |
| // to binop should be flipped. | |
| EXPECT_PROPERTY(binop->previous(), | |
| &it == rhs || (it.IsParallelMove() && | |
| it.AsParallelMove()->IsRedundant() && | |
| it.previous() == rhs)); | |
| EXPECT_PROPERTY(binop->InputAt(0)->definition(), &it == rhs); | |
| EXPECT_PROPERTY(binop->InputAt(1)->definition(), &it == lhs); | |
| } | |
| ISOLATE_UNIT_TEST_CASE(LinearScan_TestSameAsFirstOrSecondNoFlip) { | |
| using compiler::BlockBuilder; | |
| CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true); | |
| FlowGraphBuilderHelper H; | |
| auto zone = H.flow_graph()->zone(); | |
| auto b1 = H.flow_graph()->graph_entry()->normal_entry(); | |
| DummyDef* lhs; | |
| DummyDef* rhs; | |
| DummyDef* binop; | |
| { | |
| BlockBuilder builder(H.flow_graph(), b1); | |
| lhs = builder.AddDefinition( | |
| new DummyDef(zone, {}, Location::RequiresRegister())); | |
| rhs = builder.AddDefinition( | |
| new DummyDef(zone, {}, Location::RequiresRegister())); | |
| binop = builder.AddDefinition( | |
| new DummyDef(zone, | |
| {{lhs, Location::RequiresRegister()}, | |
| {rhs, Location::RequiresRegister()}}, | |
| Location::SameAsFirstOrSecondInput())); | |
| // Right hand side of the binary operation is still needed after it. | |
| builder.AddInstruction( | |
| new DummyDef(zone, {{rhs, Location::RequiresRegister()}}, Location())); | |
| builder.AddInstruction(new DartReturnInstr( | |
| InstructionSource(), new Value(binop), S.GetNextDeoptId())); | |
| } | |
| H.FinishGraph(); | |
| H.flow_graph()->InsertMoveArguments(); | |
| // Ensure loop hierarchy has been computed. | |
| H.flow_graph()->GetLoopHierarchy(); | |
| // Perform register allocation on the SSA graph. | |
| FlowGraphAllocator allocator(*H.flow_graph()); | |
| allocator.AllocateRegisters(); | |
| // There should be no parallel move between binop and rhs and inputs | |
| // to binop should *not* be flipped. | |
| EXPECT_PROPERTY(binop->previous(), | |
| &it == rhs || (it.IsParallelMove() && | |
| it.AsParallelMove()->IsRedundant() && | |
| it.previous() == rhs)); | |
| EXPECT_PROPERTY(binop->InputAt(0)->definition(), &it == lhs); | |
| EXPECT_PROPERTY(binop->InputAt(1)->definition(), &it == rhs); | |
| } | |
| ISOLATE_UNIT_TEST_CASE(LinearScan_TestSameAsFirstOrSecondNoFlip2) { | |
| using compiler::BlockBuilder; | |
| CompilerState S(thread, /*is_aot=*/false, /*is_optimizing=*/true); | |
| FlowGraphBuilderHelper H; | |
| auto zone = H.flow_graph()->zone(); | |
| auto b1 = H.flow_graph()->graph_entry()->normal_entry(); | |
| DummyDef* lhs; | |
| DummyDef* rhs; | |
| DummyDef* binop; | |
| { | |
| BlockBuilder builder(H.flow_graph(), b1); | |
| lhs = builder.AddDefinition( | |
| new DummyDef(zone, {}, Location::RequiresRegister())); | |
| rhs = builder.AddDefinition( | |
| new DummyDef(zone, {}, Location::RequiresRegister())); | |
| binop = builder.AddDefinition( | |
| new DummyDef(zone, | |
| {{lhs, Location::RequiresRegister()}, | |
| {rhs, Location::RequiresRegister()}}, | |
| Location::SameAsFirstOrSecondInput())); | |
| // Both right and left hand sides of the binary operation are still needed | |
| // after it. | |
| builder.AddInstruction( | |
| new DummyDef(zone, {{rhs, Location::RequiresRegister()}}, Location())); | |
| builder.AddInstruction( | |
| new DummyDef(zone, {{lhs, Location::RequiresRegister()}}, Location())); | |
| builder.AddInstruction(new DartReturnInstr( | |
| InstructionSource(), new Value(binop), S.GetNextDeoptId())); | |
| } | |
| H.FinishGraph(); | |
| H.flow_graph()->InsertMoveArguments(); | |
| // Ensure loop hierarchy has been computed. | |
| H.flow_graph()->GetLoopHierarchy(); | |
| // Perform register allocation on the SSA graph. | |
| FlowGraphAllocator allocator(*H.flow_graph()); | |
| allocator.AllocateRegisters(); | |
| // There should be a parallel move between binop and rhs and inputs | |
| // to binop should *not* be flipped. | |
| EXPECT_PROPERTY(binop->previous(), it.IsParallelMove()); | |
| EXPECT_PROPERTY(binop->InputAt(0)->definition(), &it == lhs); | |
| EXPECT_PROPERTY(binop->InputAt(1)->definition(), &it == rhs); | |
| } | |
| } // namespace dart |