diff options
author | 2023-11-24 12:02:13 +0100 | |
---|---|---|
committer | 2023-11-27 11:43:58 +0000 | |
commit | 7f515181fdbe88a139d973d3481964d9cb7ed118 (patch) | |
tree | dcd9802bb4eed26591d69972e605d27c68164f98 | |
parent | db8d042c6dc12d9d1ba5ff2526ccc86a077570ca (diff) |
Simplify boxing followed by unboxing.
Also mark boxing `valueOf()` intrinsics as never null
to avoid creating unnecessary `HNullCheck` instructions.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Change-Id: I86e7721e3af6c59407aa2ddfc1bd11bd2fdac83c
-rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 32 | ||||
-rw-r--r-- | compiler/optimizing/nodes.cc | 16 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 4 | ||||
-rw-r--r-- | test/640-checker-integer-valueof/src/Main.java | 190 |
4 files changed, 239 insertions, 3 deletions
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index fb6d53b9da..3328f3babe 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -22,6 +22,7 @@ #include "data_type-inl.h" #include "driver/compiler_options.h" #include "escape.h" +#include "intrinsic_objects.h" #include "intrinsics.h" #include "intrinsics_utils.h" #include "mirror/class-inl.h" @@ -30,6 +31,7 @@ #include "scoped_thread_state_change-inl.h" #include "sharpening.h" #include "string_builder_append.h" +#include "well_known_classes.h" namespace art HIDDEN { @@ -114,6 +116,7 @@ class InstructionSimplifierVisitor final : public HGraphDelegateVisitor { void VisitDeoptimize(HDeoptimize* deoptimize) override; void VisitVecMul(HVecMul* instruction) override; void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* instruction) override; + void SimplifyBoxUnbox(HInvoke* instruction, ArtField* field, DataType::Type type); void SimplifySystemArrayCopy(HInvoke* invoke); void SimplifyStringEquals(HInvoke* invoke); void SimplifyFP2Int(HInvoke* invoke); @@ -2407,6 +2410,29 @@ void InstructionSimplifierVisitor::VisitXor(HXor* instruction) { TryHandleAssociativeAndCommutativeOperation(instruction); } +void InstructionSimplifierVisitor::SimplifyBoxUnbox( + HInvoke* instruction, ArtField* field, DataType::Type type) { + DCHECK(instruction->GetIntrinsic() == Intrinsics::kByteValueOf || + instruction->GetIntrinsic() == Intrinsics::kShortValueOf || + instruction->GetIntrinsic() == Intrinsics::kCharacterValueOf || + instruction->GetIntrinsic() == Intrinsics::kIntegerValueOf); + const HUseList<HInstruction*>& uses = instruction->GetUses(); + for (auto it = uses.begin(), end = uses.end(); it != end;) { + HInstruction* user = it->GetUser(); + ++it; // Increment the iterator before we potentially remove the node from the list. + if (user->IsInstanceFieldGet() && + user->AsInstanceFieldGet()->GetFieldInfo().GetField() == field && + // Note: Due to other simplifications, we may have an `HInstanceFieldGet` with + // a different type (Int8 vs. Uint8, Int16 vs. Uint16) for the same field. + // Do not optimize that case for now. (We would need to insert a `HTypeConversion`.) + user->GetType() == type) { + user->ReplaceWith(instruction->InputAt(0)); + RecordSimplification(); + // Do not remove `user` while we're iterating over the block's instructions. Let DCE do it. + } + } +} + void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { HInstruction* argument = instruction->InputAt(1); HInstruction* receiver = instruction->InputAt(0); @@ -3058,6 +3084,12 @@ bool InstructionSimplifierVisitor::CanUseKnownBootImageVarHandle(HInvoke* invoke void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { switch (instruction->GetIntrinsic()) { +#define SIMPLIFY_BOX_UNBOX(name, low, high, type, start_index) \ + case Intrinsics::k ## name ## ValueOf: \ + SimplifyBoxUnbox(instruction, WellKnownClasses::java_lang_##name##_value, type); \ + break; + BOXED_TYPES(SIMPLIFY_BOX_UNBOX) +#undef SIMPLIFY_BOX_UNBOX case Intrinsics::kStringEquals: SimplifyStringEquals(instruction); break; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 53ed49a307..2cfe5b3ae2 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -35,6 +35,7 @@ #include "class_root-inl.h" #include "code_generator.h" #include "common_dominator.h" +#include "intrinsic_objects.h" #include "intrinsics.h" #include "intrinsics_list.h" #include "mirror/class-inl.h" @@ -3363,6 +3364,21 @@ std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::ClinitCheckReq } } +bool HInvokeStaticOrDirect::CanBeNull() const { + if (GetType() != DataType::Type::kReference || IsStringInit()) { + return false; + } + switch (GetIntrinsic()) { +#define DEFINE_BOXED_CASE(name, unused1, unused2, unused3, unused4) \ + case Intrinsics::k##name##ValueOf: \ + return false; + BOXED_TYPES(DEFINE_BOXED_CASE) +#undef DEFINE_BOXED_CASE + default: + return true; + } +} + bool HInvokeVirtual::CanDoImplicitNullCheckOn(HInstruction* obj) const { if (obj != InputAt(0)) { return false; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 04d51328f2..4db72a68e3 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -5065,9 +5065,7 @@ class HInvokeStaticOrDirect final : public HInvoke { return false; } - bool CanBeNull() const override { - return GetType() == DataType::Type::kReference && !IsStringInit(); - } + bool CanBeNull() const override; MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; } CodePtrLocation GetCodePtrLocation() const { diff --git a/test/640-checker-integer-valueof/src/Main.java b/test/640-checker-integer-valueof/src/Main.java index 0837fd18ee..cf0d7d8774 100644 --- a/test/640-checker-integer-valueof/src/Main.java +++ b/test/640-checker-integer-valueof/src/Main.java @@ -48,6 +48,178 @@ public class Main { return Integer.valueOf(55555); } + /// CHECK-START: byte Main.$noinline$boxUnboxByte(byte) builder (after) + /// CHECK: <<Input:b\d+>> ParameterValue + /// CHECK: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK-NOT: NullCheck [<<Boxed>>] + /// CHECK: <<Unboxed:b\d+>> InvokeVirtual [<<Boxed>>] method_name:java.lang.Byte.byteValue + /// CHECK: Return [<<Unboxed>>] + + /// CHECK-START: byte Main.$noinline$boxUnboxByte(byte) inliner (after) + /// CHECK: <<Input:b\d+>> ParameterValue + /// CHECK: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK: <<Unboxed:b\d+>> InstanceFieldGet [<<Boxed>>] field_name:java.lang.Byte.value + /// CHECK: Return [<<Unboxed>>] + + /// CHECK-START: byte Main.$noinline$boxUnboxByte(byte) instruction_simplifier$after_inlining (after) + /// CHECK: <<Input:b\d+>> ParameterValue + /// CHECK: Return [<<Input>>] + + /// CHECK-START: byte Main.$noinline$boxUnboxByte(byte) dead_code_elimination$after_inlining (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: InstanceFieldGet + + public static byte $noinline$boxUnboxByte(byte value) { + return Byte.valueOf(value).byteValue(); + } + + /// CHECK-START: short Main.$noinline$boxUnboxShort(short) builder (after) + /// CHECK: <<Input:s\d+>> ParameterValue + /// CHECK: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Short.valueOf intrinsic:ShortValueOf + /// CHECK-NOT: NullCheck [<<Boxed>>] + /// CHECK: <<Unboxed:s\d+>> InvokeVirtual [<<Boxed>>] method_name:java.lang.Short.shortValue + /// CHECK: Return [<<Unboxed>>] + + /// CHECK-START: short Main.$noinline$boxUnboxShort(short) inliner (after) + /// CHECK: <<Input:s\d+>> ParameterValue + /// CHECK: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Short.valueOf intrinsic:ShortValueOf + /// CHECK: <<Unboxed:s\d+>> InstanceFieldGet [<<Boxed>>] field_name:java.lang.Short.value + /// CHECK: Return [<<Unboxed>>] + + /// CHECK-START: short Main.$noinline$boxUnboxShort(short) instruction_simplifier$after_inlining (after) + /// CHECK: <<Input:s\d+>> ParameterValue + /// CHECK: Return [<<Input>>] + + /// CHECK-START: short Main.$noinline$boxUnboxShort(short) dead_code_elimination$after_inlining (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: InstanceFieldGet + + public static short $noinline$boxUnboxShort(short value) { + return Short.valueOf(value).shortValue(); + } + + /// CHECK-START: char Main.$noinline$boxUnboxCharacter(char) builder (after) + /// CHECK: <<Input:c\d+>> ParameterValue + /// CHECK: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Character.valueOf intrinsic:CharacterValueOf + /// CHECK-NOT: NullCheck [<<Boxed>>] + /// CHECK: <<Unboxed:c\d+>> InvokeVirtual [<<Boxed>>] method_name:java.lang.Character.charValue + /// CHECK: Return [<<Unboxed>>] + + /// CHECK-START: char Main.$noinline$boxUnboxCharacter(char) inliner (after) + /// CHECK: <<Input:c\d+>> ParameterValue + /// CHECK: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Character.valueOf intrinsic:CharacterValueOf + /// CHECK: <<Unboxed:c\d+>> InstanceFieldGet [<<Boxed>>] field_name:java.lang.Character.value + /// CHECK: Return [<<Unboxed>>] + + /// CHECK-START: char Main.$noinline$boxUnboxCharacter(char) instruction_simplifier$after_inlining (after) + /// CHECK: <<Input:c\d+>> ParameterValue + /// CHECK: Return [<<Input>>] + + /// CHECK-START: char Main.$noinline$boxUnboxCharacter(char) dead_code_elimination$after_inlining (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: InstanceFieldGet + + public static char $noinline$boxUnboxCharacter(char value) { + return Character.valueOf(value).charValue(); + } + + /// CHECK-START: int Main.$noinline$boxUnboxInteger(int) builder (after) + /// CHECK: <<Input:i\d+>> ParameterValue + /// CHECK: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Integer.valueOf intrinsic:IntegerValueOf + /// CHECK-NOT: NullCheck [<<Boxed>>] + /// CHECK: <<Unboxed:i\d+>> InvokeVirtual [<<Boxed>>] method_name:java.lang.Integer.intValue + /// CHECK: Return [<<Unboxed>>] + + /// CHECK-START: int Main.$noinline$boxUnboxInteger(int) inliner (after) + /// CHECK: <<Input:i\d+>> ParameterValue + /// CHECK: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Integer.valueOf intrinsic:IntegerValueOf + /// CHECK: <<Unboxed:i\d+>> InstanceFieldGet [<<Boxed>>] field_name:java.lang.Integer.value + /// CHECK: Return [<<Unboxed>>] + + /// CHECK-START: int Main.$noinline$boxUnboxInteger(int) instruction_simplifier$after_inlining (after) + /// CHECK: <<Input:i\d+>> ParameterValue + /// CHECK: Return [<<Input>>] + + /// CHECK-START: int Main.$noinline$boxUnboxInteger(int) dead_code_elimination$after_inlining (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: InstanceFieldGet + + public static int $noinline$boxUnboxInteger(int value) { + return Integer.valueOf(value).intValue(); + } + + /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) builder (after) + /// CHECK-DAG: <<Input:b\d+>> ParameterValue + /// CHECK-DAG: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK-DAG: <<Phi:l\d+>> Phi + /// CHECK-DAG: <<NC:l\d+>> NullCheck [<<Phi>>] + /// CHECK-DAG: <<Unboxed:b\d+>> InvokeVirtual [<<NC>>] method_name:java.lang.Byte.byteValue + /// CHECK-DAG: <<And:i\d+>> And + /// CHECK-DAG: Return [<<And>>] + + /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) instruction_simplifier (after) + /// CHECK-DAG: <<Input:b\d+>> ParameterValue + /// CHECK-DAG: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK-DAG: <<Phi:l\d+>> Phi + /// CHECK-DAG: <<NC:l\d+>> NullCheck [<<Phi>>] + /// CHECK-DAG: <<Unboxed:b\d+>> InvokeVirtual [<<NC>>] method_name:java.lang.Byte.byteValue + /// CHECK-DAG: <<Conv:a\d+>> TypeConversion [<<Unboxed>>] + /// CHECK-DAG: Return [<<Conv>>] + + /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) inliner (after) + /// CHECK-DAG: <<Input:b\d+>> ParameterValue + /// CHECK-DAG: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK-DAG: <<Phi:l\d+>> Phi + /// CHECK-DAG: <<NC:l\d+>> NullCheck [<<Phi>>] + /// CHECK-DAG: <<Unboxed:b\d+>> InstanceFieldGet [<<NC>>] field_name:java.lang.Byte.value + /// CHECK-DAG: <<Conv:a\d+>> TypeConversion [<<Unboxed>>] + /// CHECK-DAG: Return [<<Conv>>] + + /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Input:b\d+>> ParameterValue + /// CHECK-DAG: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK-DAG: <<Phi:l\d+>> Phi + /// CHECK-DAG: <<NC:l\d+>> NullCheck [<<Phi>>] + /// CHECK-DAG: <<Conv:a\d+>> InstanceFieldGet [<<NC>>] field_name:java.lang.Byte.value + /// CHECK-DAG: Return [<<Conv>>] + + /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) dead_code_elimination$after_inlining (after) + /// CHECK-DAG: <<Input:b\d+>> ParameterValue + /// CHECK-DAG: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK-DAG: <<NC:l\d+>> NullCheck [<<Boxed>>] + /// CHECK-DAG: <<Conv:a\d+>> InstanceFieldGet [<<NC>>] field_name:java.lang.Byte.value + /// CHECK-DAG: Return [<<Conv>>] + + /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) instruction_simplifier$after_gvn (after) + /// CHECK-DAG: <<Input:b\d+>> ParameterValue + /// CHECK-DAG: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK-DAG: <<Conv:a\d+>> InstanceFieldGet [<<Boxed>>] field_name:java.lang.Byte.value + /// CHECK-DAG: Return [<<Conv>>] + + /// CHECK-START: int Main.$noinline$boxUnboxByteAsUint8(byte) disassembly (after) + /// CHECK-DAG: <<Input:b\d+>> ParameterValue + /// CHECK-DAG: <<Boxed:l\d+>> InvokeStaticOrDirect [<<Input>>{{(,[ij]\d+)?}}] method_name:java.lang.Byte.valueOf intrinsic:ByteValueOf + /// CHECK-DAG: <<Conv:a\d+>> InstanceFieldGet [<<Boxed>>] field_name:java.lang.Byte.value + /// CHECK-DAG: Return [<<Conv>>] + + public static int $noinline$boxUnboxByteAsUint8(byte value) { + Byte boxed = Byte.valueOf(value); + + // Hide the unboxing from the initial instruction simplifier pass or the one after inlining, + // so that after inlining we can merge the `TypeConversion` into `InstanceFieldGet` with + // a modified type. The Phi shall be eliminated by `dead_code_elimination$after_inlining`. + // The null check that shall be eliminated by `instruction_simplifier$after_gvn (after)` + Byte merged = $inline$returnTrue() ? boxed : null; + + // Due to the type mismatch, we shall not simplify the unboxing. The boxing and unboxing + // shall be kept during subsequent simplifier passes all the way to the disassembly. + return merged.byteValue() & 0xff; + } + + public static boolean $inline$returnTrue() { + return true; + } + public static void main(String[] args) { assertEqual("42", foo(intField)); assertEqual(foo(intField), foo(intField2)); @@ -65,6 +237,18 @@ public class Main { assertEqual("127", foo(intField127)); assertEqual(foo(intField127), foo(intField127)); assertEqual("128", foo(intField128)); + + assertEqual(42, (int) $noinline$boxUnboxByte((byte) 42)); + assertEqual(-42, (int) $noinline$boxUnboxByte((byte) -42)); + assertEqual(42, (int) $noinline$boxUnboxShort((short) 42)); + assertEqual(-42, (int) $noinline$boxUnboxShort((short) -42)); + assertEqual((int) (char) 42, (int) $noinline$boxUnboxCharacter((char) 42)); + assertEqual((int) (char) -42, (int) $noinline$boxUnboxCharacter((char) -42)); + assertEqual(42, $noinline$boxUnboxInteger(42)); + assertEqual(-42, $noinline$boxUnboxInteger(-42)); + + assertEqual(42, $noinline$boxUnboxByteAsUint8((byte) 42)); + assertEqual(-42 & 0xff, $noinline$boxUnboxByteAsUint8((byte) -42)); } static void assertEqual(String a, Integer b) { @@ -79,6 +263,12 @@ public class Main { } } + static void assertEqual(int a, int b) { + if (a != b) { + throw new Error("Expected " + a + ", got " + b); + } + } + static int intField = 42; static int intField2 = 42; static int intField3 = 55555; |