diff options
author | 2024-08-13 12:52:00 +0000 | |
---|---|---|
committer | 2024-08-14 12:26:57 +0000 | |
commit | 4910586af2f208262dd1f8225f42fa6f6e95d355 (patch) | |
tree | 895c5c570d089d2bd9d219c0b7e5fa995443db5e | |
parent | b9075fca15c21ce4ea599e5379463c4fda82660a (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.cc | 25 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.cc | 31 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_riscv64.cc | 23 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 22 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 14 | ||||
-rw-r--r-- | compiler/optimizing/graph_visualizer.cc | 1 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 205 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 22 | ||||
-rw-r--r-- | test/2275-integral-unsigned-arithmetic/src/Main.java | 253 |
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); + } } |