diff options
| -rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 18 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips64.cc | 28 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 50 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 28 | ||||
| -rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 74 | ||||
| -rw-r--r-- | test/458-checker-instruction-simplification/src/Main.java | 318 | ||||
| -rw-r--r-- | test/551-checker-shifter-operand/src/Main.java | 16 |
7 files changed, 505 insertions, 27 deletions
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_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..a5c386d327 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -2363,6 +2363,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 +2383,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 +2462,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 +2558,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 +2568,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 +2585,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 +2595,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 +2743,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 +2753,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/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java index 2f80470cb3..07478b2ac1 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); } |