ARM/ARM64: Intrinsics - numberOfTrailingZeros, rotateLeft, rotateRight
Change-Id: I2a07c279756ee804fb7c129416bdc4a3962e93ed
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index 42b792c..af93aab 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -39,6 +39,9 @@
true, // kIntrinsicReverseBits
true, // kIntrinsicReverseBytes
true, // kIntrinsicNumberOfLeadingZeros
+ true, // kIntrinsicNumberOfTrailingZeros
+ true, // kIntrinsicRotateRight
+ true, // kIntrinsicRotateLeft
true, // kIntrinsicAbsInt
true, // kIntrinsicAbsLong
true, // kIntrinsicAbsFloat
@@ -79,6 +82,10 @@
static_assert(kIntrinsicIsStatic[kIntrinsicReverseBytes], "ReverseBytes must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfLeadingZeros],
"NumberOfLeadingZeros must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicNumberOfTrailingZeros],
+ "NumberOfTrailingZeros must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRotateRight], "RotateRight must be static");
+static_assert(kIntrinsicIsStatic[kIntrinsicRotateLeft], "RotateLeft must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsInt], "AbsInt must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsLong], "AbsLong must be static");
static_assert(kIntrinsicIsStatic[kIntrinsicAbsFloat], "AbsFloat must be static");
@@ -232,6 +239,9 @@
"putOrderedObject", // kNameCachePutOrderedObject
"arraycopy", // kNameCacheArrayCopy
"numberOfLeadingZeros", // kNameCacheNumberOfLeadingZeros
+ "numberOfTrailingZeros", // kNameCacheNumberOfTrailingZeros
+ "rotateRight", // kNameCacheRotateRight
+ "rotateLeft", // kNameCacheRotateLeft
};
const DexFileMethodInliner::ProtoDef DexFileMethodInliner::kProtoCacheDefs[] = {
@@ -289,6 +299,8 @@
{ kClassCacheVoid, 2, { kClassCacheLong, kClassCacheShort } },
// kProtoCacheObject_Z
{ kClassCacheBoolean, 1, { kClassCacheJavaLangObject } },
+ // kProtoCacheJI_J
+ { kClassCacheLong, 2, { kClassCacheLong, kClassCacheInt } },
// kProtoCacheObjectJII_Z
{ kClassCacheBoolean, 4, { kClassCacheJavaLangObject, kClassCacheLong,
kClassCacheInt, kClassCacheInt } },
@@ -379,6 +391,8 @@
INTRINSIC(JavaLangInteger, NumberOfLeadingZeros, I_I, kIntrinsicNumberOfLeadingZeros, k32),
INTRINSIC(JavaLangLong, NumberOfLeadingZeros, J_I, kIntrinsicNumberOfLeadingZeros, k64),
+ INTRINSIC(JavaLangInteger, NumberOfTrailingZeros, I_I, kIntrinsicNumberOfTrailingZeros, k32),
+ INTRINSIC(JavaLangLong, NumberOfTrailingZeros, J_I, kIntrinsicNumberOfTrailingZeros, k64),
INTRINSIC(JavaLangMath, Abs, I_I, kIntrinsicAbsInt, 0),
INTRINSIC(JavaLangStrictMath, Abs, I_I, kIntrinsicAbsInt, 0),
@@ -468,6 +482,11 @@
INTRINSIC(JavaLangSystem, ArrayCopy, CharArrayICharArrayII_V , kIntrinsicSystemArrayCopyCharArray,
0),
+ INTRINSIC(JavaLangInteger, RotateRight, II_I, kIntrinsicRotateRight, k32),
+ INTRINSIC(JavaLangLong, RotateRight, JI_J, kIntrinsicRotateRight, k64),
+ INTRINSIC(JavaLangInteger, RotateLeft, II_I, kIntrinsicRotateLeft, k32),
+ INTRINSIC(JavaLangLong, RotateLeft, JI_J, kIntrinsicRotateLeft, k64),
+
#undef INTRINSIC
#define SPECIAL(c, n, p, o, d) \
@@ -631,7 +650,10 @@
case kIntrinsicSystemArrayCopyCharArray:
return backend->GenInlinedArrayCopyCharArray(info);
case kIntrinsicNumberOfLeadingZeros:
- return false; // not implemented in quick
+ case kIntrinsicNumberOfTrailingZeros:
+ case kIntrinsicRotateRight:
+ case kIntrinsicRotateLeft:
+ return false; // not implemented in quick.
default:
LOG(FATAL) << "Unexpected intrinsic opcode: " << intrinsic.opcode;
return false; // avoid warning "control reaches end of non-void function"
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index d6c8bfb..8458806 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -208,6 +208,9 @@
kNameCachePutOrderedObject,
kNameCacheArrayCopy,
kNameCacheNumberOfLeadingZeros,
+ kNameCacheNumberOfTrailingZeros,
+ kNameCacheRotateRight,
+ kNameCacheRotateLeft,
kNameCacheLast
};
@@ -245,6 +248,7 @@
kProtoCacheJJ_V,
kProtoCacheJS_V,
kProtoCacheObject_Z,
+ kProtoCacheJI_J,
kProtoCacheObjectJII_Z,
kProtoCacheObjectJJJ_Z,
kProtoCacheObjectJObjectObject_Z,
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 075ec1e..2dd4bba 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -120,6 +120,28 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
+ case kIntrinsicRotateRight:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerRotateRight;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongRotateRight;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+ case kIntrinsicRotateLeft:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerRotateLeft;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongRotateLeft;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
+
+ // Misc data processing.
case kIntrinsicNumberOfLeadingZeros:
switch (GetType(method.d.data, true)) {
case Primitive::kPrimInt:
@@ -130,6 +152,16 @@
LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
UNREACHABLE();
}
+ case kIntrinsicNumberOfTrailingZeros:
+ switch (GetType(method.d.data, true)) {
+ case Primitive::kPrimInt:
+ return Intrinsics::kIntegerNumberOfTrailingZeros;
+ case Primitive::kPrimLong:
+ return Intrinsics::kLongNumberOfTrailingZeros;
+ default:
+ LOG(FATAL) << "Unknown/unsupported op size " << method.d.data;
+ UNREACHABLE();
+ }
// Abs.
case kIntrinsicAbsDouble:
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 69a3e62..b7dc1df 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -266,6 +266,227 @@
GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
}
+static void GenNumberOfTrailingZeros(LocationSummary* locations,
+ Primitive::Type type,
+ ArmAssembler* assembler) {
+ DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong));
+
+ Register out = locations->Out().AsRegister<Register>();
+
+ if (type == Primitive::kPrimLong) {
+ Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Label end;
+ __ rbit(out, in_reg_lo);
+ __ clz(out, out);
+ __ CompareAndBranchIfNonZero(in_reg_lo, &end);
+ __ rbit(out, in_reg_hi);
+ __ clz(out, out);
+ __ AddConstant(out, 32);
+ __ Bind(&end);
+ } else {
+ Register in = locations->InAt(0).AsRegister<Register>();
+ __ rbit(out, in);
+ __ clz(out, out);
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+ GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler());
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+ GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler());
+}
+
+static void GenIntegerRotate(LocationSummary* locations,
+ ArmAssembler* assembler,
+ bool is_left) {
+ Register in = locations->InAt(0).AsRegister<Register>();
+ Location rhs = locations->InAt(1);
+ Register out = locations->Out().AsRegister<Register>();
+
+ if (rhs.IsConstant()) {
+ // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
+ // so map all rotations to a +ve. equivalent in that range.
+ // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
+ uint32_t rot = rhs.GetConstant()->AsIntConstant()->GetValue() & 0x1F;
+ if (rot) {
+ // Rotate, mapping left rotations to right equivalents if necessary.
+ // (e.g. left by 2 bits == right by 30.)
+ __ Ror(out, in, is_left ? (0x20 - rot) : rot);
+ } else if (out != in) {
+ __ Mov(out, in);
+ }
+ } else {
+ if (is_left) {
+ __ rsb(out, rhs.AsRegister<Register>(), ShifterOperand(0));
+ __ Ror(out, in, out);
+ } else {
+ __ Ror(out, in, rhs.AsRegister<Register>());
+ }
+ }
+}
+
+// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
+// rotates by swapping input regs (effectively rotating by the first 32-bits of
+// a larger rotation) or flipping direction (thus treating larger right/left
+// rotations as sub-word sized rotations in the other direction) as appropriate.
+static void GenLongRotate(LocationSummary* locations,
+ ArmAssembler* assembler,
+ bool is_left) {
+ Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>();
+ Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>();
+ Location rhs = locations->InAt(1);
+ Register out_reg_lo = locations->Out().AsRegisterPairLow<Register>();
+ Register out_reg_hi = locations->Out().AsRegisterPairHigh<Register>();
+
+ if (rhs.IsConstant()) {
+ uint32_t rot = rhs.GetConstant()->AsIntConstant()->GetValue();
+ // Map all left rotations to right equivalents.
+ if (is_left) {
+ rot = 0x40 - rot;
+ }
+ // Map all rotations to +ve. equivalents on the interval [0,63].
+ rot &= 0x3F;
+ // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
+ // logic below to a simple pair of binary orr.
+ // (e.g. 34 bits == in_reg swap + 2 bits right.)
+ if (rot >= 0x20) {
+ rot -= 0x20;
+ std::swap(in_reg_hi, in_reg_lo);
+ }
+ // Rotate, or mov to out for zero or word size rotations.
+ if (rot) {
+ __ Lsr(out_reg_hi, in_reg_hi, rot);
+ __ orr(out_reg_hi, out_reg_hi, ShifterOperand(in_reg_lo, arm::LSL, 0x20 - rot));
+ __ Lsr(out_reg_lo, in_reg_lo, rot);
+ __ orr(out_reg_lo, out_reg_lo, ShifterOperand(in_reg_hi, arm::LSL, 0x20 - rot));
+ } else {
+ __ Mov(out_reg_lo, in_reg_lo);
+ __ Mov(out_reg_hi, in_reg_hi);
+ }
+ } else {
+ Register shift_left = locations->GetTemp(0).AsRegister<Register>();
+ Register shift_right = locations->GetTemp(1).AsRegister<Register>();
+ Label end;
+ Label right;
+
+ __ and_(shift_left, rhs.AsRegister<Register>(), ShifterOperand(0x1F));
+ __ Lsrs(shift_right, rhs.AsRegister<Register>(), 6);
+ __ rsb(shift_right, shift_left, ShifterOperand(0x20), AL, kCcKeep);
+
+ if (is_left) {
+ __ b(&right, CS);
+ } else {
+ __ b(&right, CC);
+ std::swap(shift_left, shift_right);
+ }
+
+ // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
+ // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
+ __ Lsl(out_reg_hi, in_reg_hi, shift_left);
+ __ Lsr(out_reg_lo, in_reg_lo, shift_right);
+ __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
+ __ Lsl(out_reg_lo, in_reg_lo, shift_left);
+ __ Lsr(shift_left, in_reg_hi, shift_right);
+ __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left));
+ __ b(&end);
+
+ // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
+ // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
+ __ Bind(&right);
+ __ Lsr(out_reg_hi, in_reg_hi, shift_right);
+ __ Lsl(out_reg_lo, in_reg_lo, shift_left);
+ __ add(out_reg_hi, out_reg_hi, ShifterOperand(out_reg_lo));
+ __ Lsr(out_reg_lo, in_reg_lo, shift_right);
+ __ Lsl(shift_right, in_reg_hi, shift_left);
+ __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right));
+
+ __ Bind(&end);
+ }
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerRotateRight(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerRotateRight(HInvoke* invoke) {
+ GenIntegerRotate(invoke->GetLocations(), GetAssembler(), false /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongRotateRight(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (invoke->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::ConstantLocation(invoke->InputAt(1)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongRotateRight(HInvoke* invoke) {
+ GenLongRotate(invoke->GetLocations(), GetAssembler(), false /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitIntegerRotateLeft(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitIntegerRotateLeft(HInvoke* invoke) {
+ GenIntegerRotate(invoke->GetLocations(), GetAssembler(), true /* is_left */);
+}
+
+void IntrinsicLocationsBuilderARM::VisitLongRotateLeft(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (invoke->InputAt(1)->IsConstant()) {
+ locations->SetInAt(1, Location::ConstantLocation(invoke->InputAt(1)->AsConstant()));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM::VisitLongRotateLeft(HInvoke* invoke) {
+ GenLongRotate(invoke->GetLocations(), GetAssembler(), true /* is_left */);
+}
+
static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) {
Location in = locations->InAt(0);
Location out = locations->Out();
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 0171d69..5efa01e 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -41,12 +41,12 @@
using helpers::FPRegisterFrom;
using helpers::HeapOperand;
using helpers::LocationFrom;
+using helpers::OperandFrom;
using helpers::RegisterFrom;
using helpers::SRegisterFrom;
using helpers::WRegisterFrom;
using helpers::XRegisterFrom;
-
namespace {
ALWAYS_INLINE inline MemOperand AbsoluteHeapOperandFrom(Location location, size_t offset = 0) {
@@ -287,6 +287,131 @@
GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
}
+static void GenNumberOfTrailingZeros(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+
+ __ Rbit(RegisterFrom(out, type), RegisterFrom(in, type));
+ __ Clz(RegisterFrom(out, type), RegisterFrom(out, type));
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) {
+ GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+ CreateIntToIntLocations(arena_, invoke);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongNumberOfTrailingZeros(HInvoke* invoke) {
+ GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+static void GenRotateRight(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+ Operand rhs = OperandFrom(locations->InAt(1), type);
+
+ if (rhs.IsImmediate()) {
+ uint32_t shift = rhs.immediate() & (RegisterFrom(in, type).SizeInBits() - 1);
+ __ Ror(RegisterFrom(out, type),
+ RegisterFrom(in, type),
+ shift);
+ } else {
+ DCHECK(rhs.shift() == vixl::LSL && rhs.shift_amount() == 0);
+ __ Ror(RegisterFrom(out, type),
+ RegisterFrom(in, type),
+ rhs.reg());
+ }
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerRotateRight(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerRotateRight(HInvoke* invoke) {
+ GenRotateRight(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongRotateRight(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongRotateRight(HInvoke* invoke) {
+ GenRotateRight(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
+static void GenRotateLeft(LocationSummary* locations,
+ Primitive::Type type,
+ vixl::MacroAssembler* masm) {
+ DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong);
+
+ Location in = locations->InAt(0);
+ Location out = locations->Out();
+ Operand rhs = OperandFrom(locations->InAt(1), type);
+
+ if (rhs.IsImmediate()) {
+ uint32_t regsize = RegisterFrom(in, type).SizeInBits();
+ uint32_t shift = (regsize - rhs.immediate()) & (regsize - 1);
+ __ Ror(RegisterFrom(out, type), RegisterFrom(in, type), shift);
+ } else {
+ DCHECK(rhs.shift() == vixl::LSL && rhs.shift_amount() == 0);
+ __ Neg(RegisterFrom(out, type),
+ Operand(RegisterFrom(locations->InAt(1), type)));
+ __ Ror(RegisterFrom(out, type),
+ RegisterFrom(in, type),
+ RegisterFrom(out, type));
+ }
+}
+
+void IntrinsicLocationsBuilderARM64::VisitIntegerRotateLeft(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitIntegerRotateLeft(HInvoke* invoke) {
+ GenRotateLeft(invoke->GetLocations(), Primitive::kPrimInt, GetVIXLAssembler());
+}
+
+void IntrinsicLocationsBuilderARM64::VisitLongRotateLeft(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RegisterOrConstant(invoke->InputAt(1)));
+ locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+void IntrinsicCodeGeneratorARM64::VisitLongRotateLeft(HInvoke* invoke) {
+ GenRotateLeft(invoke->GetLocations(), Primitive::kPrimLong, GetVIXLAssembler());
+}
+
static void GenReverse(LocationSummary* locations,
Primitive::Type type,
vixl::MacroAssembler* masm) {
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index 7e5339e..bfe5e55 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -29,9 +29,15 @@
V(IntegerReverse, kStatic, kNeedsEnvironmentOrCache) \
V(IntegerReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
V(IntegerNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache) \
+ V(IntegerNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache) \
+ V(IntegerRotateRight, kStatic, kNeedsEnvironmentOrCache) \
+ V(IntegerRotateLeft, kStatic, kNeedsEnvironmentOrCache) \
V(LongReverse, kStatic, kNeedsEnvironmentOrCache) \
V(LongReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
V(LongNumberOfLeadingZeros, kStatic, kNeedsEnvironmentOrCache) \
+ V(LongNumberOfTrailingZeros, kStatic, kNeedsEnvironmentOrCache) \
+ V(LongRotateRight, kStatic, kNeedsEnvironmentOrCache) \
+ V(LongRotateLeft, kStatic, kNeedsEnvironmentOrCache) \
V(ShortReverseBytes, kStatic, kNeedsEnvironmentOrCache) \
V(MathAbsDouble, kStatic, kNeedsEnvironmentOrCache) \
V(MathAbsFloat, kStatic, kNeedsEnvironmentOrCache) \
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index be076cd..bff29af 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1957,6 +1957,12 @@
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
+UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 1f35b59..f91ad7f 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1775,6 +1775,12 @@
UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
+UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateRight)
+UNIMPLEMENTED_INTRINSIC(LongRotateRight)
+UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
+UNIMPLEMENTED_INTRINSIC(LongRotateLeft)
#undef UNIMPLEMENTED_INTRINSIC
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 7825457..4585d71 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -495,6 +495,7 @@
virtual void clz(Register rd, Register rm, Condition cond = AL) = 0;
virtual void movw(Register rd, uint16_t imm16, Condition cond = AL) = 0;
virtual void movt(Register rd, uint16_t imm16, Condition cond = AL) = 0;
+ virtual void rbit(Register rd, Register rm, Condition cond = AL) = 0;
// Multiply instructions.
virtual void mul(Register rd, Register rn, Register rm, Condition cond = AL) = 0;
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index d91ddee..64fa9a4 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -735,6 +735,20 @@
}
+void Arm32Assembler::rbit(Register rd, Register rm, Condition cond) {
+ CHECK_NE(rd, kNoRegister);
+ CHECK_NE(rm, kNoRegister);
+ CHECK_NE(cond, kNoCondition);
+ CHECK_NE(rd, PC);
+ CHECK_NE(rm, PC);
+ int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) |
+ B26 | B25 | B23 | B22 | B21 | B20 | (0xf << 16) |
+ (static_cast<int32_t>(rd) << kRdShift) |
+ (0xf << 8) | B5 | B4 | static_cast<int32_t>(rm);
+ Emit(encoding);
+}
+
+
void Arm32Assembler::EmitMulOp(Condition cond, int32_t opcode,
Register rd, Register rn,
Register rm, Register rs) {
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index b96bb74..6605afa 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -87,6 +87,7 @@
void clz(Register rd, Register rm, Condition cond = AL) OVERRIDE;
void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
+ void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
// Multiply instructions.
void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index e6412ac..2a0912e 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -883,4 +883,8 @@
DriverStr(expected, "strexd");
}
+TEST_F(AssemblerArm32Test, rbit) {
+ T3Helper(&arm::Arm32Assembler::rbit, true, "rbit{cond} {reg1}, {reg2}", "rbit");
+}
+
} // namespace art
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 90ed10c..b76068b 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -2426,6 +2426,25 @@
}
+void Thumb2Assembler::rbit(Register rd, Register rm, Condition cond) {
+ CHECK_NE(rd, kNoRegister);
+ CHECK_NE(rm, kNoRegister);
+ CheckCondition(cond);
+ CHECK_NE(rd, PC);
+ CHECK_NE(rm, PC);
+ CHECK_NE(rd, SP);
+ CHECK_NE(rm, SP);
+ int32_t encoding = B31 | B30 | B29 | B28 | B27 |
+ B25 | B23 | B20 |
+ static_cast<uint32_t>(rm) << 16 |
+ 0xf << 12 |
+ static_cast<uint32_t>(rd) << 8 |
+ B7 | B5 |
+ static_cast<uint32_t>(rm);
+ Emit32(encoding);
+}
+
+
void Thumb2Assembler::ldrex(Register rt, Register rn, uint16_t imm, Condition cond) {
CHECK_NE(rn, kNoRegister);
CHECK_NE(rt, kNoRegister);
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index c802c27..aaa7b35 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -111,6 +111,7 @@
void clz(Register rd, Register rm, Condition cond = AL) OVERRIDE;
void movw(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
void movt(Register rd, uint16_t imm16, Condition cond = AL) OVERRIDE;
+ void rbit(Register rd, Register rm, Condition cond = AL) OVERRIDE;
// Multiply instructions.
void mul(Register rd, Register rn, Register rm, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 84f5cb1..9c08ce0 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -1019,4 +1019,12 @@
DriverStr(expected, "clz");
}
+TEST_F(AssemblerThumb2Test, rbit) {
+ __ rbit(arm::R1, arm::R0);
+
+ const char* expected = "rbit r1, r0\n";
+
+ DriverStr(expected, "rbit");
+}
+
} // namespace art
diff --git a/runtime/quick/inline_method_analyser.h b/runtime/quick/inline_method_analyser.h
index dd3703c..64c2249 100644
--- a/runtime/quick/inline_method_analyser.h
+++ b/runtime/quick/inline_method_analyser.h
@@ -40,6 +40,9 @@
kIntrinsicReverseBits,
kIntrinsicReverseBytes,
kIntrinsicNumberOfLeadingZeros,
+ kIntrinsicNumberOfTrailingZeros,
+ kIntrinsicRotateRight,
+ kIntrinsicRotateLeft,
kIntrinsicAbsInt,
kIntrinsicAbsLong,
kIntrinsicAbsFloat,
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 08ccf0e..5913c40 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -78,6 +78,14 @@
test_Memory_pokeShort();
test_Memory_pokeInt();
test_Memory_pokeLong();
+ test_Integer_numberOfTrailingZeros();
+ test_Long_numberOfTrailingZeros();
+ test_Integer_rotateRight();
+ test_Long_rotateRight();
+ test_Integer_rotateLeft();
+ test_Long_rotateLeft();
+ test_Integer_rotateRightLeft();
+ test_Long_rotateRightLeft();
}
/**
@@ -1360,4 +1368,136 @@
poke_long.invoke(null, address + 1, (long)0x2122232425262728L, false);
Assert.assertTrue(Arrays.equals(ru, b));
}
+
+ public static void test_Integer_numberOfTrailingZeros() {
+ Assert.assertEquals(Integer.numberOfTrailingZeros(0), Integer.SIZE);
+ for (int i = 0; i < Integer.SIZE; i++) {
+ Assert.assertEquals(
+ Integer.numberOfTrailingZeros(0x80000000 >> i),
+ Integer.SIZE - 1 - i);
+ Assert.assertEquals(
+ Integer.numberOfTrailingZeros((0x80000000 >> i) | 0x80000000),
+ Integer.SIZE - 1 - i);
+ Assert.assertEquals(Integer.numberOfTrailingZeros(1 << i), i);
+ }
+ }
+
+ public static void test_Long_numberOfTrailingZeros() {
+ Assert.assertEquals(Long.numberOfTrailingZeros(0), Long.SIZE);
+ for (int i = 0; i < Long.SIZE; i++) {
+ Assert.assertEquals(
+ Long.numberOfTrailingZeros(0x8000000000000000L >> i),
+ Long.SIZE - 1 - i);
+ Assert.assertEquals(
+ Long.numberOfTrailingZeros((0x8000000000000000L >> i) | 0x8000000000000000L),
+ Long.SIZE - 1 - i);
+ Assert.assertEquals(Long.numberOfTrailingZeros(1L << i), i);
+ }
+ }
+
+ public static void test_Integer_rotateRight() throws Exception {
+ Assert.assertEquals(Integer.rotateRight(0x11, 0), 0x11);
+
+ Assert.assertEquals(Integer.rotateRight(0x11, 1), 0x80000008);
+ Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE - 1), 0x22);
+ Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE), 0x11);
+ Assert.assertEquals(Integer.rotateRight(0x11, Integer.SIZE + 1), 0x80000008);
+
+ Assert.assertEquals(Integer.rotateRight(0x11, -1), 0x22);
+ Assert.assertEquals(Integer.rotateRight(0x11, -(Integer.SIZE - 1)), 0x80000008);
+ Assert.assertEquals(Integer.rotateRight(0x11, -Integer.SIZE), 0x11);
+ Assert.assertEquals(Integer.rotateRight(0x11, -(Integer.SIZE + 1)), 0x22);
+
+ Assert.assertEquals(Integer.rotateRight(0x80000000, 1), 0x40000000);
+
+ for (int i = 0; i < Integer.SIZE; i++) {
+ Assert.assertEquals(
+ Integer.rotateRight(0xBBAAAADD, i),
+ (0xBBAAAADD >>> i) | (0xBBAAAADD << (Integer.SIZE - i)));
+ }
+ }
+
+ public static void test_Long_rotateRight() throws Exception {
+ Assert.assertEquals(Long.rotateRight(0x11, 0), 0x11);
+
+ Assert.assertEquals(Long.rotateRight(0x11, 1), 0x8000000000000008L);
+ Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE - 1), 0x22);
+ Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE), 0x11);
+ Assert.assertEquals(Long.rotateRight(0x11, Long.SIZE + 1), 0x8000000000000008L);
+
+ Assert.assertEquals(Long.rotateRight(0x11, -1), 0x22);
+ Assert.assertEquals(Long.rotateRight(0x11, -(Long.SIZE - 1)), 0x8000000000000008L);
+ Assert.assertEquals(Long.rotateRight(0x11, -Long.SIZE), 0x11);
+ Assert.assertEquals(Long.rotateRight(0x11, -(Long.SIZE + 1)), 0x22);
+
+ Assert.assertEquals(Long.rotateRight(0x8000000000000000L, 1), 0x4000000000000000L);
+
+ for (int i = 0; i < Long.SIZE; i++) {
+ Assert.assertEquals(
+ Long.rotateRight(0xBBAAAADDFF0000DDL, i),
+ (0xBBAAAADDFF0000DDL >>> i) | (0xBBAAAADDFF0000DDL << (Long.SIZE - i)));
+ }
+ }
+
+ public static void test_Integer_rotateLeft() throws Exception {
+ Assert.assertEquals(Integer.rotateLeft(0x11, 0), 0x11);
+
+ Assert.assertEquals(Integer.rotateLeft(0x11, 1), 0x22);
+ Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE - 1), 0x80000008);
+ Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE), 0x11);
+ Assert.assertEquals(Integer.rotateLeft(0x11, Integer.SIZE + 1), 0x22);
+
+ Assert.assertEquals(Integer.rotateLeft(0x11, -1), 0x80000008);
+ Assert.assertEquals(Integer.rotateLeft(0x11, -(Integer.SIZE - 1)), 0x22);
+ Assert.assertEquals(Integer.rotateLeft(0x11, -Integer.SIZE), 0x11);
+ Assert.assertEquals(Integer.rotateLeft(0x11, -(Integer.SIZE + 1)), 0x80000008);
+
+ Assert.assertEquals(Integer.rotateLeft(0xC0000000, 1), 0x80000001);
+
+ for (int i = 0; i < Integer.SIZE; i++) {
+ Assert.assertEquals(
+ Integer.rotateLeft(0xBBAAAADD, i),
+ (0xBBAAAADD << i) | (0xBBAAAADD >>> (Integer.SIZE - i)));
+ }
+ }
+
+ public static void test_Long_rotateLeft() throws Exception {
+ Assert.assertEquals(Long.rotateLeft(0x11, 0), 0x11);
+
+ Assert.assertEquals(Long.rotateLeft(0x11, 1), 0x22);
+ Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE - 1), 0x8000000000000008L);
+ Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE), 0x11);
+ Assert.assertEquals(Long.rotateLeft(0x11, Long.SIZE + 1), 0x22);
+
+ Assert.assertEquals(Long.rotateLeft(0x11, -1), 0x8000000000000008L);
+ Assert.assertEquals(Long.rotateLeft(0x11, -(Long.SIZE - 1)), 0x22);
+ Assert.assertEquals(Long.rotateLeft(0x11, -Long.SIZE), 0x11);
+ Assert.assertEquals(Long.rotateLeft(0x11, -(Long.SIZE + 1)), 0x8000000000000008L);
+
+ Assert.assertEquals(Long.rotateLeft(0xC000000000000000L, 1), 0x8000000000000001L);
+
+ for (int i = 0; i < Long.SIZE; i++) {
+ Assert.assertEquals(
+ Long.rotateLeft(0xBBAAAADDFF0000DDL, i),
+ (0xBBAAAADDFF0000DDL << i) | (0xBBAAAADDFF0000DDL >>> (Long.SIZE - i)));
+ }
+ }
+
+ public static void test_Integer_rotateRightLeft() throws Exception {
+ for (int i = 0; i < Integer.SIZE * 2; i++) {
+ Assert.assertEquals(Integer.rotateLeft(0xBBAAAADD, i),
+ Integer.rotateRight(0xBBAAAADD, -i));
+ Assert.assertEquals(Integer.rotateLeft(0xBBAAAADD, -i),
+ Integer.rotateRight(0xBBAAAADD, i));
+ }
+ }
+
+ public static void test_Long_rotateRightLeft() throws Exception {
+ for (int i = 0; i < Long.SIZE * 2; i++) {
+ Assert.assertEquals(Long.rotateLeft(0xBBAAAADDFF0000DDL, i),
+ Long.rotateRight(0xBBAAAADDFF0000DDL, -i));
+ Assert.assertEquals(Long.rotateLeft(0xBBAAAADDFF0000DDL, -i),
+ Long.rotateRight(0xBBAAAADDFF0000DDL, i));
+ }
+ }
}