diff options
24 files changed, 887 insertions, 73 deletions
diff --git a/Android.mk b/Android.mk index 4f73127123..2e05d33209 100644 --- a/Android.mk +++ b/Android.mk @@ -547,3 +547,5 @@ endif # !art_dont_bother art_dont_bother := art_test_bother := TEST_ART_TARGET_SYNC_DEPS := + +include $(art_path)/runtime/openjdkjvm/Android.mk diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 005b6c189e..87f52c6f21 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -2079,6 +2079,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { switch (result_type) { case Primitive::kPrimByte: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to byte is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimShort: @@ -2097,6 +2099,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimShort: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to short is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2181,6 +2185,8 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimChar: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to char is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2280,6 +2286,10 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio switch (result_type) { case Primitive::kPrimByte: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to byte is a result of code transformations. + __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 8); + break; case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimShort: @@ -2297,6 +2307,10 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimShort: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to short is a result of code transformations. + __ sbfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16); + break; case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2398,6 +2412,10 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimChar: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to char is a result of code transformations. + __ ubfx(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>(), 0, 16); + break; case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index de23fe8eef..435ae5e954 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2928,30 +2928,128 @@ void InstructionCodeGeneratorARM64::VisitDeoptimize(HDeoptimize* deoptimize) { /* false_target */ nullptr); } -void LocationsBuilderARM64::VisitSelect(HSelect* select) { - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); +enum SelectVariant { + kCsel, + kCselFalseConst, + kCselTrueConst, + kFcsel, +}; + +static inline bool IsConditionOnFloatingPointValues(HInstruction* condition) { + return condition->IsCondition() && + Primitive::IsFloatingPointType(condition->InputAt(0)->GetType()); +} + +static inline bool IsRecognizedCselConstant(HInstruction* constant) { + if (constant->IsConstant()) { + int64_t value = Int64FromConstant(constant->AsConstant()); + if ((value == -1) || (value == 0) || (value == 1)) { + return true; + } + } + return false; +} + +static inline SelectVariant GetSelectVariant(HSelect* select) { if (Primitive::IsFloatingPointType(select->GetType())) { - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetInAt(1, Location::RequiresFpuRegister()); + return kFcsel; + } else if (IsRecognizedCselConstant(select->GetFalseValue())) { + return kCselFalseConst; + } else if (IsRecognizedCselConstant(select->GetTrueValue())) { + return kCselTrueConst; } else { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetInAt(1, Location::RequiresRegister()); + return kCsel; + } +} + +static inline bool HasSwappedInputs(SelectVariant variant) { + return variant == kCselTrueConst; +} + +static inline Condition GetConditionForSelect(HCondition* condition, SelectVariant variant) { + IfCondition cond = HasSwappedInputs(variant) ? condition->GetOppositeCondition() + : condition->GetCondition(); + return IsConditionOnFloatingPointValues(condition) ? ARM64FPCondition(cond, condition->IsGtBias()) + : ARM64Condition(cond); +} + +void LocationsBuilderARM64::VisitSelect(HSelect* select) { + LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); + switch (GetSelectVariant(select)) { + case kCsel: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + break; + case kCselFalseConst: + locations->SetInAt(0, Location::ConstantLocation(select->InputAt(0)->AsConstant())); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister()); + break; + case kCselTrueConst: + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::ConstantLocation(select->InputAt(1)->AsConstant())); + locations->SetOut(Location::RequiresRegister()); + break; + case kFcsel: + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetInAt(1, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister()); + break; } if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) { locations->SetInAt(2, Location::RequiresRegister()); } - locations->SetOut(Location::SameAsFirstInput()); } void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) { - LocationSummary* locations = select->GetLocations(); - vixl::Label false_target; - GenerateTestAndBranch(select, - /* condition_input_index */ 2, - /* true_target */ nullptr, - &false_target); - codegen_->MoveLocation(locations->Out(), locations->InAt(1), select->GetType()); - __ Bind(&false_target); + HInstruction* cond = select->GetCondition(); + SelectVariant variant = GetSelectVariant(select); + Condition csel_cond; + + if (IsBooleanValueOrMaterializedCondition(cond)) { + if (cond->IsCondition() && cond->GetNext() == select) { + // Condition codes set from previous instruction. + csel_cond = GetConditionForSelect(cond->AsCondition(), variant); + } else { + __ Cmp(InputRegisterAt(select, 2), 0); + csel_cond = HasSwappedInputs(variant) ? eq : ne; + } + } else if (IsConditionOnFloatingPointValues(cond)) { + Location rhs = cond->GetLocations()->InAt(1); + if (rhs.IsConstant()) { + DCHECK(IsFloatingPointZeroConstant(rhs.GetConstant())); + __ Fcmp(InputFPRegisterAt(cond, 0), 0.0); + } else { + __ Fcmp(InputFPRegisterAt(cond, 0), InputFPRegisterAt(cond, 1)); + } + csel_cond = GetConditionForSelect(cond->AsCondition(), variant); + } else { + __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1)); + csel_cond = GetConditionForSelect(cond->AsCondition(), variant); + } + + switch (variant) { + case kCsel: + case kCselFalseConst: + __ Csel(OutputRegister(select), + InputRegisterAt(select, 1), + InputOperandAt(select, 0), + csel_cond); + break; + case kCselTrueConst: + __ Csel(OutputRegister(select), + InputRegisterAt(select, 0), + InputOperandAt(select, 1), + csel_cond); + break; + case kFcsel: + __ Fcsel(OutputFPRegister(select), + InputFPRegisterAt(select, 1), + InputFPRegisterAt(select, 0), + csel_cond); + break; + } } void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) { diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 71d65e84a6..119084e026 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -3943,18 +3943,26 @@ void InstructionCodeGeneratorMIPS64::VisitTypeConversion(HTypeConversion* conver __ Andi(dst, src, 0xFFFF); break; case Primitive::kPrimByte: - // long is never converted into types narrower than int directly, - // so SEB and SEH can be used without ever causing unpredictable results - // on 64-bit inputs - DCHECK(input_type != Primitive::kPrimLong); - __ Seb(dst, src); + if (input_type == Primitive::kPrimLong) { + // Type conversion from long to types narrower than int is a result of code + // transformations. To avoid unpredictable results for SEB and SEH, we first + // need to sign-extend the low 32-bit value into bits 32 through 63. + __ Sll(dst, src, 0); + __ Seb(dst, dst); + } else { + __ Seb(dst, src); + } break; case Primitive::kPrimShort: - // long is never converted into types narrower than int directly, - // so SEB and SEH can be used without ever causing unpredictable results - // on 64-bit inputs - DCHECK(input_type != Primitive::kPrimLong); - __ Seh(dst, src); + if (input_type == Primitive::kPrimLong) { + // Type conversion from long to types narrower than int is a result of code + // transformations. To avoid unpredictable results for SEB and SEH, we first + // need to sign-extend the low 32-bit value into bits 32 through 63. + __ Sll(dst, src, 0); + __ Seh(dst, dst); + } else { + __ Seh(dst, src); + } break; case Primitive::kPrimInt: case Primitive::kPrimLong: diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 3713690335..07edd97c1f 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -2145,6 +2145,18 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { switch (result_type) { case Primitive::kPrimByte: switch (input_type) { + case Primitive::kPrimLong: { + // Type conversion from long to byte is a result of code transformations. + HInstruction* input = conversion->InputAt(0); + Location input_location = input->IsConstant() + ? Location::ConstantLocation(input->AsConstant()) + : Location::RegisterPairLocation(EAX, EDX); + locations->SetInAt(0, input_location); + // Make the output overlap to please the register allocator. This greatly simplifies + // the validation of the linear scan implementation + locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); + break; + } case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimShort: @@ -2165,6 +2177,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimShort: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to short is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2242,6 +2256,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimChar: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to char is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2336,6 +2352,16 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio switch (result_type) { case Primitive::kPrimByte: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to byte is a result of code transformations. + if (in.IsRegisterPair()) { + __ movsxb(out.AsRegister<Register>(), in.AsRegisterPairLow<ByteRegister>()); + } else { + DCHECK(in.GetConstant()->IsLongConstant()); + int64_t value = in.GetConstant()->AsLongConstant()->GetValue(); + __ movl(out.AsRegister<Register>(), Immediate(static_cast<int8_t>(value))); + } + break; case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimShort: @@ -2359,6 +2385,18 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimShort: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to short is a result of code transformations. + if (in.IsRegisterPair()) { + __ movsxw(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>()); + } else if (in.IsDoubleStackSlot()) { + __ movsxw(out.AsRegister<Register>(), Address(ESP, in.GetStackIndex())); + } else { + DCHECK(in.GetConstant()->IsLongConstant()); + int64_t value = in.GetConstant()->AsLongConstant()->GetValue(); + __ movl(out.AsRegister<Register>(), Immediate(static_cast<int16_t>(value))); + } + break; case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2495,6 +2533,18 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimChar: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to short is a result of code transformations. + if (in.IsRegisterPair()) { + __ movzxw(out.AsRegister<Register>(), in.AsRegisterPairLow<Register>()); + } else if (in.IsDoubleStackSlot()) { + __ movzxw(out.AsRegister<Register>(), Address(ESP, in.GetStackIndex())); + } else { + DCHECK(in.GetConstant()->IsLongConstant()); + int64_t value = in.GetConstant()->AsLongConstant()->GetValue(); + __ movl(out.AsRegister<Register>(), Immediate(static_cast<uint16_t>(value))); + } + break; case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 35603aa030..a53a6be3de 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1551,14 +1551,16 @@ void LocationsBuilderX86_64::VisitSelect(HSelect* select) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(select); if (Primitive::IsFloatingPointType(select->GetType())) { locations->SetInAt(0, Location::RequiresFpuRegister()); - // Since we can't use CMOV, there is no need to force 'true' into a register. locations->SetInAt(1, Location::Any()); } else { locations->SetInAt(0, Location::RequiresRegister()); if (SelectCanUseCMOV(select)) { - locations->SetInAt(1, Location::RequiresRegister()); + if (select->InputAt(1)->IsConstant()) { + locations->SetInAt(1, Location::RequiresRegister()); + } else { + locations->SetInAt(1, Location::Any()); + } } else { - // Since we can't use CMOV, there is no need to force 'true' into a register. locations->SetInAt(1, Location::Any()); } } @@ -1574,7 +1576,7 @@ void InstructionCodeGeneratorX86_64::VisitSelect(HSelect* select) { // If both the condition and the source types are integer, we can generate // a CMOV to implement Select. CpuRegister value_false = locations->InAt(0).AsRegister<CpuRegister>(); - CpuRegister value_true = locations->InAt(1).AsRegister<CpuRegister>(); + Location value_true_loc = locations->InAt(1); DCHECK(locations->InAt(0).Equals(locations->Out())); HInstruction* select_condition = select->GetCondition(); @@ -1606,7 +1608,14 @@ void InstructionCodeGeneratorX86_64::VisitSelect(HSelect* select) { // If the condition is true, overwrite the output, which already contains false. // Generate the correct sized CMOV. - __ cmov(cond, value_false, value_true, select->GetType() == Primitive::kPrimLong); + bool is_64_bit = Primitive::Is64BitType(select->GetType()); + if (value_true_loc.IsRegister()) { + __ cmov(cond, value_false, value_true_loc.AsRegister<CpuRegister>(), is_64_bit); + } else { + __ cmov(cond, + value_false, + Address(CpuRegister(RSP), value_true_loc.GetStackIndex()), is_64_bit); + } } else { NearLabel false_target; GenerateTestAndBranch<NearLabel>(select, @@ -2363,6 +2372,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { switch (result_type) { case Primitive::kPrimByte: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to byte is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimShort: @@ -2381,6 +2392,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimShort: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to short is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2458,6 +2471,8 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimChar: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to char is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2552,6 +2567,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver switch (result_type) { case Primitive::kPrimByte: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to byte is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimShort: @@ -2560,13 +2577,12 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver // Processing a Dex `int-to-byte' instruction. if (in.IsRegister()) { __ movsxb(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>()); - } else if (in.IsStackSlot()) { + } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) { __ movsxb(out.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), in.GetStackIndex())); } else { - DCHECK(in.GetConstant()->IsIntConstant()); __ movl(out.AsRegister<CpuRegister>(), - Immediate(static_cast<int8_t>(in.GetConstant()->AsIntConstant()->GetValue()))); + Immediate(static_cast<int8_t>(Int64FromConstant(in.GetConstant())))); } break; @@ -2578,6 +2594,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver case Primitive::kPrimShort: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to short is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2586,13 +2604,12 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver // Processing a Dex `int-to-short' instruction. if (in.IsRegister()) { __ movsxw(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>()); - } else if (in.IsStackSlot()) { + } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) { __ movsxw(out.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), in.GetStackIndex())); } else { - DCHECK(in.GetConstant()->IsIntConstant()); __ movl(out.AsRegister<CpuRegister>(), - Immediate(static_cast<int16_t>(in.GetConstant()->AsIntConstant()->GetValue()))); + Immediate(static_cast<int16_t>(Int64FromConstant(in.GetConstant())))); } break; @@ -2735,6 +2752,8 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver case Primitive::kPrimChar: switch (input_type) { + case Primitive::kPrimLong: + // Type conversion from long to char is a result of code transformations. case Primitive::kPrimBoolean: // Boolean input is a result of code transformations. case Primitive::kPrimByte: @@ -2743,14 +2762,12 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver // Processing a Dex `int-to-char' instruction. if (in.IsRegister()) { __ movzxw(out.AsRegister<CpuRegister>(), in.AsRegister<CpuRegister>()); - } else if (in.IsStackSlot()) { + } else if (in.IsStackSlot() || in.IsDoubleStackSlot()) { __ movzxw(out.AsRegister<CpuRegister>(), Address(CpuRegister(RSP), in.GetStackIndex())); } else { - DCHECK(in.GetConstant()->IsIntConstant()); __ movl(out.AsRegister<CpuRegister>(), - Immediate(static_cast<uint16_t>( - in.GetConstant()->AsIntConstant()->GetValue()))); + Immediate(static_cast<uint16_t>(Int64FromConstant(in.GetConstant())))); } break; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 0029cc3650..98f8009846 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -757,11 +757,79 @@ void InstructionSimplifierVisitor::VisitArraySet(HArraySet* instruction) { } } +static bool IsTypeConversionImplicit(Primitive::Type input_type, Primitive::Type result_type) { + // Besides conversion to the same type, widening integral conversions are implicit, + // excluding conversions to long and the byte->char conversion where we need to + // clear the high 16 bits of the 32-bit sign-extended representation of byte. + return result_type == input_type || + (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimByte) || + (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimShort) || + (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimChar) || + (result_type == Primitive::kPrimShort && input_type == Primitive::kPrimByte); +} + +static bool IsTypeConversionLossless(Primitive::Type input_type, Primitive::Type result_type) { + // The conversion to a larger type is loss-less with the exception of two cases, + // - conversion to char, the only unsigned type, where we may lose some bits, and + // - conversion from float to long, the only FP to integral conversion with smaller FP type. + // For integral to FP conversions this holds because the FP mantissa is large enough. + DCHECK_NE(input_type, result_type); + return Primitive::ComponentSize(result_type) > Primitive::ComponentSize(input_type) && + result_type != Primitive::kPrimChar && + !(result_type == Primitive::kPrimLong && input_type == Primitive::kPrimFloat); +} + void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) { - if (instruction->GetResultType() == instruction->GetInputType()) { - // Remove the instruction if it's converting to the same type. - instruction->ReplaceWith(instruction->GetInput()); + HInstruction* input = instruction->GetInput(); + Primitive::Type input_type = input->GetType(); + Primitive::Type result_type = instruction->GetResultType(); + if (IsTypeConversionImplicit(input_type, result_type)) { + // Remove the implicit conversion; this includes conversion to the same type. + instruction->ReplaceWith(input); instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + return; + } + + if (input->IsTypeConversion()) { + HTypeConversion* input_conversion = input->AsTypeConversion(); + HInstruction* original_input = input_conversion->GetInput(); + Primitive::Type original_type = original_input->GetType(); + + // When the first conversion is lossless, a direct conversion from the original type + // to the final type yields the same result, even for a lossy second conversion, for + // example float->double->int or int->double->float. + bool is_first_conversion_lossless = IsTypeConversionLossless(original_type, input_type); + + // For integral conversions, see if the first conversion loses only bits that the second + // doesn't need, i.e. the final type is no wider than the intermediate. If so, direct + // conversion yields the same result, for example long->int->short or int->char->short. + bool integral_conversions_with_non_widening_second = + Primitive::IsIntegralType(input_type) && + Primitive::IsIntegralType(original_type) && + Primitive::IsIntegralType(result_type) && + Primitive::ComponentSize(result_type) <= Primitive::ComponentSize(input_type); + + if (is_first_conversion_lossless || integral_conversions_with_non_widening_second) { + // If the merged conversion is implicit, do the simplification unconditionally. + if (IsTypeConversionImplicit(original_type, result_type)) { + instruction->ReplaceWith(original_input); + instruction->GetBlock()->RemoveInstruction(instruction); + if (!input_conversion->HasUses()) { + // Don't wait for DCE. + input_conversion->GetBlock()->RemoveInstruction(input_conversion); + } + RecordSimplification(); + return; + } + // Otherwise simplify only if the first conversion has no other use. + if (input_conversion->HasOnlyOneNonEnvironmentUse()) { + input_conversion->ReplaceWith(original_input); + input_conversion->GetBlock()->RemoveInstruction(input_conversion); + RecordSimplification(); + return; + } + } } } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 1c9b09f1e6..18b256f48e 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -5997,9 +5997,14 @@ class HBlocksInLoopReversePostOrderIterator : public ValueObject { }; inline int64_t Int64FromConstant(HConstant* constant) { - DCHECK(constant->IsIntConstant() || constant->IsLongConstant()); - return constant->IsIntConstant() ? constant->AsIntConstant()->GetValue() - : constant->AsLongConstant()->GetValue(); + if (constant->IsIntConstant()) { + return constant->AsIntConstant()->GetValue(); + } else if (constant->IsLongConstant()) { + return constant->AsLongConstant()->GetValue(); + } else { + DCHECK(constant->IsNullConstant()); + return 0; + } } inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) { diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index 7138a46890..3efef70f77 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -326,6 +326,14 @@ void X86Assembler::cmovl(Condition condition, Register dst, Register src) { } +void X86Assembler::cmovl(Condition condition, Register dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x40 + condition); + EmitOperand(dst, src); +} + + void X86Assembler::setb(Condition condition, Register dst) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 759a41e80e..00ff7bdbbd 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -363,6 +363,7 @@ class X86Assembler FINAL : public Assembler { void leal(Register dst, const Address& src); void cmovl(Condition condition, Register dst, Register src); + void cmovl(Condition condition, Register dst, const Address& src); void setb(Condition condition, Register dst); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 0fd098227a..d0d51473fe 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -332,6 +332,21 @@ TEST_F(AssemblerX86Test, UComisdAddr) { } +TEST_F(AssemblerX86Test, CmovlAddress) { + GetAssembler()->cmovl(x86::kEqual, x86::Register(x86::EAX), x86::Address( + x86::Register(x86::EDI), x86::Register(x86::EBX), x86::TIMES_4, 12)); + GetAssembler()->cmovl(x86::kNotEqual, x86::Register(x86::EDI), x86::Address( + x86::Register(x86::ESI), x86::Register(x86::EBX), x86::TIMES_4, 12)); + GetAssembler()->cmovl(x86::kEqual, x86::Register(x86::EDI), x86::Address( + x86::Register(x86::EDI), x86::Register(x86::EAX), x86::TIMES_4, 12)); + const char* expected = + "cmovzl 0xc(%EDI,%EBX,4), %eax\n" + "cmovnzl 0xc(%ESI,%EBX,4), %edi\n" + "cmovzl 0xc(%EDI,%EAX,4), %edi\n"; + + DriverStr(expected, "cmovl_address"); +} + ///////////////// // Near labels // ///////////////// diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index 10f5a005e1..d86ad1be5f 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -223,6 +223,19 @@ void X86_64Assembler::cmov(Condition c, CpuRegister dst, CpuRegister src, bool i } +void X86_64Assembler::cmov(Condition c, CpuRegister dst, const Address& src, bool is64bit) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + if (is64bit) { + EmitRex64(dst, src); + } else { + EmitOptionalRex32(dst, src); + } + EmitUint8(0x0F); + EmitUint8(0x40 + c); + EmitOperand(dst.LowBits(), src); +} + + void X86_64Assembler::movzxb(CpuRegister dst, CpuRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitOptionalByteRegNormalizingRex32(dst, src); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index 6f0847eb61..f00cb12413 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -366,6 +366,7 @@ class X86_64Assembler FINAL : public Assembler { void cmov(Condition c, CpuRegister dst, CpuRegister src); // This is the 64b version. void cmov(Condition c, CpuRegister dst, CpuRegister src, bool is64bit); + void cmov(Condition c, CpuRegister dst, const Address& src, bool is64bit); void movzxb(CpuRegister dst, CpuRegister src); void movzxb(CpuRegister dst, const Address& src); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index 8a87fca96a..4f65709810 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1371,6 +1371,37 @@ TEST_F(AssemblerX86_64Test, PopcntqAddress) { DriverStr(expected, "popcntq_address"); } +TEST_F(AssemblerX86_64Test, CmovlAddress) { + GetAssembler()->cmov(x86_64::kEqual, x86_64::CpuRegister(x86_64::R10), x86_64::Address( + x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), false); + GetAssembler()->cmov(x86_64::kNotEqual, x86_64::CpuRegister(x86_64::RDI), x86_64::Address( + x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), false); + GetAssembler()->cmov(x86_64::kEqual, x86_64::CpuRegister(x86_64::RDI), x86_64::Address( + x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), false); + const char* expected = + "cmovzl 0xc(%RDI,%RBX,4), %R10d\n" + "cmovnzl 0xc(%R10,%RBX,4), %edi\n" + "cmovzl 0xc(%RDI,%R9,4), %edi\n"; + + DriverStr(expected, "cmovl_address"); +} + +TEST_F(AssemblerX86_64Test, CmovqAddress) { + GetAssembler()->cmov(x86_64::kEqual, x86_64::CpuRegister(x86_64::R10), x86_64::Address( + x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), true); + GetAssembler()->cmov(x86_64::kNotEqual, x86_64::CpuRegister(x86_64::RDI), x86_64::Address( + x86_64::CpuRegister(x86_64::R10), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12), true); + GetAssembler()->cmov(x86_64::kEqual, x86_64::CpuRegister(x86_64::RDI), x86_64::Address( + x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::R9), x86_64::TIMES_4, 12), true); + const char* expected = + "cmovzq 0xc(%RDI,%RBX,4), %R10\n" + "cmovnzq 0xc(%R10,%RBX,4), %rdi\n" + "cmovzq 0xc(%RDI,%R9,4), %rdi\n"; + + DriverStr(expected, "cmovq_address"); +} + + ///////////////// // Near labels // ///////////////// diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index cc45c385b8..ebe89bbbd2 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -449,24 +449,25 @@ template<typename RootVisitorType> void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) { ArtMethod* interface_method = nullptr; mirror::Class* klass = declaring_class_.Read(); - if (UNLIKELY(klass != nullptr && klass->IsProxyClass())) { - // For normal methods, dex cache shortcuts will be visited through the declaring class. - // However, for proxies we need to keep the interface method alive, so we visit its roots. - interface_method = mirror::DexCache::GetElementPtrSize( - GetDexCacheResolvedMethods(pointer_size), - GetDexMethodIndex(), - pointer_size); - DCHECK(interface_method != nullptr); - DCHECK_EQ(interface_method, - Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this)); - interface_method->VisitRoots(visitor, pointer_size); - } - - visitor.VisitRootIfNonNull(declaring_class_.AddressWithoutBarrier()); - if (!IsNative()) { - ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size); - if (profiling_info != nullptr) { - profiling_info->VisitRoots(visitor); + if (LIKELY(klass != nullptr)) { + if (UNLIKELY(klass->IsProxyClass())) { + // For normal methods, dex cache shortcuts will be visited through the declaring class. + // However, for proxies we need to keep the interface method alive, so we visit its roots. + interface_method = mirror::DexCache::GetElementPtrSize( + GetDexCacheResolvedMethods(pointer_size), + GetDexMethodIndex(), + pointer_size); + DCHECK(interface_method != nullptr); + DCHECK_EQ(interface_method, + Runtime::Current()->GetClassLinker()->FindMethodForProxy(klass, this)); + interface_method->VisitRoots(visitor, pointer_size); + } + visitor.VisitRoot(declaring_class_.AddressWithoutBarrier()); + if (!IsNative()) { + ProfilingInfo* profiling_info = GetProfilingInfo(pointer_size); + if (profiling_info != nullptr) { + profiling_info->VisitRoots(visitor); + } } } } diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index fcfa457bf7..31c278e748 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -36,6 +36,8 @@ namespace art { namespace jit { +static constexpr bool kEnableOnStackReplacement = false; + JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& options) { auto* jit_options = new JitOptions; jit_options->use_jit_ = options.GetOrDefault(RuntimeArgumentMap::UseJIT); @@ -278,6 +280,10 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread, uint32_t dex_pc, int32_t dex_pc_offset, JValue* result) { + if (!kEnableOnStackReplacement) { + return false; + } + Jit* jit = Runtime::Current()->GetJit(); if (jit == nullptr) { return false; diff --git a/runtime/openjdkjvm/Android.mk b/runtime/openjdkjvm/Android.mk new file mode 100644 index 0000000000..9b7404ebf5 --- /dev/null +++ b/runtime/openjdkjvm/Android.mk @@ -0,0 +1,20 @@ +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := openjdkjvm-phony +include $(BUILD_PHONY_PACKAGE) diff --git a/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION b/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/runtime/openjdkjvm/MODULE_LICENSE_GPL_WITH_CLASSPATH_EXCEPTION diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index f2cba478c1..1b710d8142 100644 --- a/test/458-checker-instruction-simplification/src/Main.java +++ b/test/458-checker-instruction-simplification/src/Main.java @@ -46,6 +46,12 @@ public class Main { } } + public static void assertStringEquals(String expected, String result) { + if (expected == null ? result != null : !expected.equals(result)) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + /** * Tiny programs exercising optimizations of arithmetic identities. */ @@ -1433,6 +1439,278 @@ public class Main { return ((d > 42.0) != false) ? 13 : 54; } + /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] + /// CHECK-DAG: Return [<<Int>>] + + /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: Return [<<Arg>>] + + /// CHECK-START: int Main.intToDoubleToInt(int) instruction_simplifier (after) + /// CHECK-NOT: TypeConversion + + public static int intToDoubleToInt(int value) { + // Lossless conversion followed by a conversion back. + return (int) (double) value; + } + + /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>] + + /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: {{d\d+}} TypeConversion [<<Arg>>] + + /// CHECK-START: java.lang.String Main.intToDoubleToIntPrint(int) instruction_simplifier (after) + /// CHECK-DAG: TypeConversion + /// CHECK-NOT: TypeConversion + + public static String intToDoubleToIntPrint(int value) { + // Lossless conversion followed by a conversion back + // with another use of the intermediate result. + double d = (double) value; + int i = (int) d; + return "d=" + d + ", i=" + i; + } + + /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:b\d+>> ParameterValue + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] + /// CHECK-DAG: Return [<<Int>>] + + /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:b\d+>> ParameterValue + /// CHECK-DAG: Return [<<Arg>>] + + /// CHECK-START: int Main.byteToDoubleToInt(byte) instruction_simplifier (after) + /// CHECK-NOT: TypeConversion + + public static int byteToDoubleToInt(byte value) { + // Lossless conversion followed by another conversion, use implicit conversion. + return (int) (double) value; + } + + /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:f\d+>> ParameterValue + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] + /// CHECK-DAG: Return [<<Int>>] + + /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:f\d+>> ParameterValue + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: Return [<<Int>>] + + /// CHECK-START: int Main.floatToDoubleToInt(float) instruction_simplifier (after) + /// CHECK-DAG: TypeConversion + /// CHECK-NOT: TypeConversion + + public static int floatToDoubleToInt(float value) { + // Lossless conversion followed by another conversion. + return (int) (double) value; + } + + /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:f\d+>> ParameterValue + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>] + + /// CHECK-START: java.lang.String Main.floatToDoubleToIntPrint(float) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:f\d+>> ParameterValue + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: {{i\d+}} TypeConversion [<<Double>>] + + public static String floatToDoubleToIntPrint(float value) { + // Lossless conversion followed by another conversion with + // an extra use of the intermediate result. + double d = (double) value; + int i = (int) d; + return "d=" + d + ", i=" + i; + } + + /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:b\d+>> ParameterValue + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] + /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>] + /// CHECK-DAG: Return [<<Short>>] + + /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:b\d+>> ParameterValue + /// CHECK-DAG: Return [<<Arg>>] + + /// CHECK-START: short Main.byteToDoubleToShort(byte) instruction_simplifier (after) + /// CHECK-NOT: TypeConversion + + public static short byteToDoubleToShort(byte value) { + // Originally, this is byte->double->int->short. The first conversion is lossless, + // so we merge this with the second one to byte->int which we omit as it's an implicit + // conversion. Then we eliminate the resulting byte->short as an implicit conversion. + return (short) (double) value; + } + + /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:c\d+>> ParameterValue + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Double>>] + /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>] + /// CHECK-DAG: Return [<<Short>>] + + /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:c\d+>> ParameterValue + /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: Return [<<Short>>] + + /// CHECK-START: short Main.charToDoubleToShort(char) instruction_simplifier (after) + /// CHECK-DAG: TypeConversion + /// CHECK-NOT: TypeConversion + + public static short charToDoubleToShort(char value) { + // Originally, this is char->double->int->short. The first conversion is lossless, + // so we merge this with the second one to char->int which we omit as it's an implicit + // conversion. Then we are left with the resulting char->short conversion. + return (short) (double) value; + } + + /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:f\d+>> ParameterValue + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>] + /// CHECK-DAG: Return [<<Short>>] + + /// CHECK-START: short Main.floatToIntToShort(float) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:f\d+>> ParameterValue + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Int>>] + /// CHECK-DAG: Return [<<Short>>] + + public static short floatToIntToShort(float value) { + // Lossy FP to integral conversion followed by another conversion: no simplification. + return (short) value; + } + + /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>] + /// CHECK-DAG: Return [<<Int>>] + + /// CHECK-START: int Main.intToFloatToInt(int) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:i\d+>> ParameterValue + /// CHECK-DAG: <<Float:f\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Float>>] + /// CHECK-DAG: Return [<<Int>>] + + public static int intToFloatToInt(int value) { + // Lossy integral to FP conversion followed another conversion: no simplification. + return (int) (float) value; + } + + /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:j\d+>> ParameterValue + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>] + /// CHECK-DAG: Return [<<Double>>] + + /// CHECK-START: double Main.longToIntToDouble(long) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:j\d+>> ParameterValue + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Int>>] + /// CHECK-DAG: Return [<<Double>>] + + public static double longToIntToDouble(long value) { + // Lossy long-to-int conversion followed an integral to FP conversion: no simplification. + return (double) (int) value; + } + + /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:j\d+>> ParameterValue + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>] + /// CHECK-DAG: Return [<<Long>>] + + /// CHECK-START: long Main.longToIntToLong(long) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:j\d+>> ParameterValue + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Int>>] + /// CHECK-DAG: Return [<<Long>>] + + public static long longToIntToLong(long value) { + // Lossy long-to-int conversion followed an int-to-long conversion: no simplification. + return (long) (int) value; + } + + /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:s\d+>> ParameterValue + /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Short:s\d+>> TypeConversion [<<Char>>] + /// CHECK-DAG: Return [<<Short>>] + + /// CHECK-START: short Main.shortToCharToShort(short) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:s\d+>> ParameterValue + /// CHECK-DAG: Return [<<Arg>>] + + public static short shortToCharToShort(short value) { + // Integral conversion followed by non-widening integral conversion to original type. + return (short) (char) value; + } + + /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:s\d+>> ParameterValue + /// CHECK-DAG: <<Long:j\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Int:i\d+>> TypeConversion [<<Long>>] + /// CHECK-DAG: Return [<<Int>>] + + /// CHECK-START: int Main.shortToLongToInt(short) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:s\d+>> ParameterValue + /// CHECK-DAG: Return [<<Arg>>] + + public static int shortToLongToInt(short value) { + // Integral conversion followed by non-widening integral conversion, use implicit conversion. + return (int) (long) value; + } + + /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:s\d+>> ParameterValue + /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Char>>] + /// CHECK-DAG: Return [<<Byte>>] + + /// CHECK-START: byte Main.shortToCharToByte(short) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:s\d+>> ParameterValue + /// CHECK-DAG: <<Byte:b\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: Return [<<Byte>>] + + public static byte shortToCharToByte(short value) { + // Integral conversion followed by non-widening integral conversion losing bits + // from the original type. Simplify to use only one conversion. + return (byte) (char) value; + } + + /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (before) + /// CHECK-DAG: <<Arg:s\d+>> ParameterValue + /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>] + + /// CHECK-START: java.lang.String Main.shortToCharToBytePrint(short) instruction_simplifier (after) + /// CHECK-DAG: <<Arg:s\d+>> ParameterValue + /// CHECK-DAG: <<Char:c\d+>> TypeConversion [<<Arg>>] + /// CHECK-DAG: {{b\d+}} TypeConversion [<<Char>>] + + public static String shortToCharToBytePrint(short value) { + // Integral conversion followed by non-widening integral conversion losing bits + // from the original type with an extra use of the intermediate result. + char c = (char) value; + byte b = (byte) c; + return "c=" + ((int) c) + ", b=" + ((int) b); // implicit conversions. + } + public static void main(String[] args) { int arg = 123456; @@ -1518,6 +1796,46 @@ public class Main { assertIntEquals(floatConditionNotEqualOne(43.0f), 13); assertIntEquals(doubleConditionEqualZero(6.0), 54); assertIntEquals(doubleConditionEqualZero(43.0), 13); + + assertIntEquals(intToDoubleToInt(1234567), 1234567); + assertIntEquals(intToDoubleToInt(Integer.MIN_VALUE), Integer.MIN_VALUE); + assertIntEquals(intToDoubleToInt(Integer.MAX_VALUE), Integer.MAX_VALUE); + assertStringEquals(intToDoubleToIntPrint(7654321), "d=7654321.0, i=7654321"); + assertIntEquals(byteToDoubleToInt((byte) 12), 12); + assertIntEquals(byteToDoubleToInt(Byte.MIN_VALUE), Byte.MIN_VALUE); + assertIntEquals(byteToDoubleToInt(Byte.MAX_VALUE), Byte.MAX_VALUE); + assertIntEquals(floatToDoubleToInt(11.3f), 11); + assertStringEquals(floatToDoubleToIntPrint(12.25f), "d=12.25, i=12"); + assertIntEquals(byteToDoubleToShort((byte) 123), 123); + assertIntEquals(byteToDoubleToShort(Byte.MIN_VALUE), Byte.MIN_VALUE); + assertIntEquals(byteToDoubleToShort(Byte.MAX_VALUE), Byte.MAX_VALUE); + assertIntEquals(charToDoubleToShort((char) 1234), 1234); + assertIntEquals(charToDoubleToShort(Character.MIN_VALUE), Character.MIN_VALUE); + assertIntEquals(charToDoubleToShort(Character.MAX_VALUE), /* sign-extended */ -1); + assertIntEquals(floatToIntToShort(12345.75f), 12345); + assertIntEquals(floatToIntToShort((float)(Short.MIN_VALUE - 1)), Short.MAX_VALUE); + assertIntEquals(floatToIntToShort((float)(Short.MAX_VALUE + 1)), Short.MIN_VALUE); + assertIntEquals(intToFloatToInt(-54321), -54321); + assertDoubleEquals(longToIntToDouble(0x1234567812345678L), (double) 0x12345678); + assertDoubleEquals(longToIntToDouble(Long.MIN_VALUE), 0.0); + assertDoubleEquals(longToIntToDouble(Long.MAX_VALUE), -1.0); + assertLongEquals(longToIntToLong(0x1234567812345678L), 0x0000000012345678L); + assertLongEquals(longToIntToLong(0x1234567887654321L), 0xffffffff87654321L); + assertLongEquals(longToIntToLong(Long.MIN_VALUE), 0L); + assertLongEquals(longToIntToLong(Long.MAX_VALUE), -1L); + assertIntEquals(shortToCharToShort((short) -5678), (short) -5678); + assertIntEquals(shortToCharToShort(Short.MIN_VALUE), Short.MIN_VALUE); + assertIntEquals(shortToCharToShort(Short.MAX_VALUE), Short.MAX_VALUE); + assertIntEquals(shortToLongToInt((short) 5678), 5678); + assertIntEquals(shortToLongToInt(Short.MIN_VALUE), Short.MIN_VALUE); + assertIntEquals(shortToLongToInt(Short.MAX_VALUE), Short.MAX_VALUE); + assertIntEquals(shortToCharToByte((short) 0x1234), 0x34); + assertIntEquals(shortToCharToByte((short) 0x12f0), -0x10); + assertIntEquals(shortToCharToByte(Short.MIN_VALUE), 0); + assertIntEquals(shortToCharToByte(Short.MAX_VALUE), -1); + assertStringEquals(shortToCharToBytePrint((short) 1025), "c=1025, b=1"); + assertStringEquals(shortToCharToBytePrint((short) 1023), "c=1023, b=-1"); + assertStringEquals(shortToCharToBytePrint((short) -1), "c=65535, b=-1"); } public static boolean booleanField; diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java index decdd1f324..8d73d69db9 100644 --- a/test/551-checker-shifter-operand/src/Main.java +++ b/test/551-checker-shifter-operand/src/Main.java @@ -241,13 +241,14 @@ public class Main { /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after) /// CHECK: Arm64DataProcWithShifterOp - /// CHECK: Arm64DataProcWithShifterOp + /// CHECK-NOT: Arm64DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendByteInt1(int, byte) instruction_simplifier_arm64 (after) /// CHECK-NOT: TypeConversion public static void $opt$validateExtendByteInt1(int a, byte b) { assertIntEquals(a + $noinline$byteToChar (b), a + (char)b); + // Conversions byte->short and short->int are implicit; nothing to merge. assertIntEquals(a + $noinline$byteToShort(b), a + (short)b); } @@ -266,17 +267,24 @@ public class Main { /// CHECK: Arm64DataProcWithShifterOp /// CHECK: Arm64DataProcWithShifterOp /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: Arm64DataProcWithShifterOp + /// CHECK: Arm64DataProcWithShifterOp + /// CHECK-NOT: Arm64DataProcWithShifterOp /// CHECK-START-ARM64: void Main.$opt$validateExtendByteLong(long, byte) instruction_simplifier_arm64 (after) /// CHECK: TypeConversion - /// CHECK: TypeConversion /// CHECK-NOT: TypeConversion public static void $opt$validateExtendByteLong(long a, byte b) { - // The first two tests have a type conversion. + // In each of the following tests, there will be a merge on the LHS. + + // The first test has an explicit byte->char conversion on RHS, + // followed by a conversion that is merged with the Add. assertLongEquals(a + $noinline$byteToChar (b), a + (char)b); + // Since conversions byte->short and byte->int are implicit, the RHS + // for the two tests below is the same and one is eliminated by GVN. + // The other is then merged to a shifter operand instruction. assertLongEquals(a + $noinline$byteToShort(b), a + (short)b); - // This test does not because the conversion to `int` is optimized away. assertLongEquals(a + $noinline$byteToInt (b), a + (int)b); } diff --git a/test/570-checker-select/src/Main.java b/test/570-checker-select/src/Main.java index ec60240e90..8a4cf603af 100644 --- a/test/570-checker-select/src/Main.java +++ b/test/570-checker-select/src/Main.java @@ -19,6 +19,11 @@ public class Main { /// CHECK-START: int Main.BoolCond_IntVarVar(boolean, int, int) register (after) /// CHECK: Select [{{i\d+}},{{i\d+}},{{z\d+}}] + /// CHECK-START-ARM64: int Main.BoolCond_IntVarVar(boolean, int, int) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csel ne + /// CHECK-START-X86_64: int Main.BoolCond_IntVarVar(boolean, int, int) disassembly (after) /// CHECK: <<Cond:z\d+>> ParameterValue /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] @@ -31,6 +36,11 @@ public class Main { /// CHECK-START: int Main.BoolCond_IntVarCst(boolean, int) register (after) /// CHECK: Select [{{i\d+}},{{i\d+}},{{z\d+}}] + /// CHECK-START-ARM64: int Main.BoolCond_IntVarCst(boolean, int) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csinc ne + /// CHECK-START-X86_64: int Main.BoolCond_IntVarCst(boolean, int) disassembly (after) /// CHECK: <<Cond:z\d+>> ParameterValue /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] @@ -43,6 +53,11 @@ public class Main { /// CHECK-START: int Main.BoolCond_IntCstVar(boolean, int) register (after) /// CHECK: Select [{{i\d+}},{{i\d+}},{{z\d+}}] + /// CHECK-START-ARM64: int Main.BoolCond_IntCstVar(boolean, int) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csinc eq + /// CHECK-START-X86_64: int Main.BoolCond_IntCstVar(boolean, int) disassembly (after) /// CHECK: <<Cond:z\d+>> ParameterValue /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] @@ -55,6 +70,11 @@ public class Main { /// CHECK-START: long Main.BoolCond_LongVarVar(boolean, long, long) register (after) /// CHECK: Select [{{j\d+}},{{j\d+}},{{z\d+}}] + /// CHECK-START-ARM64: long Main.BoolCond_LongVarVar(boolean, long, long) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csel ne + /// CHECK-START-X86_64: long Main.BoolCond_LongVarVar(boolean, long, long) disassembly (after) /// CHECK: <<Cond:z\d+>> ParameterValue /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] @@ -67,6 +87,11 @@ public class Main { /// CHECK-START: long Main.BoolCond_LongVarCst(boolean, long) register (after) /// CHECK: Select [{{j\d+}},{{j\d+}},{{z\d+}}] + /// CHECK-START-ARM64: long Main.BoolCond_LongVarCst(boolean, long) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csinc ne + /// CHECK-START-X86_64: long Main.BoolCond_LongVarCst(boolean, long) disassembly (after) /// CHECK: <<Cond:z\d+>> ParameterValue /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] @@ -79,6 +104,11 @@ public class Main { /// CHECK-START: long Main.BoolCond_LongCstVar(boolean, long) register (after) /// CHECK: Select [{{j\d+}},{{j\d+}},{{z\d+}}] + /// CHECK-START-ARM64: long Main.BoolCond_LongCstVar(boolean, long) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csinc eq + /// CHECK-START-X86_64: long Main.BoolCond_LongCstVar(boolean, long) disassembly (after) /// CHECK: <<Cond:z\d+>> ParameterValue /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] @@ -91,6 +121,11 @@ public class Main { /// CHECK-START: float Main.BoolCond_FloatVarVar(boolean, float, float) register (after) /// CHECK: Select [{{f\d+}},{{f\d+}},{{z\d+}}] + /// CHECK-START-ARM64: float Main.BoolCond_FloatVarVar(boolean, float, float) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: fcsel ne + public static float BoolCond_FloatVarVar(boolean cond, float x, float y) { return cond ? x : y; } @@ -98,6 +133,11 @@ public class Main { /// CHECK-START: float Main.BoolCond_FloatVarCst(boolean, float) register (after) /// CHECK: Select [{{f\d+}},{{f\d+}},{{z\d+}}] + /// CHECK-START-ARM64: float Main.BoolCond_FloatVarCst(boolean, float) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: fcsel ne + public static float BoolCond_FloatVarCst(boolean cond, float x) { return cond ? x : 1.0f; } @@ -105,6 +145,11 @@ public class Main { /// CHECK-START: float Main.BoolCond_FloatCstVar(boolean, float) register (after) /// CHECK: Select [{{f\d+}},{{f\d+}},{{z\d+}}] + /// CHECK-START-ARM64: float Main.BoolCond_FloatCstVar(boolean, float) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: fcsel ne + public static float BoolCond_FloatCstVar(boolean cond, float y) { return cond ? 1.0f : y; } @@ -113,6 +158,11 @@ public class Main { /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK-START-ARM64: int Main.IntNonmatCond_IntVarVar(int, int, int, int) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csel le + /// CHECK-START-X86_64: int Main.IntNonmatCond_IntVarVar(int, int, int, int) disassembly (after) /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>] @@ -127,6 +177,13 @@ public class Main { /// CHECK-NEXT: <<Sel:i\d+>> Select [{{i\d+}},{{i\d+}},{{z\d+}}] /// CHECK-NEXT: Add [<<Cond>>,<<Sel>>] + /// CHECK-START-ARM64: int Main.IntMatCond_IntVarVar(int, int, int, int) disassembly (after) + /// CHECK: LessThanOrEqual + /// CHECK-NEXT: cmp + /// CHECK-NEXT: cset le + /// CHECK: Select + /// CHECK-NEXT: csel le + /// CHECK-START-X86_64: int Main.IntMatCond_IntVarVar(int, int, int, int) disassembly (after) /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] /// CHECK: Select [{{i\d+}},{{i\d+}},<<Cond>>] @@ -141,6 +198,11 @@ public class Main { /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK-START-ARM64: long Main.IntNonmatCond_LongVarVar(int, int, long, long) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csel le + /// CHECK-START-X86_64: long Main.IntNonmatCond_LongVarVar(int, int, long, long) disassembly (after) /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] /// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>] @@ -156,6 +218,13 @@ public class Main { /// CHECK: <<Sel2:j\d+>> Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: Add [<<Sel2>>,<<Sel1>>] + /// CHECK-START-ARM64: long Main.IntMatCond_LongVarVar(int, int, long, long) disassembly (after) + /// CHECK: LessThanOrEqual + /// CHECK-NEXT: cmp + /// CHECK-NEXT: cset le + /// CHECK: Select + /// CHECK-NEXT: csel le + /// CHECK-START-X86_64: long Main.IntMatCond_LongVarVar(int, int, long, long) disassembly (after) /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{i\d+}},{{i\d+}}] /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] @@ -172,6 +241,11 @@ public class Main { /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}] /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] + /// CHECK-START-ARM64: long Main.LongNonmatCond_LongVarVar(long, long, long, long) disassembly (after) + /// CHECK: Select + /// CHECK-NEXT: cmp + /// CHECK-NEXT: csel le + /// CHECK-START-X86_64: long Main.LongNonmatCond_LongVarVar(long, long, long, long) disassembly (after) /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}] /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] @@ -187,6 +261,13 @@ public class Main { /// CHECK: <<Sel2:j\d+>> Select [{{j\d+}},{{j\d+}},<<Cond>>] /// CHECK: Add [<<Sel2>>,<<Sel1>>] + /// CHECK-START-ARM64: long Main.LongMatCond_LongVarVar(long, long, long, long) disassembly (after) + /// CHECK: LessThanOrEqual + /// CHECK-NEXT: cmp + /// CHECK-NEXT: cset le + /// CHECK: Select + /// CHECK-NEXT: csel le + /// CHECK-START-X86_64: long Main.LongMatCond_LongVarVar(long, long, long, long) disassembly (after) /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{j\d+}},{{j\d+}}] /// CHECK: Select [{{j\d+}},{{j\d+}},<<Cond>>] @@ -203,6 +284,12 @@ public class Main { /// CHECK: <<Cond:z\d+>> LessThanOrEqual [{{f\d+}},{{f\d+}}] /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK-START-ARM64: int Main.FloatLtNonmatCond_IntVarVar(float, float, int, int) disassembly (after) + /// CHECK: LessThanOrEqual + /// CHECK: Select + /// CHECK-NEXT: fcmp + /// CHECK-NEXT: csel le + public static int FloatLtNonmatCond_IntVarVar(float a, float b, int x, int y) { return a > b ? x : y; } @@ -211,6 +298,12 @@ public class Main { /// CHECK: <<Cond:z\d+>> GreaterThanOrEqual [{{f\d+}},{{f\d+}}] /// CHECK-NEXT: Select [{{i\d+}},{{i\d+}},<<Cond>>] + /// CHECK-START-ARM64: int Main.FloatGtNonmatCond_IntVarVar(float, float, int, int) disassembly (after) + /// CHECK: GreaterThanOrEqual + /// CHECK: Select + /// CHECK-NEXT: fcmp + /// CHECK-NEXT: csel hs + public static int FloatGtNonmatCond_IntVarVar(float a, float b, int x, int y) { return a < b ? x : y; } @@ -219,6 +312,12 @@ public class Main { /// CHECK: <<Cond:z\d+>> GreaterThanOrEqual [{{f\d+}},{{f\d+}}] /// CHECK-NEXT: Select [{{f\d+}},{{f\d+}},<<Cond>>] + /// CHECK-START-ARM64: float Main.FloatGtNonmatCond_FloatVarVar(float, float, float, float) disassembly (after) + /// CHECK: GreaterThanOrEqual + /// CHECK: Select + /// CHECK-NEXT: fcmp + /// CHECK-NEXT: fcsel hs + public static float FloatGtNonmatCond_FloatVarVar(float a, float b, float x, float y) { return a < b ? x : y; } @@ -228,6 +327,13 @@ public class Main { /// CHECK-NEXT: <<Sel:i\d+>> Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK-NEXT: Add [<<Cond>>,<<Sel>>] + /// CHECK-START-ARM64: int Main.FloatLtMatCond_IntVarVar(float, float, int, int) disassembly (after) + /// CHECK: LessThanOrEqual + /// CHECK-NEXT: fcmp + /// CHECK-NEXT: cset le + /// CHECK: Select + /// CHECK-NEXT: csel le + public static int FloatLtMatCond_IntVarVar(float a, float b, int x, int y) { int result = (a > b ? x : y); return result + (a > b ? 0 : 1); @@ -238,6 +344,13 @@ public class Main { /// CHECK-NEXT: <<Sel:i\d+>> Select [{{i\d+}},{{i\d+}},<<Cond>>] /// CHECK-NEXT: Add [<<Cond>>,<<Sel>>] + /// CHECK-START-ARM64: int Main.FloatGtMatCond_IntVarVar(float, float, int, int) disassembly (after) + /// CHECK: GreaterThanOrEqual + /// CHECK-NEXT: fcmp + /// CHECK-NEXT: cset hs + /// CHECK: Select + /// CHECK-NEXT: csel hs + public static int FloatGtMatCond_IntVarVar(float a, float b, int x, int y) { int result = (a < b ? x : y); return result + (a < b ? 0 : 1); @@ -248,6 +361,13 @@ public class Main { /// CHECK-NEXT: <<Sel:f\d+>> Select [{{f\d+}},{{f\d+}},<<Cond>>] /// CHECK-NEXT: TypeConversion [<<Cond>>] + /// CHECK-START-ARM64: float Main.FloatGtMatCond_FloatVarVar(float, float, float, float) disassembly (after) + /// CHECK: GreaterThanOrEqual + /// CHECK-NEXT: fcmp + /// CHECK-NEXT: cset hs + /// CHECK: Select + /// CHECK-NEXT: fcsel hs + public static float FloatGtMatCond_FloatVarVar(float a, float b, float x, float y) { float result = (a < b ? x : y); return result + (a < b ? 0 : 1); diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index b3560b634b..7c71ce3c6a 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -446,7 +446,9 @@ TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := # Known broken tests for the JIT. # CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT # also uses Generic JNI instead of the JNI compiler. +# Disable 570 while investigating OSR issues. TEST_ART_BROKEN_JIT_RUN_TESTS := \ + 570-checker-osr \ 137-cfi ifneq (,$(filter jit,$(COMPILER_TYPES))) diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh index 9e02ce2f90..2eb52bcad9 100755 --- a/tools/buildbot-build.sh +++ b/tools/buildbot-build.sh @@ -21,7 +21,7 @@ fi out_dir=${OUT_DIR-out} java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES -common_targets="vogar ${java_libraries_dir}/core-tests_intermediates/javalib.jar apache-harmony-jdwp-tests-hostdex ${java_libraries_dir}/jsr166-tests_intermediates/javalib.jar ${out_dir}/host/linux-x86/bin/jack" +common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests ${out_dir}/host/linux-x86/bin/jack" mode="target" j_arg="-j$(nproc)" showcommands= diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index f346239763..45fb4b4dec 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -20,13 +20,13 @@ if [ ! -d libcore ]; then fi # Jar containing jsr166 tests. -jsr166_test_jar=${OUT_DIR-out}/target/common/obj/JAVA_LIBRARIES/jsr166-tests_intermediates/javalib.jar +jsr166_test_jack=${OUT_DIR-out}/target/common/obj/JAVA_LIBRARIES/jsr166-tests_intermediates/classes.jack # Jar containing all the other tests. -test_jar=${OUT_DIR-out}/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/javalib.jar +test_jack=${OUT_DIR-out}/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/classes.jack -if [ ! -f $test_jar ]; then +if [ ! -f $test_jack ]; then echo "Before running, you must build core-tests, jsr166-tests and vogar: \ make core-tests jsr166-tests vogar vogar.jar" exit 1 @@ -108,7 +108,11 @@ done # the default timeout. vogar_args="$vogar_args --timeout 480" +# Use Jack with "1.8" configuration. +export JACK_VERSION=`basename prebuilts/sdk/tools/jacks/*ALPHA* | sed 's/^jack-//' | sed 's/.jar$//'` +vogar_args="$vogar_args --toolchain jack --language JN" + # Run the tests using vogar. echo "Running tests for the following test packages:" echo ${working_packages[@]} | tr " " "\n" -vogar $vogar_args --vm-arg -Xusejit:true $expectations --classpath $jsr166_test_jar --classpath $test_jar ${working_packages[@]} +vogar $vogar_args --vm-arg -Xusejit:true $expectations --classpath $jsr166_test_jack --classpath $test_jack ${working_packages[@]} |