summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Roman Artemev <roman.artemev@syntacore.com> 2024-08-13 12:52:00 +0000
committer VladimĂ­r Marko <vmarko@google.com> 2024-08-14 12:26:57 +0000
commit4910586af2f208262dd1f8225f42fa6f6e95d355 (patch)
tree895c5c570d089d2bd9d219c0b7e5fa995443db5e
parentb9075fca15c21ce4ea599e5379463c4fda82660a (diff)
Revert^2 "Implement transform from signed to unsigned compare"
This reverts commit 275cf7423efdb36e441b7bceb2e678944f8fa34b. Reason for revert: Fix HEquals and HNotEquals cases Change-Id: I47559e0b0d6625370e392119fd005bc10167aa87
-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.cc205
-rw-r--r--compiler/optimizing/nodes.h22
-rw-r--r--test/2275-integral-unsigned-arithmetic/src/Main.java253
9 files changed, 555 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..4bee9c0711 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,26 @@ 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()) &&
+ !condition->IsEqual() &&
+ !condition->IsNotEqual()) {
+ 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 +1935,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 {
diff --git a/test/2275-integral-unsigned-arithmetic/src/Main.java b/test/2275-integral-unsigned-arithmetic/src/Main.java
index c434ebf7bc..82785309f3 100644
--- a/test/2275-integral-unsigned-arithmetic/src/Main.java
+++ b/test/2275-integral-unsigned-arithmetic/src/Main.java
@@ -28,6 +28,14 @@ public class Main {
test_Long_remainderUnsigned_no_fold();
test_Integer_remainderUnsigned();
test_Long_remainderUnsigned();
+
+ test_Integer_compareUnsigned_in_condition();
+ test_Long_compareUnsigned_in_condition();
+
+ assertEquals($noinline$compareSignedSameOperands(300), IF_TRUE_VALUE);
+
+ test_Integer_doubleUnsignedCompare();
+ test_Integer_doubleUnsignedCompare_Xored();
}
public static int $noinline$cmpUnsignedInt(int a, int b) {
@@ -348,4 +356,249 @@ public class Main {
throw new Error("Expected: " + expected + ", found: " + actual);
}
}
+
+ public static void assertEquals(boolean expected, boolean actual) {
+ if (expected != actual) {
+ throw new Error("Expected: " + expected + ", found: " + actual);
+ }
+ }
+
+ static final int IF_TRUE_VALUE = -55555;
+ static final int IF_FALSE_VALUE = 99999;
+
+ public static int $noinline$compareUnsignedInt_LT(int a, int b) {
+ return Integer.compareUnsigned(a, b) < 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedInt_LE(int a, int b) {
+ return Integer.compareUnsigned(a, b) <= 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedInt_GT(int a, int b) {
+ return Integer.compareUnsigned(a, b) > 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedInt_GE(int a, int b) {
+ return Integer.compareUnsigned(a, b) >= 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedInt_EQ(int a, int b) {
+ return Integer.compareUnsigned(a, b) == 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedInt_NE(int a, int b) {
+ return Integer.compareUnsigned(a, b) != 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedLong_LT(long a, long b) {
+ return Long.compareUnsigned(a, b) < 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedLong_LE(long a, long b) {
+ return Long.compareUnsigned(a, b) <= 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedLong_GT(long a, long b) {
+ return Long.compareUnsigned(a, b) > 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedLong_GE(long a, long b) {
+ return Long.compareUnsigned(a, b) >= 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedLong_EQ(long a, long b) {
+ return Long.compareUnsigned(a, b) == 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareUnsignedLong_NE(long a, long b) {
+ return Long.compareUnsigned(a, b) != 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static int $noinline$compareSignedSameOperands(long a) {
+ return Long.compare(a, a) >= 0 ? IF_TRUE_VALUE : IF_FALSE_VALUE;
+ }
+
+ public static void test_Integer_compareUnsigned_in_condition() {
+ // <
+ assertEquals($noinline$compareUnsignedInt_LT(10, 20), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_LT(Integer.MIN_VALUE, 0), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_LT(0, 0), IF_FALSE_VALUE);
+
+ // <=
+ assertEquals($noinline$compareUnsignedInt_LE(10, 20), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_LE(Integer.MIN_VALUE, 0), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_LE(0, 0), IF_TRUE_VALUE);
+
+ // >
+ assertEquals($noinline$compareUnsignedInt_GT(10, 20), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_GT(Integer.MIN_VALUE, 0), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_GT(0, 0), IF_FALSE_VALUE);
+
+ // =>
+ assertEquals($noinline$compareUnsignedInt_GE(10, 20), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_GE(Integer.MIN_VALUE, 0), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_GE(0, 0), IF_TRUE_VALUE);
+
+ // ==
+ assertEquals($noinline$compareUnsignedInt_EQ(10, 20), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_EQ(Integer.MIN_VALUE, 0), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_EQ(0, 0), IF_TRUE_VALUE);
+
+ // !=
+ assertEquals($noinline$compareUnsignedInt_NE(10, 20), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_NE(Integer.MIN_VALUE, 0), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedInt_NE(0, 0), IF_FALSE_VALUE);
+ }
+
+ public static void test_Long_compareUnsigned_in_condition() {
+ // <
+ assertEquals($noinline$compareUnsignedLong_LT(10L, 20L), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_LT(Long.MIN_VALUE, 0L), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_LT(0L, 0L), IF_FALSE_VALUE);
+
+ // <=
+ assertEquals($noinline$compareUnsignedLong_LE(10L, 20L), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_LE(Long.MIN_VALUE, 0L), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_LE(0L, 0L), IF_TRUE_VALUE);
+
+ // >
+ assertEquals($noinline$compareUnsignedLong_GT(10L, 20L), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_GT(Long.MIN_VALUE, 0L), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_GT(0L, 0L), IF_FALSE_VALUE);
+
+ // =>
+ assertEquals($noinline$compareUnsignedLong_GE(10L, 20L), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_GE(Long.MIN_VALUE, 0L), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_GE(0L, 0L), IF_TRUE_VALUE);
+
+ // ==
+ assertEquals($noinline$compareUnsignedLong_EQ(10L, 20L), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_EQ(Long.MIN_VALUE, 0L), IF_FALSE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_EQ(0L, 0L), IF_TRUE_VALUE);
+
+ // !=
+ assertEquals($noinline$compareUnsignedLong_NE(10L, 20L), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_NE(Long.MIN_VALUE, 0L), IF_TRUE_VALUE);
+ assertEquals($noinline$compareUnsignedLong_NE(0L, 0L), IF_FALSE_VALUE);
+ }
+
+ private static int $inline$hidden_zero() {
+ return "1".indexOf('1');
+ }
+
+ public static boolean $inline$BelowInteger(int x, int y) {
+ return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE) < 0;
+ }
+
+ public static boolean $noinline$testDoubleUnsignedCompareInteger_B(int x, int y) {
+ int cmp = Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
+ return $inline$BelowInteger(cmp, $inline$hidden_zero());
+ }
+
+ public static boolean $inline$BelowOrEqualInteger(int x, int y) {
+ return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE) <= 0;
+ }
+
+ public static boolean $noinline$testDoubleUnsignedCompareInteger_BE(int x, int y) {
+ int cmp = Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
+ return $inline$BelowOrEqualInteger(cmp, $inline$hidden_zero());
+ }
+
+ public static boolean $inline$AboveInteger(int x, int y) {
+ return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE) > 0;
+ }
+
+ public static boolean $noinline$testDoubleUnsignedCompareInteger_A(int x, int y) {
+ int cmp = Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
+ return $inline$AboveInteger(cmp, $inline$hidden_zero());
+ }
+
+ public static boolean $inline$AboveOrEqualInteger(int x, int y) {
+ return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE) >= 0;
+ }
+
+ public static boolean $noinline$testDoubleUnsignedCompareInteger_AE(int x, int y) {
+ int cmp = Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
+ return $inline$AboveOrEqualInteger(cmp, $inline$hidden_zero());
+ }
+
+ public static void test_Integer_doubleUnsignedCompare() {
+ // <
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_B(0, 0), false);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_B(Integer.MIN_VALUE, 0), false);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_B(0, Integer.MIN_VALUE), false);
+
+ // <=
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BE(0, 0), true);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BE(Integer.MIN_VALUE, 0), false);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BE(0, Integer.MIN_VALUE), false);
+
+ // >
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_A(0, 0), false);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_A(Integer.MIN_VALUE, 0), true);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_A(0, Integer.MIN_VALUE), true);
+
+ // =>
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AE(0, 0), true);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AE(Integer.MIN_VALUE, 0), true);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AE(0, Integer.MIN_VALUE), true);
+ }
+
+ public static boolean $inline$BelowInteger_Xored(int x, int y) {
+ return Integer.compare(x ^ Integer.MIN_VALUE, y ^ Integer.MIN_VALUE) < 0;
+ }
+
+ public static boolean $noinline$testDoubleUnsignedCompareInteger_BT_Xored(int x, int y) {
+ int cmp = Integer.compare(x ^ Integer.MIN_VALUE, y ^ Integer.MIN_VALUE);
+ return $inline$BelowInteger_Xored(cmp, $inline$hidden_zero());
+ }
+
+ public static boolean $inline$BelowOrEqualInteger_Xored(int x, int y) {
+ return Integer.compare(x ^ Integer.MIN_VALUE, y ^ Integer.MIN_VALUE) <= 0;
+ }
+
+ public static boolean $noinline$testDoubleUnsignedCompareInteger_BE_Xored(int x, int y) {
+ int cmp = Integer.compare(x ^ Integer.MIN_VALUE, y ^ Integer.MIN_VALUE);
+ return $inline$BelowOrEqualInteger_Xored(cmp, $inline$hidden_zero());
+ }
+
+ public static boolean $inline$AboveInteger_Xored(int x, int y) {
+ return Integer.compare(x ^ Integer.MIN_VALUE, y ^ Integer.MIN_VALUE) > 0;
+ }
+
+ public static boolean $noinline$testDoubleUnsignedCompareInteger_AT_Xored(int x, int y) {
+ int cmp = Integer.compare(x ^ Integer.MIN_VALUE, y ^ Integer.MIN_VALUE);
+ return $inline$AboveInteger_Xored(cmp, $inline$hidden_zero());
+ }
+
+ public static boolean $inline$AboveOrEqualInteger_Xored(int x, int y) {
+ return Integer.compare(x ^ Integer.MIN_VALUE, y ^ Integer.MIN_VALUE) >= 0;
+ }
+
+ public static boolean $noinline$testDoubleUnsignedCompareInteger_AE_Xored(int x, int y) {
+ int cmp = Integer.compare(x ^ Integer.MIN_VALUE, y ^ Integer.MIN_VALUE);
+ return $inline$AboveOrEqualInteger_Xored(cmp, $inline$hidden_zero());
+ }
+
+ public static void test_Integer_doubleUnsignedCompare_Xored() {
+ // <
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BT_Xored(0, 0), false);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BT_Xored(Integer.MIN_VALUE, 0), false);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BT_Xored(0, Integer.MIN_VALUE), false);
+
+ // <=
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BE_Xored(0, 0), true);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BE_Xored(Integer.MIN_VALUE, 0), false);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_BE_Xored(0, Integer.MIN_VALUE), false);
+
+ // >
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AT_Xored(0, 0), false);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AT_Xored(Integer.MIN_VALUE, 0), true);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AT_Xored(0, Integer.MIN_VALUE), true);
+
+ // =>
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AE_Xored(0, 0), true);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AE_Xored(Integer.MIN_VALUE, 0), true);
+ assertEquals($noinline$testDoubleUnsignedCompareInteger_AE_Xored(0, Integer.MIN_VALUE), true);
+ }
}