summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
author Roman Artemev <roman.artemev@syntacore.com> 2024-05-24 17:06:14 +0300
committer Santiago Aboy Solanes <solanes@google.com> 2024-08-13 08:12:08 +0000
commit7496a81f4298257a5c6ce25271873b0c12e69f96 (patch)
tree9221b1e2b39ab35bdd4ab3de00a8badcac141ce7 /compiler/optimizing
parent3e75615ad25b6af1842b194e78b429b0f585b46a (diff)
Implement transform from signed to unsigned compare
Support unsigned comparison in HCompare IR node Implement instruction simplifier pass Implement code generation of HCompare node for all supported CPU targets Performance gain on RISC-V: Integer.compareUnsigned: +7.17% Long.compareUnsigned: +7.17% Add test cases to check simplification from HCompare to HCondition Test: testrunner.py --target --64 --ndebug --optimizing Change-Id: I591016e9aa4d094dc5e6f8b0128ac91f842cf25e
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/code_generator_arm64.cc25
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.cc31
-rw-r--r--compiler/optimizing/code_generator_riscv64.cc23
-rw-r--r--compiler/optimizing/code_generator_x86.cc22
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc14
-rw-r--r--compiler/optimizing/graph_visualizer.cc1
-rw-r--r--compiler/optimizing/instruction_simplifier.cc203
-rw-r--r--compiler/optimizing/nodes.h22
8 files changed, 300 insertions, 41 deletions
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index d6375f8b59..5b7f880589 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -3270,16 +3270,18 @@ void InstructionCodeGeneratorARM64::GenerateFcmp(HInstruction* instruction) {
void LocationsBuilderARM64::VisitCompare(HCompare* compare) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
- DataType::Type in_type = compare->InputAt(0)->GetType();
+ DataType::Type compare_type = compare->GetComparisonType();
HInstruction* rhs = compare->InputAt(1);
- switch (in_type) {
+ switch (compare_type) {
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
case DataType::Type::kInt32:
- case DataType::Type::kInt64: {
+ case DataType::Type::kUint32:
+ case DataType::Type::kInt64:
+ case DataType::Type::kUint64: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, ARM64EncodableConstantOrRegister(rhs, compare));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
@@ -3296,17 +3298,22 @@ void LocationsBuilderARM64::VisitCompare(HCompare* compare) {
break;
}
default:
- LOG(FATAL) << "Unexpected type for compare operation " << in_type;
+ LOG(FATAL) << "Unexpected type for compare operation " << compare_type;
}
}
void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) {
- DataType::Type in_type = compare->InputAt(0)->GetType();
+ DataType::Type compare_type = compare->GetComparisonType();
// 0 if: left == right
// 1 if: left > right
// -1 if: left < right
- switch (in_type) {
+ Condition less_cond = lt;
+ switch (compare_type) {
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64:
+ less_cond = lo;
+ FALLTHROUGH_INTENDED;
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
@@ -3318,8 +3325,8 @@ void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) {
Register left = InputRegisterAt(compare, 0);
Operand right = InputOperandAt(compare, 1);
__ Cmp(left, right);
- __ Cset(result, ne); // result == +1 if NE or 0 otherwise
- __ Cneg(result, result, lt); // result == -1 if LT or unchanged otherwise
+ __ Cset(result, ne); // result == +1 if NE or 0 otherwise
+ __ Cneg(result, result, less_cond); // result == -1 if LT or unchanged otherwise
break;
}
case DataType::Type::kFloat32:
@@ -3331,7 +3338,7 @@ void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) {
break;
}
default:
- LOG(FATAL) << "Unimplemented compare type " << in_type;
+ LOG(FATAL) << "Unimplemented compare type " << compare_type;
}
}
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 34227a5480..92081ff1ac 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -5756,14 +5756,16 @@ void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
- switch (compare->InputAt(0)->GetType()) {
+ switch (compare->GetComparisonType()) {
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
case DataType::Type::kInt32:
- case DataType::Type::kInt64: {
+ case DataType::Type::kUint32:
+ case DataType::Type::kInt64:
+ case DataType::Type::kUint64: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::RequiresRegister());
// Output overlaps because it is written before doing the low comparison.
@@ -5790,9 +5792,14 @@ void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
vixl32::Label less, greater, done;
vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
- DataType::Type type = compare->InputAt(0)->GetType();
- vixl32::Condition less_cond = vixl32::Condition::None();
+ DataType::Type type = compare->GetComparisonType();
+ vixl32::Condition less_cond = vixl32::ConditionType::lt;
+ vixl32::Condition greater_cond = vixl32::ConditionType::gt;
switch (type) {
+ case DataType::Type::kUint32:
+ less_cond = vixl32::ConditionType::lo;
+ // greater_cond - is not needed below
+ FALLTHROUGH_INTENDED;
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
@@ -5801,18 +5808,22 @@ void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
case DataType::Type::kInt32: {
// Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
__ Mov(out, 0);
- __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
- less_cond = lt;
+ __ Cmp(RegisterFrom(left), RegisterFrom(right));
break;
}
+ case DataType::Type::kUint64:
+ less_cond = vixl32::ConditionType::lo;
+ greater_cond = vixl32::ConditionType::hi;
+ FALLTHROUGH_INTENDED;
case DataType::Type::kInt64: {
- __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
- __ B(lt, &less, /* is_far_target= */ false);
- __ B(gt, &greater, /* is_far_target= */ false);
+ __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // High part compare.
+ __ B(less_cond, &less, /* is_far_target= */ false);
+ __ B(greater_cond, &greater, /* is_far_target= */ false);
// Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
__ Mov(out, 0);
__ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
- less_cond = lo;
+ less_cond = vixl32::ConditionType::lo;
+ // greater_cond - is not needed below
break;
}
case DataType::Type::kFloat32:
diff --git a/compiler/optimizing/code_generator_riscv64.cc b/compiler/optimizing/code_generator_riscv64.cc
index f6067a5468..bfc693f76b 100644
--- a/compiler/optimizing/code_generator_riscv64.cc
+++ b/compiler/optimizing/code_generator_riscv64.cc
@@ -3470,18 +3470,20 @@ void InstructionCodeGeneratorRISCV64::VisitClinitCheck(HClinitCheck* instruction
}
void LocationsBuilderRISCV64::VisitCompare(HCompare* instruction) {
- DataType::Type in_type = instruction->InputAt(0)->GetType();
+ DataType::Type compare_type = instruction->GetComparisonType();
LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
- switch (in_type) {
+ switch (compare_type) {
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
case DataType::Type::kInt32:
+ case DataType::Type::kUint32:
case DataType::Type::kInt64:
+ case DataType::Type::kUint64:
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, RegisterOrZeroBitPatternLocation(instruction->InputAt(1)));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
@@ -3495,7 +3497,7 @@ void LocationsBuilderRISCV64::VisitCompare(HCompare* instruction) {
break;
default:
- LOG(FATAL) << "Unexpected type for compare operation " << in_type;
+ LOG(FATAL) << "Unexpected type for compare operation " << compare_type;
UNREACHABLE();
}
}
@@ -3504,11 +3506,12 @@ void InstructionCodeGeneratorRISCV64::VisitCompare(HCompare* instruction) {
LocationSummary* locations = instruction->GetLocations();
XRegister result = locations->Out().AsRegister<XRegister>();
DataType::Type in_type = instruction->InputAt(0)->GetType();
+ DataType::Type compare_type = instruction->GetComparisonType();
// 0 if: left == right
// 1 if: left > right
// -1 if: left < right
- switch (in_type) {
+ switch (compare_type) {
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
@@ -3526,6 +3529,18 @@ void InstructionCodeGeneratorRISCV64::VisitCompare(HCompare* instruction) {
break;
}
+ case DataType::Type::kUint32:
+ case DataType::Type::kUint64: {
+ XRegister left = locations->InAt(0).AsRegister<XRegister>();
+ XRegister right = InputXRegisterOrZero(locations->InAt(1));
+ ScratchRegisterScope srs(GetAssembler());
+ XRegister tmp = srs.AllocateXRegister();
+ __ Sltu(tmp, left, right);
+ __ Sltu(result, right, left);
+ __ Sub(result, result, tmp);
+ break;
+ }
+
case DataType::Type::kFloat32:
case DataType::Type::kFloat64: {
FRegister left = locations->InAt(0).AsFpuRegister<FRegister>();
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 91f4a89ced..52c4b321f9 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -5288,14 +5288,16 @@ void InstructionCodeGeneratorX86::VisitBooleanNot(HBooleanNot* bool_not) {
void LocationsBuilderX86::VisitCompare(HCompare* compare) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
- switch (compare->InputAt(0)->GetType()) {
+ switch (compare->GetComparisonType()) {
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
case DataType::Type::kInt32:
- case DataType::Type::kInt64: {
+ case DataType::Type::kUint32:
+ case DataType::Type::kInt64:
+ case DataType::Type::kUint64: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
@@ -5327,8 +5329,13 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
NearLabel less, greater, done;
Condition less_cond = kLess;
+ Condition greater_cond = kGreater;
- switch (compare->InputAt(0)->GetType()) {
+ switch (compare->GetComparisonType()) {
+ case DataType::Type::kUint32:
+ less_cond = kBelow;
+ // greater_cond - is not needed below
+ FALLTHROUGH_INTENDED;
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
@@ -5338,6 +5345,10 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
codegen_->GenerateIntCompare(left, right);
break;
}
+ case DataType::Type::kUint64:
+ less_cond = kBelow;
+ greater_cond = kAbove;
+ FALLTHROUGH_INTENDED;
case DataType::Type::kInt64: {
Register left_low = left.AsRegisterPairLow<Register>();
Register left_high = left.AsRegisterPairHigh<Register>();
@@ -5361,8 +5372,8 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
DCHECK(right_is_const) << right;
codegen_->Compare32BitValue(left_high, val_high);
}
- __ j(kLess, &less); // Signed compare.
- __ j(kGreater, &greater); // Signed compare.
+ __ j(less_cond, &less); // High part compare.
+ __ j(greater_cond, &greater); // High part compare.
if (right.IsRegisterPair()) {
__ cmpl(left_low, right.AsRegisterPairLow<Register>());
} else if (right.IsDoubleStackSlot()) {
@@ -5372,6 +5383,7 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
codegen_->Compare32BitValue(left_low, val_low);
}
less_cond = kBelow; // for CF (unsigned).
+ // greater_cond - is not needed below
break;
}
case DataType::Type::kFloat32: {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 7d2a9213fd..2d26fb86ad 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2727,14 +2727,16 @@ void InstructionCodeGeneratorX86_64::VisitAboveOrEqual(HAboveOrEqual* comp) {
void LocationsBuilderX86_64::VisitCompare(HCompare* compare) {
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
- switch (compare->InputAt(0)->GetType()) {
+ switch (compare->GetComparisonType()) {
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
case DataType::Type::kUint16:
case DataType::Type::kInt16:
case DataType::Type::kInt32:
- case DataType::Type::kInt64: {
+ case DataType::Type::kUint32:
+ case DataType::Type::kInt64:
+ case DataType::Type::kUint64: {
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::Any());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
@@ -2759,10 +2761,13 @@ void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
Location right = locations->InAt(1);
NearLabel less, greater, done;
- DataType::Type type = compare->InputAt(0)->GetType();
+ DataType::Type type = compare->GetComparisonType();
Condition less_cond = kLess;
switch (type) {
+ case DataType::Type::kUint32:
+ less_cond = kBelow;
+ FALLTHROUGH_INTENDED;
case DataType::Type::kBool:
case DataType::Type::kUint8:
case DataType::Type::kInt8:
@@ -2772,6 +2777,9 @@ void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
codegen_->GenerateIntCompare(left, right);
break;
}
+ case DataType::Type::kUint64:
+ less_cond = kBelow;
+ FALLTHROUGH_INTENDED;
case DataType::Type::kInt64: {
codegen_->GenerateLongCompare(left, right);
break;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 46db4489d6..7de0ac1998 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -488,6 +488,7 @@ class HGraphVisualizerPrinter final : public HGraphDelegateVisitor {
void VisitCompare(HCompare* compare) override {
StartAttributeStream("bias") << compare->GetBias();
+ StartAttributeStream("comparison_type") << compare->GetComparisonType();
}
void VisitCondition(HCondition* condition) override {
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index cafa83bece..ec02c6ef66 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -89,6 +89,7 @@ class InstructionSimplifierVisitor final : public HGraphDelegateVisitor {
void VisitAbs(HAbs* instruction) override;
void VisitAdd(HAdd* instruction) override;
void VisitAnd(HAnd* instruction) override;
+ void VisitCompare(HCompare* instruction) override;
void VisitCondition(HCondition* instruction) override;
void VisitGreaterThan(HGreaterThan* condition) override;
void VisitGreaterThanOrEqual(HGreaterThanOrEqual* condition) override;
@@ -878,7 +879,7 @@ void InstructionSimplifierVisitor::VisitStaticFieldSet(HStaticFieldSet* instruct
}
}
-static HCondition* GetOppositeConditionSwapOps(ArenaAllocator* allocator, HInstruction* cond) {
+static HCondition* CreateOppositeConditionSwapOps(ArenaAllocator* allocator, HInstruction* cond) {
HInstruction *lhs = cond->InputAt(0);
HInstruction *rhs = cond->InputAt(1);
switch (cond->GetKind()) {
@@ -1794,6 +1795,56 @@ static bool RecognizeAndSimplifyClassCheck(HCondition* condition) {
}
}
+static HInstruction* CreateUnsignedConditionReplacement(ArenaAllocator* allocator,
+ HCondition* cond,
+ HCompare* compare) {
+ DCHECK(cond->InputAt(1)->IsIntConstant());
+ DCHECK_EQ(cond->InputAt(1)->AsIntConstant()->GetValue(), 0);
+ DCHECK(cond->InputAt(0) == compare);
+
+ HBasicBlock* block = cond->GetBlock();
+ HInstruction* lhs = compare->InputAt(0);
+ HInstruction* rhs = compare->InputAt(1);
+
+ switch (cond->GetKind()) {
+ case HInstruction::kLessThan:
+ return new (allocator) HBelow(lhs, rhs, cond->GetDexPc());
+ case HInstruction::kLessThanOrEqual:
+ return new (allocator) HBelowOrEqual(lhs, rhs, cond->GetDexPc());
+ case HInstruction::kGreaterThan:
+ return new (allocator) HAbove(lhs, rhs, cond->GetDexPc());
+ case HInstruction::kGreaterThanOrEqual:
+ return new (allocator) HAboveOrEqual(lhs, rhs, cond->GetDexPc());
+ case HInstruction::kBelow:
+ // Below(Compare(x, y), 0) always False since
+ // unsigned(-1) < 0 -> False
+ // 0 < 0 -> False
+ // 1 < 0 -> False
+ return block->GetGraph()->GetConstant(DataType::Type::kBool, 0, cond->GetDexPc());
+ case HInstruction::kBelowOrEqual:
+ // BelowOrEqual(Compare(x, y), 0) transforms into Equal(x, y)
+ // unsigned(-1) <= 0 -> False
+ // 0 <= 0 -> True
+ // 1 <= 0 -> False
+ return new (allocator) HEqual(lhs, rhs, cond->GetDexPc());
+ case HInstruction::kAbove:
+ // Above(Compare(x, y), 0) transforms into NotEqual(x, y)
+ // unsigned(-1) > 0 -> True
+ // 0 > 0 -> False
+ // 1 > 0 -> True
+ return new (allocator) HNotEqual(lhs, rhs, cond->GetDexPc());
+ case HInstruction::kAboveOrEqual:
+ // AboveOrEqual(Compare(x, y), 0) always True since
+ // unsigned(-1) >= 0 -> True
+ // 0 >= 0 -> True
+ // 1 >= 0 -> True
+ return block->GetGraph()->GetConstant(DataType::Type::kBool, 1, cond->GetDexPc());
+ default:
+ LOG(FATAL) << "Unknown ConditionType " << cond->GetKind();
+ UNREACHABLE();
+ }
+}
+
void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
if (condition->IsEqual() || condition->IsNotEqual()) {
if (RecognizeAndSimplifyClassCheck(condition)) {
@@ -1803,10 +1854,10 @@ void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
// Reverse condition if left is constant. Our code generators prefer constant
// on the right hand side.
+ HBasicBlock* block = condition->GetBlock();
if (condition->GetLeft()->IsConstant() && !condition->GetRight()->IsConstant()) {
- HBasicBlock* block = condition->GetBlock();
HCondition* replacement =
- GetOppositeConditionSwapOps(block->GetGraph()->GetAllocator(), condition);
+ CreateOppositeConditionSwapOps(block->GetGraph()->GetAllocator(), condition);
// If it is a fp we must set the opposite bias.
if (replacement != nullptr) {
if (condition->IsLtBias()) {
@@ -1814,6 +1865,7 @@ void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
} else if (condition->IsGtBias()) {
replacement->SetBias(ComparisonBias::kLtBias);
}
+
block->ReplaceAndRemoveInstructionWith(condition, replacement);
RecordSimplification();
@@ -1856,11 +1908,24 @@ void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
left->RemoveEnvironmentUsers();
// We have decided to fold the HCompare into the HCondition. Transfer the information.
- condition->SetBias(left->AsCompare()->GetBias());
+ if (DataType::IsUnsignedType(left->AsCompare()->GetComparisonType())) {
+ DCHECK_EQ(condition->GetBias(), ComparisonBias::kNoBias);
+ HInstruction* replacement = CreateUnsignedConditionReplacement(
+ block->GetGraph()->GetAllocator(), condition, left->AsCompare());
+
+ if (replacement->IsConstant()) {
+ condition->ReplaceWith(replacement);
+ block->RemoveInstruction(condition);
+ } else {
+ block->ReplaceAndRemoveInstructionWith(condition, replacement);
+ }
+ } else {
+ condition->SetBias(left->AsCompare()->GetBias());
- // Replace the operands of the HCondition.
- condition->ReplaceInput(left->InputAt(0), 0);
- condition->ReplaceInput(left->InputAt(1), 1);
+ // Replace the operands of the HCondition.
+ condition->ReplaceInput(left->InputAt(0), 0);
+ condition->ReplaceInput(left->InputAt(1), 1);
+ }
// Remove the HCompare.
left->GetBlock()->RemoveInstruction(left);
@@ -1868,6 +1933,130 @@ void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) {
RecordSimplification();
}
+static HInstruction* CheckSignedToUnsignedCompareConversion(HInstruction* operand,
+ HCompare* compare) {
+ // Check if operand looks like `ADD op, MIN_INTEGRAL`
+ if (operand->IsConstant()) {
+ // CONSTANT #x -> CONSTANT #(x - MIN_INTEGRAL)
+ HConstant* constant = operand->AsConstant();
+ if (constant->IsIntConstant()) {
+ HIntConstant* int_constant = constant->AsIntConstant();
+ int32_t old_value = int_constant->GetValue();
+ int32_t new_value = old_value - std::numeric_limits<int32_t>::min();
+ return operand->GetBlock()->GetGraph()->GetIntConstant(new_value, constant->GetDexPc());
+ } else if (constant->IsLongConstant()) {
+ HLongConstant* long_constant = constant->AsLongConstant();
+ int64_t old_value = long_constant->GetValue();
+ int64_t new_value = old_value - std::numeric_limits<int64_t>::min();
+ return operand->GetBlock()->GetGraph()->GetLongConstant(new_value, constant->GetDexPc());
+ } else {
+ return nullptr;
+ }
+ }
+
+ if (!operand->IsAdd() && !operand->IsXor()) {
+ return nullptr;
+ }
+
+ if (!operand->GetEnvUses().empty()) {
+ // There is a reference to the compare result in an environment. Do we really need it?
+ if (operand->GetBlock()->GetGraph()->IsDebuggable()) {
+ return nullptr;
+ }
+
+ // We have to ensure that there are no deopt points in the sequence.
+ if (operand->HasAnyEnvironmentUseBefore(compare)) {
+ return nullptr;
+ }
+ }
+
+ HBinaryOperation* additive_operand = operand->AsBinaryOperation();
+
+ HInstruction* left = additive_operand->GetLeft();
+ HInstruction* right = additive_operand->GetRight();
+
+ HConstant* constant = nullptr;
+ HInstruction* value = nullptr;
+
+ if (left->IsConstant() && !right->IsConstant()) {
+ constant = left->AsConstant();
+ value = right;
+ } else if (!left->IsConstant() && right->IsConstant()) {
+ value = left;
+ constant = right->AsConstant();
+ } else {
+ return nullptr;
+ }
+
+ if (constant->IsIntConstant()) {
+ HIntConstant* int_constant = constant->AsIntConstant();
+ if (int_constant->GetValue() != std::numeric_limits<int32_t>::min()) {
+ return nullptr;
+ }
+ } else if (constant->IsLongConstant()) {
+ HLongConstant* long_constant = constant->AsLongConstant();
+ if (long_constant->GetValue() != std::numeric_limits<int64_t>::min()) {
+ return nullptr;
+ }
+ } else {
+ return nullptr;
+ }
+
+ return value;
+}
+
+static DataType::Type GetOpositeSignType(DataType::Type type) {
+ return DataType::IsUnsignedType(type) ? DataType::ToSigned(type) : DataType::ToUnsigned(type);
+}
+
+void InstructionSimplifierVisitor::VisitCompare(HCompare* compare) {
+ // Transform signed compare into unsigned if possible
+ // Replace code looking like
+ // ADD normalizedLeft, left, MIN_INTEGRAL
+ // ADD normalizedRight, right, MIN_INTEGRAL
+ // COMPARE normalizedLeft, normalizedRight, sign
+ // with
+ // COMPARE left, right, !sign
+
+ if (!DataType::IsIntegralType(compare->GetComparisonType())) {
+ return;
+ }
+
+ HInstruction* compare_left = compare->GetLeft();
+ HInstruction* compare_right = compare->GetRight();
+
+ if (compare_left->IsConstant() && compare_right->IsConstant()) {
+ // Do not simplify, let it be folded.
+ return;
+ }
+
+ HInstruction* left = CheckSignedToUnsignedCompareConversion(compare_left, compare);
+ if (left == nullptr) {
+ return;
+ }
+
+ HInstruction* right = CheckSignedToUnsignedCompareConversion(compare_right, compare);
+ if (right == nullptr) {
+ return;
+ }
+
+ compare->SetComparisonType(GetOpositeSignType(compare->GetComparisonType()));
+ compare->ReplaceInput(left, 0);
+ compare->ReplaceInput(right, 1);
+
+ RecordSimplification();
+
+ if (compare_left->GetUses().empty()) {
+ compare_left->RemoveEnvironmentUsers();
+ compare_left->GetBlock()->RemoveInstruction(compare_left);
+ }
+
+ if (compare_right->GetUses().empty()) {
+ compare_right->RemoveEnvironmentUsers();
+ compare_right->GetBlock()->RemoveInstruction(compare_right);
+ }
+}
+
// Return whether x / divisor == x * (1.0f / divisor), for every float x.
static constexpr bool CanDivideByReciprocalMultiplyFloat(int32_t divisor) {
// True, if the most significant bits of divisor are 0.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 99bb5f8478..9d6da95f0f 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4503,6 +4503,7 @@ class HCompare final : public HBinaryOperation {
SideEffectsForArchRuntimeCalls(comparison_type),
dex_pc) {
SetPackedField<ComparisonBiasField>(bias);
+ SetPackedField<ComparisonTypeField>(comparison_type);
}
template <typename T>
@@ -4522,10 +4523,16 @@ class HCompare final : public HBinaryOperation {
// graph. However HCompare integer instructions can be synthesized
// by the instruction simplifier to implement IntegerCompare and
// IntegerSignum intrinsics, so we have to handle this case.
- return MakeConstantComparison(Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ const int32_t value = DataType::IsUnsignedType(GetComparisonType()) ?
+ Compute(x->GetValueAsUint64(), y->GetValueAsUint64()) :
+ Compute(x->GetValue(), y->GetValue());
+ return MakeConstantComparison(value, GetDexPc());
}
HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const override {
- return MakeConstantComparison(Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ const int32_t value = DataType::IsUnsignedType(GetComparisonType()) ?
+ Compute(x->GetValueAsUint64(), y->GetValueAsUint64()) :
+ Compute(x->GetValue(), y->GetValue());
+ return MakeConstantComparison(value, GetDexPc());
}
HConstant* Evaluate(HFloatConstant* x, HFloatConstant* y) const override {
return MakeConstantComparison(ComputeFP(x->GetValue(), y->GetValue()), GetDexPc());
@@ -4540,6 +4547,10 @@ class HCompare final : public HBinaryOperation {
ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); }
+ DataType::Type GetComparisonType() const { return GetPackedField<ComparisonTypeField>(); }
+
+ void SetComparisonType(DataType::Type newType) { SetPackedField<ComparisonTypeField>(newType); }
+
// Does this compare instruction have a "gt bias" (vs an "lt bias")?
// Only meaningful for floating-point comparisons.
bool IsGtBias() const {
@@ -4558,11 +4569,16 @@ class HCompare final : public HBinaryOperation {
static constexpr size_t kFieldComparisonBias = kNumberOfGenericPackedBits;
static constexpr size_t kFieldComparisonBiasSize =
MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast));
+ static constexpr size_t kFieldComparisonType = kFieldComparisonBias + kFieldComparisonBiasSize;
+ static constexpr size_t kFieldComparisonTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(DataType::Type::kLast));
static constexpr size_t kNumberOfComparePackedBits =
- kFieldComparisonBias + kFieldComparisonBiasSize;
+ kFieldComparisonType + kFieldComparisonTypeSize;
static_assert(kNumberOfComparePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using ComparisonBiasField =
BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>;
+ using ComparisonTypeField =
+ BitField<DataType::Type, kFieldComparisonType, kFieldComparisonTypeSize>;
// Return an integer constant containing the result of a comparison evaluated at compile time.
HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const {