blob: ae5f944fe257082673d3e897a62da1d6c3f236d8 [file] [log] [blame] [edit]
// 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