Tighten art::HNeg type constraints on its input.
Ensure art::HNeg is only passed a type having the kind of
its input. For a boolean, byte, short, or char input, it
means HNeg's type should be int.
Bug: 27684275
Change-Id: Ic8442c62090a8ab65590754874a14a0deb7acd8d
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 11e3689..9c1ded2 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -917,6 +917,19 @@
}
}
+void GraphChecker::VisitNeg(HNeg* instruction) {
+ VisitInstruction(instruction);
+ Primitive::Type input_type = instruction->InputAt(0)->GetType();
+ Primitive::Type result_type = instruction->GetType();
+ if (result_type != Primitive::PrimitiveKind(input_type)) {
+ AddError(StringPrintf("Binary operation %s %d has a result type different "
+ "from its input kind: %s vs %s.",
+ instruction->DebugName(), instruction->GetId(),
+ Primitive::PrettyDescriptor(result_type),
+ Primitive::PrettyDescriptor(input_type)));
+ }
+}
+
void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
VisitInstruction(op);
Primitive::Type lhs_type = op->InputAt(0)->GetType();
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 52252cd..a835084 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -56,6 +56,7 @@
void VisitInstanceOf(HInstanceOf* check) OVERRIDE;
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE;
void VisitLoadException(HLoadException* load) OVERRIDE;
+ void VisitNeg(HNeg* instruction) OVERRIDE;
void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE;
void VisitReturn(HReturn* ret) OVERRIDE;
void VisitReturnVoid(HReturnVoid* ret) OVERRIDE;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 4a8186a..820c696 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1538,7 +1538,10 @@
HInstruction* distance = invoke->InputAt(1);
// Replace the invoke with an HRor.
if (is_left) {
- distance = new (GetGraph()->GetArena()) HNeg(distance->GetType(), distance);
+ // Unconditionally set the type of the negated distance to `int`,
+ // as shift and rotate operations expect a 32-bit (or narrower)
+ // value for their distance input.
+ distance = new (GetGraph()->GetArena()) HNeg(Primitive::kPrimInt, distance);
invoke->GetBlock()->InsertInstructionBefore(distance, invoke);
}
HRor* ror = new (GetGraph()->GetArena()) HRor(type, value, distance);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 46377ee..f63d661 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4148,7 +4148,9 @@
class HNeg : public HUnaryOperation {
public:
HNeg(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc)
- : HUnaryOperation(result_type, input, dex_pc) {}
+ : HUnaryOperation(result_type, input, dex_pc) {
+ DCHECK_EQ(result_type, Primitive::PrimitiveKind(input->GetType()));
+ }
template <typename T> T Compute(T x) const { return -x; }
diff --git a/test/565-checker-rotate/src/Main.java b/test/565-checker-rotate/src/Main.java
index d7f57d8..aadb597 100644
--- a/test/565-checker-rotate/src/Main.java
+++ b/test/565-checker-rotate/src/Main.java
@@ -320,6 +320,48 @@
}
+ /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK: <<ArgVal:i\d+>> ParameterValue
+ /// CHECK: <<ArgDist:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateLeft
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) instruction_simplifier (after)
+ /// CHECK: <<ArgVal:i\d+>> ParameterValue
+ /// CHECK: <<ArgDist:b\d+>> ParameterValue
+ /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>]
+ /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<NegDist>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.rotateLeftIntWithByteDistance(int, byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int rotateLeftIntWithByteDistance(int value, byte distance) {
+ return Integer.rotateLeft(value, distance);
+ }
+
+ /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK: <<ArgVal:i\d+>> ParameterValue
+ /// CHECK: <<ArgDist:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<ArgVal>>,<<ArgDist>>,<<Method>>] intrinsic:IntegerRotateRight
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) instruction_simplifier (after)
+ /// CHECK: <<ArgVal:i\d+>> ParameterValue
+ /// CHECK: <<ArgDist:b\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<ArgDist>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.rotateRightIntWithByteDistance(int, byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int rotateRightIntWithByteDistance(int value, byte distance) {
+ return Integer.rotateRight(value, distance);
+ }
+
+
public static void testRotateLeftBoolean() {
for (int i = 0; i < 40; i++) { // overshoot a bit
int j = i & 31;
@@ -518,6 +560,45 @@
}
+ public static void testRotateLeftIntWithByteDistance() {
+ expectEqualsInt(0x00000001, rotateLeftIntWithByteDistance(0x00000001, (byte)0));
+ expectEqualsInt(0x00000002, rotateLeftIntWithByteDistance(0x00000001, (byte)1));
+ expectEqualsInt(0x80000000, rotateLeftIntWithByteDistance(0x00000001, (byte)31));
+ expectEqualsInt(0x00000001, rotateLeftIntWithByteDistance(0x00000001, (byte)32)); // overshoot
+ expectEqualsInt(0x00000003, rotateLeftIntWithByteDistance(0x80000001, (byte)1));
+ expectEqualsInt(0x00000006, rotateLeftIntWithByteDistance(0x80000001, (byte)2));
+ expectEqualsInt(0x23456781, rotateLeftIntWithByteDistance(0x12345678, (byte)4));
+ expectEqualsInt(0xBCDEF09A, rotateLeftIntWithByteDistance(0x9ABCDEF0, (byte)8));
+ for (byte i = 0; i < 40; i++) { // overshoot a bit
+ byte j = (byte)(i & 31);
+ expectEqualsInt(0x00000000, rotateLeftIntWithByteDistance(0x00000000, i));
+ expectEqualsInt(0xFFFFFFFF, rotateLeftIntWithByteDistance(0xFFFFFFFF, i));
+ expectEqualsInt(1 << j, rotateLeftIntWithByteDistance(0x00000001, i));
+ expectEqualsInt((0x12345678 << j) | (0x12345678 >>> -j),
+ rotateLeftIntWithByteDistance(0x12345678, i));
+ }
+ }
+
+ public static void testRotateRightIntWithByteDistance() {
+ expectEqualsInt(0x80000000, rotateRightIntWithByteDistance(0x80000000, (byte)0));
+ expectEqualsInt(0x40000000, rotateRightIntWithByteDistance(0x80000000, (byte)1));
+ expectEqualsInt(0x00000001, rotateRightIntWithByteDistance(0x80000000, (byte)31));
+ expectEqualsInt(0x80000000, rotateRightIntWithByteDistance(0x80000000, (byte)32)); // overshoot
+ expectEqualsInt(0xC0000000, rotateRightIntWithByteDistance(0x80000001, (byte)1));
+ expectEqualsInt(0x60000000, rotateRightIntWithByteDistance(0x80000001, (byte)2));
+ expectEqualsInt(0x81234567, rotateRightIntWithByteDistance(0x12345678, (byte)4));
+ expectEqualsInt(0xF09ABCDE, rotateRightIntWithByteDistance(0x9ABCDEF0, (byte)8));
+ for (byte i = 0; i < 40; i++) { // overshoot a bit
+ byte j = (byte)(i & 31);
+ expectEqualsInt(0x00000000, rotateRightIntWithByteDistance(0x00000000, i));
+ expectEqualsInt(0xFFFFFFFF, rotateRightIntWithByteDistance(0xFFFFFFFF, i));
+ expectEqualsInt(0x80000000 >>> j, rotateRightIntWithByteDistance(0x80000000, i));
+ expectEqualsInt((0x12345678 >>> j) | (0x12345678 << -j),
+ rotateRightIntWithByteDistance(0x12345678, i));
+ }
+ }
+
+
public static void main(String args[]) {
testRotateLeftBoolean();
testRotateLeftByte();
@@ -533,6 +614,10 @@
testRotateRightInt();
testRotateRightLong();
+ // Also exercise distance values with types other than int.
+ testRotateLeftIntWithByteDistance();
+ testRotateRightIntWithByteDistance();
+
System.out.println("passed");
}