Skip to content

Commit

Permalink
riscv: Expand BitwiseNegatedRight to riscv64, optimize
Browse files Browse the repository at this point in the history
Add BitwiseNegatedRight optimization for riscv:
And + Not -> AndNot
Or  + Not -> OrNot
Xor + Not -> XorNot

By compiling facebook app using dex2oat I got:
169 cases of And + Not pattern
9 cases of    Or + Not pattern
1 case of    Xor + Not pattern.

Test: art/test/testrunner/testrunner.py --target --64 --ndebug --optimizing

Change-Id: Icc2db96770378005d2fb01176298a067e1a0e4ad
  • Loading branch information
Anton Romanov authored and vmarko committed Jun 12, 2024
1 parent 05e428d commit b658a26
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 159 deletions.
31 changes: 31 additions & 0 deletions compiler/optimizing/code_generator_riscv64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5440,6 +5440,37 @@ void InstructionCodeGeneratorRISCV64::VisitRiscv64ShiftAdd(HRiscv64ShiftAdd* ins
}
}

void LocationsBuilderRISCV64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
DCHECK(codegen_->GetInstructionSetFeatures().HasZbb());
DCHECK(DataType::IsIntegralType(instruction->GetType())) << instruction->GetType();

LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}

void InstructionCodeGeneratorRISCV64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
LocationSummary* locations = instruction->GetLocations();
XRegister lhs = locations->InAt(0).AsRegister<XRegister>();
XRegister rhs = locations->InAt(1).AsRegister<XRegister>();
XRegister dst = locations->Out().AsRegister<XRegister>();

switch (instruction->GetOpKind()) {
case HInstruction::kAnd:
__ Andn(dst, lhs, rhs);
break;
case HInstruction::kOr:
__ Orn(dst, lhs, rhs);
break;
case HInstruction::kXor:
__ Xnor(dst, lhs, rhs);
break;
default:
LOG(FATAL) << "Unreachable";
}
}

void LocationsBuilderRISCV64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
UNUSED(instruction);
LOG(FATAL) << "Unimplemented";
Expand Down
10 changes: 10 additions & 0 deletions compiler/optimizing/code_generator_x86.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9302,6 +9302,16 @@ bool InstructionCodeGeneratorX86::CpuHasAvx2FeatureFlag() {
return codegen_->GetInstructionSetFeatures().HasAVX2();
}

void LocationsBuilderX86::VisitBitwiseNegatedRight(
[[maybe_unused]] HBitwiseNegatedRight* instruction) {
LOG(FATAL) << "Unimplemented";
}

void InstructionCodeGeneratorX86::VisitBitwiseNegatedRight(
[[maybe_unused]] HBitwiseNegatedRight* instruction) {
LOG(FATAL) << "Unimplemented";
}

#undef __

} // namespace x86
Expand Down
10 changes: 10 additions & 0 deletions compiler/optimizing/code_generator_x86_64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8561,6 +8561,16 @@ bool InstructionCodeGeneratorX86_64::CpuHasAvx2FeatureFlag() {
return codegen_->GetInstructionSetFeatures().HasAVX2();
}

void LocationsBuilderX86_64::VisitBitwiseNegatedRight(
[[maybe_unused]] HBitwiseNegatedRight* instruction) {
LOG(FATAL) << "Unimplemented";
}

void InstructionCodeGeneratorX86_64::VisitBitwiseNegatedRight(
[[maybe_unused]] HBitwiseNegatedRight* instruction) {
LOG(FATAL) << "Unimplemented";
}

#undef __

} // namespace x86_64
Expand Down
6 changes: 3 additions & 3 deletions compiler/optimizing/graph_visualizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -628,12 +628,12 @@ class HGraphVisualizerPrinter final : public HGraphDelegateVisitor {
DataType::ToSigned(arg_type));
}

#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) override {
void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) override {
StartAttributeStream("kind") << instruction->GetOpKind();
}

void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) override {
#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) override {
StartAttributeStream("kind") << instruction->GetOpKind();
}

Expand Down
72 changes: 72 additions & 0 deletions compiler/optimizing/instruction_simplifier.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3405,4 +3405,76 @@ void InstructionSimplifierVisitor::VisitVecMul(HVecMul* instruction) {
}
}

bool TryMergeNegatedInput(HBinaryOperation* op) {
DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName();
HInstruction* left = op->GetLeft();
HInstruction* right = op->GetRight();

// Only consider the case where there is exactly one Not, with 2 Not's De
// Morgan's laws should be applied instead.
if (left->IsNot() ^ right->IsNot()) {
HInstruction* hnot = (left->IsNot() ? left : right);
HInstruction* hother = (left->IsNot() ? right : left);

// Only do the simplification if the Not has only one use and can thus be
// safely removed. Even though ARM64 negated bitwise operations do not have
// an immediate variant (only register), we still do the simplification when
// `hother` is a constant, because it removes an instruction if the constant
// cannot be encoded as an immediate:
// mov r0, #large_constant
// neg r2, r1
// and r0, r0, r2
// becomes:
// mov r0, #large_constant
// bic r0, r0, r1
if (hnot->HasOnlyOneNonEnvironmentUse()) {
// Replace code looking like
// NOT tmp, mask
// AND dst, src, tmp (respectively ORR, EOR)
// with
// BIC dst, src, mask (respectively ORN, EON)
HInstruction* src = hnot->AsNot()->GetInput();

HBitwiseNegatedRight* neg_op = new (hnot->GetBlock()->GetGraph()->GetAllocator())
HBitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc());

op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op);
hnot->GetBlock()->RemoveInstruction(hnot);
return true;
}
}

return false;
}

bool TryMergeWithAnd(HSub* instruction) {
HAnd* and_instr = instruction->GetRight()->AsAndOrNull();
if (and_instr == nullptr) {
return false;
}

HInstruction* value = instruction->GetLeft();

HInstruction* left = and_instr->GetLeft();
const bool left_is_equal = left == value;
HInstruction* right = and_instr->GetRight();
const bool right_is_equal = right == value;
if (!left_is_equal && !right_is_equal) {
return false;
}

HBitwiseNegatedRight* bnr = new (instruction->GetBlock()->GetGraph()->GetAllocator())
HBitwiseNegatedRight(instruction->GetType(),
HInstruction::InstructionKind::kAnd,
value,
left_is_equal ? right : left,
instruction->GetDexPc());
instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, bnr);
// Since we don't run DCE after this phase, try to manually remove the And instruction.
if (!and_instr->HasUses()) {
and_instr->GetBlock()->RemoveInstruction(and_instr);
}
return true;
}

} // namespace art
13 changes: 13 additions & 0 deletions compiler/optimizing/instruction_simplifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ class InstructionSimplifier : public HOptimization {
DISALLOW_COPY_AND_ASSIGN(InstructionSimplifier);
};

// For bitwise operations (And/Or/Xor) with a negated input, try to use
// a negated bitwise instruction.
bool TryMergeNegatedInput(HBinaryOperation* op);

// Convert
// i1: AND a, b
// SUB a, i1
// into:
// BIC a, a, b
//
// It also works if `i1` is AND b, a
bool TryMergeWithAnd(HSub* instruction);

} // namespace art

#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
1 change: 1 addition & 0 deletions compiler/optimizing/instruction_simplifier_arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "code_generator.h"
#include "common_arm.h"
#include "instruction_simplifier.h"
#include "instruction_simplifier_shared.h"
#include "mirror/array-inl.h"
#include "mirror/string.h"
Expand Down
1 change: 1 addition & 0 deletions compiler/optimizing/instruction_simplifier_arm64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "instruction_simplifier_arm64.h"

#include "common_arm64.h"
#include "instruction_simplifier.h"
#include "instruction_simplifier_shared.h"
#include "mirror/array-inl.h"
#include "mirror/string.h"
Expand Down
26 changes: 26 additions & 0 deletions compiler/optimizing/instruction_simplifier_riscv64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#include "instruction_simplifier_riscv64.h"

#include "instruction_simplifier.h"

namespace art HIDDEN {

namespace riscv64 {
Expand Down Expand Up @@ -86,6 +88,30 @@ class InstructionSimplifierRiscv64Visitor final : public HGraphVisitor {
}
}

void VisitAnd(HAnd* inst) override {
if (TryMergeNegatedInput(inst)) {
RecordSimplification();
}
}

void VisitOr(HOr* inst) override {
if (TryMergeNegatedInput(inst)) {
RecordSimplification();
}
}

void VisitSub(HSub* inst) override {
if (TryMergeWithAnd(inst)) {
RecordSimplification();
}
}

void VisitXor(HXor* inst) override {
if (TryMergeNegatedInput(inst)) {
RecordSimplification();
}
}

OptimizingCompilerStats* stats_ = nullptr;
};

Expand Down
73 changes: 0 additions & 73 deletions compiler/optimizing/instruction_simplifier_shared.cc
Original file line number Diff line number Diff line change
Expand Up @@ -187,79 +187,6 @@ bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) {
return false;
}


bool TryMergeNegatedInput(HBinaryOperation* op) {
DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName();
HInstruction* left = op->GetLeft();
HInstruction* right = op->GetRight();

// Only consider the case where there is exactly one Not, with 2 Not's De
// Morgan's laws should be applied instead.
if (left->IsNot() ^ right->IsNot()) {
HInstruction* hnot = (left->IsNot() ? left : right);
HInstruction* hother = (left->IsNot() ? right : left);

// Only do the simplification if the Not has only one use and can thus be
// safely removed. Even though ARM64 negated bitwise operations do not have
// an immediate variant (only register), we still do the simplification when
// `hother` is a constant, because it removes an instruction if the constant
// cannot be encoded as an immediate:
// mov r0, #large_constant
// neg r2, r1
// and r0, r0, r2
// becomes:
// mov r0, #large_constant
// bic r0, r0, r1
if (hnot->HasOnlyOneNonEnvironmentUse()) {
// Replace code looking like
// NOT tmp, mask
// AND dst, src, tmp (respectively ORR, EOR)
// with
// BIC dst, src, mask (respectively ORN, EON)
HInstruction* src = hnot->AsNot()->GetInput();

HBitwiseNegatedRight* neg_op = new (hnot->GetBlock()->GetGraph()->GetAllocator())
HBitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc());

op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op);
hnot->GetBlock()->RemoveInstruction(hnot);
return true;
}
}

return false;
}

bool TryMergeWithAnd(HSub* instruction) {
HAnd* and_instr = instruction->GetRight()->AsAndOrNull();
if (and_instr == nullptr) {
return false;
}

HInstruction* value = instruction->GetLeft();

HInstruction* left = and_instr->GetLeft();
const bool left_is_equal = left == value;
HInstruction* right = and_instr->GetRight();
const bool right_is_equal = right == value;
if (!left_is_equal && !right_is_equal) {
return false;
}

HBitwiseNegatedRight* bnr = new (instruction->GetBlock()->GetGraph()->GetAllocator())
HBitwiseNegatedRight(instruction->GetType(),
HInstruction::InstructionKind::kAnd,
value,
left_is_equal ? right : left,
instruction->GetDexPc());
instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, bnr);
// Since we don't run DCE after this phase, try to manually remove the And instruction.
if (!and_instr->HasUses()) {
and_instr->GetBlock()->RemoveInstruction(and_instr);
}
return true;
}

bool TryExtractArrayAccessAddress(CodeGenerator* codegen,
HInstruction* access,
HInstruction* array,
Expand Down
12 changes: 0 additions & 12 deletions compiler/optimizing/instruction_simplifier_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,6 @@ inline bool IsSubRightSubLeftShl(HSub *sub) {
} // namespace helpers

bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa);
// For bitwise operations (And/Or/Xor) with a negated input, try to use
// a negated bitwise instruction.
bool TryMergeNegatedInput(HBinaryOperation* op);

// Convert
// i1: AND a, b
// SUB a, i1
// into:
// BIC a, a, b
//
// It also works if `i1` is AND b, a
bool TryMergeWithAnd(HSub* instruction);

bool TryExtractArrayAccessAddress(CodeGenerator* codegen,
HInstruction* access,
Expand Down
Loading

0 comments on commit b658a26

Please sign in to comment.