blob: 71a83af48ec3c8b0a0f97cd70a651aa43030635e [file]
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "instruction_simplifier_arm.h"
#include <gtest/gtest.h>
#include "base/globals.h"
#include "optimizing/nodes.h"
#include "optimizing_unit_test.h"
namespace art HIDDEN {
namespace arm {
class InstructionSimplifierArmTest : public OptimizingUnitTest {};
// Regression test for a bug in simplification of `Sub(., Sub(Shl(...), .))` to enable shifter
// operand merging. The code wrongly assumed that the `Shl` can have only one non-environment
// use. It is the inner `Sub` that can have only one non-environment use.
TEST_F(InstructionSimplifierArmTest, SubRightSubLeftShlRegressionTest) {
HBasicBlock* return_block = InitEntryMainExitGraph();
HInstruction* param0 = MakeParam(DataType::Type::kInt32);
HInstruction* param1 = MakeParam(DataType::Type::kInt32);
HInstruction* param2 = MakeParam(DataType::Type::kInt32);
HInstruction* param3 = MakeParam(DataType::Type::kInt32);
HInstruction* param4 = MakeParam(DataType::Type::kInt32);
HInstruction* param5 = MakeParam(DataType::Type::kInt32);
HInstruction* param6 = MakeParam(DataType::Type::kInt32);
HInstruction* bool_param = MakeParam(DataType::Type::kBool);
HInstruction* const1 = graph_->GetIntConstant(1);
auto [start, left, right] = CreateDiamondPattern(return_block, bool_param);
// When visiting the `sub2`, we exchange the `sub1` operands and replace `sub2` with an `Add`.
// We used to crash if `shl` had more than one use (the `Sub` with exchanged operands) but we
// fixed this and we test with additional uses both before and after the replaced `sub2`,
// inside and outside the block. The `shl` is then merged as a shifter operand to all its uses.
HShl* shl = MakeBinOp<HShl>(start, DataType::Type::kInt32, param0, const1);
HAdd* add1 = MakeBinOp<HAdd>(start, DataType::Type::kInt32, param1, shl);
HAdd* add2 = MakeBinOp<HAdd>(right, DataType::Type::kInt32, param2, shl);
HSub* sub1 = MakeBinOp<HSub>(right, DataType::Type::kInt32, shl, param3);
HSub* sub2 = MakeBinOp<HSub>(right, DataType::Type::kInt32, param4, sub1);
HAdd* add3 = MakeBinOp<HAdd>(right, DataType::Type::kInt32, param5, shl);
HOr* or1 = MakeBinOp<HOr>(right, DataType::Type::kInt32, add2, sub2);
HOr* or2 = MakeBinOp<HOr>(right, DataType::Type::kInt32, or1, add3);
HPhi* phi = MakePhi(return_block, {add1, or2});
HAdd* add4 = MakeBinOp<HAdd>(return_block, DataType::Type::kInt32, param6, shl);
HOr* or3 = MakeBinOp<HOr>(return_block, DataType::Type::kInt32, phi, add4);
MakeReturn(return_block, or3);
graph_->BuildDominatorTree();
InstructionSimplifierArm simplifier(graph_, /*codegen=*/ nullptr, /*stats=*/ nullptr);
simplifier.Run();
EXPECT_FALSE(shl->IsInBlock());
EXPECT_FALSE(add1->IsInBlock());
EXPECT_FALSE(add2->IsInBlock());
EXPECT_FALSE(sub1->IsInBlock());
EXPECT_FALSE(sub2->IsInBlock());
EXPECT_FALSE(add3->IsInBlock());
EXPECT_TRUE(or1->IsInBlock());
EXPECT_TRUE(or2->IsInBlock());
EXPECT_TRUE(phi->IsInBlock());
EXPECT_FALSE(add4->IsInBlock());
EXPECT_TRUE(or3->IsInBlock());
}
} // namespace arm
} // namespace art