Merge "ART: OneBit intrinsics should use 1ULL for 64-bit shift"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 1afbdfc..c09116f 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -277,6 +277,7 @@
compiler/optimizing/suspend_check_test.cc \
compiler/utils/dedupe_set_test.cc \
compiler/utils/intrusive_forward_list_test.cc \
+ compiler/utils/string_reference_test.cc \
compiler/utils/swap_space_test.cc \
compiler/utils/test_dex_file_builder_test.cc \
compiler/utils/transform_array_ref_test.cc \
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index b9466ba..83b4705 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -430,7 +430,9 @@
instruction_->IsLoadClass() ||
instruction_->IsLoadString() ||
instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast())
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
+ instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
@@ -493,8 +495,12 @@
Register reg_out = out_.AsRegister<Register>();
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
- DCHECK(!instruction_->IsInvoke() ||
- (instruction_->IsInvokeStaticOrDirect() &&
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
@@ -507,7 +513,7 @@
// introduce a copy of it, `index`.
Location index = index_;
if (index_.IsValid()) {
- // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
+ // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
if (instruction_->IsArrayGet()) {
// Compute the actual memory offset and store it in `index`.
Register index_reg = index_.AsRegister<Register>();
@@ -555,7 +561,11 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
__ AddConstant(index_reg, index_reg, offset_);
} else {
- DCHECK(instruction_->IsInvoke());
+ // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
+ // intrinsics, `index_` is not shifted by a scale factor of 2
+ // (as in the case of ArrayGet), as it is actually an offset
+ // to an object field within an object.
+ DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
DCHECK(instruction_->GetLocations()->Intrinsified());
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
@@ -6203,8 +6213,9 @@
// /* HeapReference<Object> */ ref = *(obj + offset)
Location no_index = Location::NoLocation();
+ ScaleFactor no_scale_factor = TIMES_1;
GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, offset, no_index, temp, needs_null_check);
+ instruction, ref, obj, offset, no_index, no_scale_factor, temp, needs_null_check);
}
void CodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -6217,10 +6228,14 @@
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ ref =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
+ ScaleFactor scale_factor = TIMES_4;
GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, data_offset, index, temp, needs_null_check);
+ instruction, ref, obj, data_offset, index, scale_factor, temp, needs_null_check);
}
void CodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -6228,6 +6243,7 @@
Register obj,
uint32_t offset,
Location index,
+ ScaleFactor scale_factor,
Location temp,
bool needs_null_check) {
DCHECK(kEmitCompilerReadBarrier);
@@ -6282,17 +6298,22 @@
// The actual reference load.
if (index.IsValid()) {
- static_assert(
- sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- // /* HeapReference<Object> */ ref =
- // *(obj + offset + index * sizeof(HeapReference<Object>))
+ // Load types involving an "index": ArrayGet and
+ // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
+ // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
if (index.IsConstant()) {
size_t computed_offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset;
+ (index.GetConstant()->AsIntConstant()->GetValue() << scale_factor) + offset;
__ LoadFromOffset(kLoadWord, ref_reg, obj, computed_offset);
} else {
- __ add(IP, obj, ShifterOperand(index.AsRegister<Register>(), LSL, TIMES_4));
+ // Handle the special case of the
+ // UnsafeGetObject/UnsafeGetObjectVolatile intrinsics, which use
+ // a register pair as index ("long offset"), of which only the low
+ // part contains data.
+ Register index_reg = index.IsRegisterPair()
+ ? index.AsRegisterPairLow<Register>()
+ : index.AsRegister<Register>();
+ __ add(IP, obj, ShifterOperand(index_reg, LSL, scale_factor));
__ LoadFromOffset(kLoadWord, ref_reg, IP, offset);
}
} else {
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 4fce5af..477c4f1 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -472,6 +472,16 @@
Location index,
Location temp,
bool needs_null_check);
+ // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ uint32_t offset,
+ Location index,
+ ScaleFactor scale_factor,
+ Location temp,
+ bool needs_null_check);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
@@ -527,16 +537,6 @@
void GenerateExplicitNullCheck(HNullCheck* instruction);
private:
- // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- Register obj,
- uint32_t offset,
- Location index,
- Location temp,
- bool needs_null_check);
-
Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 4692a4a..07d5e50 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -598,7 +598,9 @@
instruction_->IsLoadClass() ||
instruction_->IsLoadString() ||
instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast())
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
+ instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
@@ -661,8 +663,12 @@
Primitive::Type type = Primitive::kPrimNot;
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg()));
- DCHECK(!instruction_->IsInvoke() ||
- (instruction_->IsInvokeStaticOrDirect() &&
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
@@ -680,7 +686,7 @@
// introduce a copy of it, `index`.
Location index = index_;
if (index_.IsValid()) {
- // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
+ // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
if (instruction_->IsArrayGet()) {
// Compute the actual memory offset and store it in `index`.
Register index_reg = RegisterFrom(index_, Primitive::kPrimInt);
@@ -728,7 +734,11 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
__ Add(index_reg, index_reg, Operand(offset_));
} else {
- DCHECK(instruction_->IsInvoke());
+ // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
+ // intrinsics, `index_` is not shifted by a scale factor of 2
+ // (as in the case of ArrayGet), as it is actually an offset
+ // to an object field within an object.
+ DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
DCHECK(instruction_->GetLocations()->Intrinsified());
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
@@ -5102,8 +5112,16 @@
// /* HeapReference<Object> */ ref = *(obj + offset)
Location no_index = Location::NoLocation();
- GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, offset, no_index, temp, needs_null_check, use_load_acquire);
+ size_t no_scale_factor = 0U;
+ GenerateReferenceLoadWithBakerReadBarrier(instruction,
+ ref,
+ obj,
+ offset,
+ no_index,
+ no_scale_factor,
+ temp,
+ needs_null_check,
+ use_load_acquire);
}
void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -5120,10 +5138,21 @@
// never use Load-Acquire instructions on ARM64.
const bool use_load_acquire = false;
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ ref =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
- GenerateReferenceLoadWithBakerReadBarrier(
- instruction, ref, obj, data_offset, index, temp, needs_null_check, use_load_acquire);
+ size_t scale_factor = Primitive::ComponentSizeShift(Primitive::kPrimNot);
+ GenerateReferenceLoadWithBakerReadBarrier(instruction,
+ ref,
+ obj,
+ data_offset,
+ index,
+ scale_factor,
+ temp,
+ needs_null_check,
+ use_load_acquire);
}
void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
@@ -5131,15 +5160,16 @@
vixl::Register obj,
uint32_t offset,
Location index,
+ size_t scale_factor,
Register temp,
bool needs_null_check,
bool use_load_acquire) {
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
- // If `index` is a valid location, then we are emitting an array
- // load, so we shouldn't be using a Load Acquire instruction.
- // In other words: `index.IsValid()` => `!use_load_acquire`.
- DCHECK(!index.IsValid() || !use_load_acquire);
+ // If we are emitting an array load, we should not be using a
+ // Load Acquire instruction. In other words:
+ // `instruction->IsArrayGet()` => `!use_load_acquire`.
+ DCHECK(!instruction->IsArrayGet() || !use_load_acquire);
MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope temps(masm);
@@ -5196,20 +5226,33 @@
// The actual reference load.
if (index.IsValid()) {
- static_assert(
- sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
- "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- // /* HeapReference<Object> */ ref =
- // *(obj + offset + index * sizeof(HeapReference<Object>))
- const size_t shift_amount = Primitive::ComponentSizeShift(type);
- if (index.IsConstant()) {
- uint32_t computed_offset = offset + (Int64ConstantFrom(index) << shift_amount);
- Load(type, ref_reg, HeapOperand(obj, computed_offset));
+ // Load types involving an "index".
+ if (use_load_acquire) {
+ // UnsafeGetObjectVolatile intrinsic case.
+ // Register `index` is not an index in an object array, but an
+ // offset to an object reference field within object `obj`.
+ DCHECK(instruction->IsInvoke()) << instruction->DebugName();
+ DCHECK(instruction->GetLocations()->Intrinsified());
+ DCHECK(instruction->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)
+ << instruction->AsInvoke()->GetIntrinsic();
+ DCHECK_EQ(offset, 0U);
+ DCHECK_EQ(scale_factor, 0U);
+ DCHECK_EQ(needs_null_check, 0U);
+ // /* HeapReference<Object> */ ref = *(obj + index)
+ MemOperand field = HeapOperand(obj, XRegisterFrom(index));
+ LoadAcquire(instruction, ref_reg, field, /* needs_null_check */ false);
} else {
- temp2 = temps.AcquireW();
- __ Add(temp2, obj, offset);
- Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, shift_amount));
- temps.Release(temp2);
+ // ArrayGet and UnsafeGetObject intrinsics cases.
+ // /* HeapReference<Object> */ ref = *(obj + offset + (index << scale_factor))
+ if (index.IsConstant()) {
+ uint32_t computed_offset = offset + (Int64ConstantFrom(index) << scale_factor);
+ Load(type, ref_reg, HeapOperand(obj, computed_offset));
+ } else {
+ temp2 = temps.AcquireW();
+ __ Add(temp2, obj, offset);
+ Load(type, ref_reg, HeapOperand(temp2, XRegisterFrom(index), LSL, scale_factor));
+ temps.Release(temp2);
+ }
}
} else {
// /* HeapReference<Object> */ ref = *(obj + offset)
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index e6fd336..d4bf695 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -531,6 +531,17 @@
Location index,
vixl::Register temp,
bool needs_null_check);
+ // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ vixl::Register obj,
+ uint32_t offset,
+ Location index,
+ size_t scale_factor,
+ vixl::Register temp,
+ bool needs_null_check,
+ bool use_load_acquire);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
@@ -586,17 +597,6 @@
void GenerateExplicitNullCheck(HNullCheck* instruction);
private:
- // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- vixl::Register obj,
- uint32_t offset,
- Location index,
- vixl::Register temp,
- bool needs_null_check,
- bool use_load_acquire);
-
using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, vixl::Literal<uint64_t>*>;
using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, vixl::Literal<uint32_t>*>;
using MethodToLiteralMap = ArenaSafeMap<MethodReference,
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 52868f4..af10bad 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -448,7 +448,9 @@
instruction_->IsLoadClass() ||
instruction_->IsLoadString() ||
instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast())
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
+ instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
@@ -511,8 +513,12 @@
Register reg_out = out_.AsRegister<Register>();
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out));
- DCHECK(!instruction_->IsInvoke() ||
- (instruction_->IsInvokeStaticOrDirect() &&
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
@@ -525,7 +531,7 @@
// introduce a copy of it, `index`.
Location index = index_;
if (index_.IsValid()) {
- // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
+ // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
if (instruction_->IsArrayGet()) {
// Compute the actual memory offset and store it in `index`.
Register index_reg = index_.AsRegister<Register>();
@@ -573,7 +579,11 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
__ AddImmediate(index_reg, Immediate(offset_));
} else {
- DCHECK(instruction_->IsInvoke());
+ // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
+ // intrinsics, `index_` is not shifted by a scale factor of 2
+ // (as in the case of ArrayGet), as it is actually an offset
+ // to an object field within an object.
+ DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
DCHECK(instruction_->GetLocations()->Intrinsified());
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
@@ -6977,6 +6987,9 @@
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ ref =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
Address src = index.IsConstant() ?
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index fb402be..2a9fb80 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -491,6 +491,14 @@
Location index,
Location temp,
bool needs_null_check);
+ // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ Register obj,
+ const Address& src,
+ Location temp,
+ bool needs_null_check);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
@@ -561,15 +569,6 @@
static constexpr int32_t kDummy32BitOffset = 256;
private:
- // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- Register obj,
- const Address& src,
- Location temp,
- bool needs_null_check);
-
Register GetInvokeStaticOrDirectExtraParameter(HInvokeStaticOrDirect* invoke, Register temp);
struct PcRelativeDexCacheAccessInfo {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 9a3e8d2..2b21454 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -469,7 +469,9 @@
instruction_->IsLoadClass() ||
instruction_->IsLoadString() ||
instruction_->IsInstanceOf() ||
- instruction_->IsCheckCast())
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
+ instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier marking slow path: "
<< instruction_->DebugName();
@@ -532,8 +534,12 @@
CpuRegister reg_out = out_.AsRegister<CpuRegister>();
DCHECK(locations->CanCall());
DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.AsRegister())) << out_;
- DCHECK(!instruction_->IsInvoke() ||
- (instruction_->IsInvokeStaticOrDirect() &&
+ DCHECK(instruction_->IsInstanceFieldGet() ||
+ instruction_->IsStaticFieldGet() ||
+ instruction_->IsArrayGet() ||
+ instruction_->IsInstanceOf() ||
+ instruction_->IsCheckCast() ||
+ ((instruction_->IsInvokeStaticOrDirect() || instruction_->IsInvokeVirtual()) &&
instruction_->GetLocations()->Intrinsified()))
<< "Unexpected instruction in read barrier for heap reference slow path: "
<< instruction_->DebugName();
@@ -546,7 +552,7 @@
// introduce a copy of it, `index`.
Location index = index_;
if (index_.IsValid()) {
- // Handle `index_` for HArrayGet and intrinsic UnsafeGetObject.
+ // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
if (instruction_->IsArrayGet()) {
// Compute real offset and store it in index_.
Register index_reg = index_.AsRegister<CpuRegister>().AsRegister();
@@ -594,7 +600,11 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
__ AddImmediate(CpuRegister(index_reg), Immediate(offset_));
} else {
- DCHECK(instruction_->IsInvoke());
+ // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
+ // intrinsics, `index_` is not shifted by a scale factor of 2
+ // (as in the case of ArrayGet), as it is actually an offset
+ // to an object field within an object.
+ DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
DCHECK(instruction_->GetLocations()->Intrinsified());
DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
(instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
@@ -6430,6 +6440,9 @@
DCHECK(kEmitCompilerReadBarrier);
DCHECK(kUseBakerReadBarrier);
+ static_assert(
+ sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
+ "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ ref =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
Address src = index.IsConstant() ?
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index cf4cc4c..d7cfd37 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -433,6 +433,14 @@
Location index,
Location temp,
bool needs_null_check);
+ // Factored implementation used by GenerateFieldLoadWithBakerReadBarrier
+ // and GenerateArrayLoadWithBakerReadBarrier.
+ void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
+ Location ref,
+ CpuRegister obj,
+ const Address& src,
+ Location temp,
+ bool needs_null_check);
// Generate a read barrier for a heap reference within `instruction`
// using a slow path.
@@ -535,15 +543,6 @@
static constexpr int32_t kDummy32BitOffset = 256;
private:
- // Factored implementation of GenerateFieldLoadWithBakerReadBarrier
- // and GenerateArrayLoadWithBakerReadBarrier.
- void GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
- Location ref,
- CpuRegister obj,
- const Address& src,
- Location temp,
- bool needs_null_check);
-
struct PcRelativeDexCacheAccessInfo {
PcRelativeDexCacheAccessInfo(const DexFile& dex_file, uint32_t element_off)
: target_dex_file(dex_file), element_offset(element_off), label() { }
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 93950d5..f43f8ed 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -47,19 +47,6 @@
if (res == nullptr) {
return false;
}
- if (kEmitCompilerReadBarrier && res->CanCall()) {
- // Generating an intrinsic for this HInvoke may produce an
- // IntrinsicSlowPathARM slow path. Currently this approach
- // does not work when using read barriers, as the emitted
- // calling sequence will make use of another slow path
- // (ReadBarrierForRootSlowPathARM for HInvokeStaticOrDirect,
- // ReadBarrierSlowPathARM for HInvokeVirtual). So we bail
- // out in this case.
- //
- // TODO: Find a way to have intrinsics work with read barriers.
- invoke->SetLocations(nullptr);
- return false;
- }
return res->Intrinsified();
}
@@ -524,8 +511,8 @@
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
Location temp = locations->GetTemp(0);
- codegen->GenerateArrayLoadWithBakerReadBarrier(
- invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ codegen->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke, trg_loc, base, 0U, offset_loc, TIMES_1, temp, /* needs_null_check */ false);
if (is_volatile) {
__ dmb(ISH);
}
@@ -581,10 +568,11 @@
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ locations->SetOut(Location::RequiresRegister(),
+ can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// We need a temporary register for the read barrier marking slow
- // path in InstructionCodeGeneratorARM::GenerateArrayLoadWithBakerReadBarrier.
+ // path in InstructionCodeGeneratorARM::GenerateReferenceLoadWithBakerReadBarrier.
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -919,9 +907,10 @@
// The UnsafeCASObject intrinsic is missing a read barrier, and
// therefore sometimes does not work as expected (b/25883050).
// Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS below).
+ // implemented (see TODO in GenCAS).
//
- // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
if (kEmitCompilerReadBarrier) {
return;
}
@@ -932,6 +921,15 @@
GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_);
}
void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
+ // The UnsafeCASObject intrinsic is missing a read barrier, and
+ // therefore sometimes does not work as expected (b/25883050).
+ // Turn it off temporarily as a quick fix, until the read barrier is
+ // implemented (see TODO in GenCAS).
+ //
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
+ DCHECK(!kEmitCompilerReadBarrier);
+
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
@@ -1335,6 +1333,12 @@
}
void IntrinsicLocationsBuilderARM::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ if (kEmitCompilerReadBarrier) {
+ return;
+ }
+
CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
LocationSummary* locations = invoke->GetLocations();
if (locations == nullptr) {
@@ -1419,11 +1423,11 @@
}
}
-// TODO: Implement read barriers in the SystemArrayCopy intrinsic.
-// Note that this code path is not used (yet) because we do not
-// intrinsify methods that can go into the IntrinsicSlowPathARM
-// slow path.
void IntrinsicCodeGeneratorARM::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ DCHECK(!kEmitCompilerReadBarrier);
+
ArmAssembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 4da0843..1685cf9 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -149,19 +149,6 @@
if (res == nullptr) {
return false;
}
- if (kEmitCompilerReadBarrier && res->CanCall()) {
- // Generating an intrinsic for this HInvoke may produce an
- // IntrinsicSlowPathARM64 slow path. Currently this approach
- // does not work when using read barriers, as the emitted
- // calling sequence will make use of another slow path
- // (ReadBarrierForRootSlowPathARM64 for HInvokeStaticOrDirect,
- // ReadBarrierSlowPathARM64 for HInvokeVirtual). So we bail
- // out in this case.
- //
- // TODO: Find a way to have intrinsics work with read barriers.
- invoke->SetLocations(nullptr);
- return false;
- }
return res->Intrinsified();
}
@@ -791,8 +778,15 @@
// UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
UseScratchRegisterScope temps(masm);
Register temp = temps.AcquireW();
- codegen->GenerateArrayLoadWithBakerReadBarrier(
- invoke, trg_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ codegen->GenerateReferenceLoadWithBakerReadBarrier(invoke,
+ trg_loc,
+ base,
+ /* offset */ 0U,
+ /* index */ offset_loc,
+ /* scale_factor */ 0U,
+ temp,
+ /* needs_null_check */ false,
+ is_volatile);
} else {
// Other cases.
MemOperand mem_op(base.X(), offset);
@@ -821,7 +815,8 @@
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+ locations->SetOut(Location::RequiresRegister(),
+ can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
void IntrinsicLocationsBuilderARM64::VisitUnsafeGet(HInvoke* invoke) {
@@ -1102,9 +1097,10 @@
// The UnsafeCASObject intrinsic is missing a read barrier, and
// therefore sometimes does not work as expected (b/25883050).
// Turn it off temporarily as a quick fix, until the read barrier is
- // implemented (see TODO in GenCAS below).
+ // implemented (see TODO in GenCAS).
//
- // TODO(rpl): Fix this issue and re-enable this intrinsic with read barriers.
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
if (kEmitCompilerReadBarrier) {
return;
}
@@ -1119,6 +1115,15 @@
GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_);
}
void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
+ // The UnsafeCASObject intrinsic is missing a read barrier, and
+ // therefore sometimes does not work as expected (b/25883050).
+ // Turn it off temporarily as a quick fix, until the read barrier is
+ // implemented (see TODO in GenCAS).
+ //
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
+ DCHECK(!kEmitCompilerReadBarrier);
+
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
@@ -2012,6 +2017,12 @@
// We want to use two temporary registers in order to reduce the register pressure in arm64.
// So we don't use the CodeGenerator::CreateSystemArrayCopyLocationSummary.
void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ if (kEmitCompilerReadBarrier) {
+ return;
+ }
+
// Check to see if we have known failures that will cause us to have to bail out
// to the runtime, and just generate the runtime call directly.
HIntConstant* src_pos = invoke->InputAt(1)->AsIntConstant();
@@ -2064,6 +2075,10 @@
}
void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ DCHECK(!kEmitCompilerReadBarrier);
+
vixl::MacroAssembler* masm = GetVIXLAssembler();
LocationSummary* locations = invoke->GetLocations();
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 4988398..031cd13 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -60,19 +60,6 @@
if (res == nullptr) {
return false;
}
- if (kEmitCompilerReadBarrier && res->CanCall()) {
- // Generating an intrinsic for this HInvoke may produce an
- // IntrinsicSlowPathX86 slow path. Currently this approach
- // does not work when using read barriers, as the emitted
- // calling sequence will make use of another slow path
- // (ReadBarrierForRootSlowPathX86 for HInvokeStaticOrDirect,
- // ReadBarrierSlowPathX86 for HInvokeVirtual). So we bail
- // out in this case.
- //
- // TODO: Find a way to have intrinsics work with read barriers.
- invoke->SetLocations(nullptr);
- return false;
- }
return res->Intrinsified();
}
@@ -1822,8 +1809,9 @@
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
Location temp = locations->GetTemp(0);
- codegen->GenerateArrayLoadWithBakerReadBarrier(
- invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ Address src(base, offset, ScaleFactor::TIMES_1, 0);
+ codegen->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke, output_loc, base, src, temp, /* needs_null_check */ false);
} else {
__ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
codegen->GenerateReadBarrierSlow(
@@ -1878,16 +1866,17 @@
if (is_volatile) {
// Need to use XMM to read volatile.
locations->AddTemp(Location::RequiresFpuRegister());
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
} else {
locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
}
} else {
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(),
+ can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
}
if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// We need a temporary register for the read barrier marking slow
- // path in InstructionCodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier.
+ // path in InstructionCodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier.
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -2109,9 +2098,9 @@
// The UnsafeCASObject intrinsic is missing a read barrier, and
// therefore sometimes does not work as expected (b/25883050).
// Turn it off temporarily as a quick fix, until the read barrier is
- // implemented.
+ // implemented (see TODO in GenCAS).
//
- // TODO(rpl): Implement a read barrier in GenCAS below and re-enable
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
// this intrinsic.
if (kEmitCompilerReadBarrier) {
return;
@@ -2236,6 +2225,15 @@
}
void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) {
+ // The UnsafeCASObject intrinsic is missing a read barrier, and
+ // therefore sometimes does not work as expected (b/25883050).
+ // Turn it off temporarily as a quick fix, until the read barrier is
+ // implemented (see TODO in GenCAS).
+ //
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
+ DCHECK(!kEmitCompilerReadBarrier);
+
GenCAS(Primitive::kPrimNot, invoke, codegen_);
}
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 405b4c0..c5b44d4 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -54,19 +54,6 @@
if (res == nullptr) {
return false;
}
- if (kEmitCompilerReadBarrier && res->CanCall()) {
- // Generating an intrinsic for this HInvoke may produce an
- // IntrinsicSlowPathX86_64 slow path. Currently this approach
- // does not work when using read barriers, as the emitted
- // calling sequence will make use of another slow path
- // (ReadBarrierForRootSlowPathX86_64 for HInvokeStaticOrDirect,
- // ReadBarrierSlowPathX86_64 for HInvokeVirtual). So we bail
- // out in this case.
- //
- // TODO: Find a way to have intrinsics work with read barriers.
- invoke->SetLocations(nullptr);
- return false;
- }
return res->Intrinsified();
}
@@ -1079,14 +1066,20 @@
void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ if (kEmitCompilerReadBarrier) {
+ return;
+ }
+
CodeGenerator::CreateSystemArrayCopyLocationSummary(invoke);
}
-// TODO: Implement read barriers in the SystemArrayCopy intrinsic.
-// Note that this code path is not used (yet) because we do not
-// intrinsify methods that can go into the IntrinsicSlowPathX86_64
-// slow path.
void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
+ // TODO(rpl): Implement read barriers in the SystemArrayCopy
+ // intrinsic and re-enable it (b/29516905).
+ DCHECK(!kEmitCompilerReadBarrier);
+
X86_64Assembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -1910,8 +1903,9 @@
if (kEmitCompilerReadBarrier) {
if (kUseBakerReadBarrier) {
Location temp = locations->GetTemp(0);
- codegen->GenerateArrayLoadWithBakerReadBarrier(
- invoke, output_loc, base, 0U, offset_loc, temp, /* needs_null_check */ false);
+ Address src(base, offset, ScaleFactor::TIMES_1, 0);
+ codegen->GenerateReferenceLoadWithBakerReadBarrier(
+ invoke, output_loc, base, src, temp, /* needs_null_check */ false);
} else {
__ movl(output, Address(base, offset, ScaleFactor::TIMES_1, 0));
codegen->GenerateReadBarrierSlow(
@@ -1948,10 +1942,11 @@
locations->SetInAt(0, Location::NoLocation()); // Unused receiver.
locations->SetInAt(1, Location::RequiresRegister());
locations->SetInAt(2, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(),
+ can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap);
if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
// We need a temporary register for the read barrier marking slow
- // path in InstructionCodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier.
+ // path in InstructionCodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier.
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -2135,9 +2130,9 @@
// The UnsafeCASObject intrinsic is missing a read barrier, and
// therefore sometimes does not work as expected (b/25883050).
// Turn it off temporarily as a quick fix, until the read barrier is
- // implemented.
+ // implemented (see TODO in GenCAS).
//
- // TODO(rpl): Implement a read barrier in GenCAS below and re-enable
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
// this intrinsic.
if (kEmitCompilerReadBarrier) {
return;
@@ -2253,6 +2248,15 @@
}
void IntrinsicCodeGeneratorX86_64::VisitUnsafeCASObject(HInvoke* invoke) {
+ // The UnsafeCASObject intrinsic is missing a read barrier, and
+ // therefore sometimes does not work as expected (b/25883050).
+ // Turn it off temporarily as a quick fix, until the read barrier is
+ // implemented (see TODO in GenCAS).
+ //
+ // TODO(rpl): Implement read barrier support in GenCAS and re-enable
+ // this intrinsic.
+ DCHECK(!kEmitCompilerReadBarrier);
+
GenCAS(Primitive::kPrimNot, invoke, codegen_);
}
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index 63bbc2c..3f27c91 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -38,7 +38,13 @@
class Location : public ValueObject {
public:
enum OutputOverlap {
+ // The liveness of the output overlaps the liveness of one or
+ // several input(s); the register allocator cannot reuse an
+ // input's location for the output's location.
kOutputOverlap,
+ // The liveness of the output does not overlap the liveness of any
+ // input; the register allocator is allowed to reuse an input's
+ // location for the output's location.
kNoOutputOverlap
};
@@ -494,6 +500,10 @@
return inputs_.size();
}
+ // Set the output location. Argument `overlaps` tells whether the
+ // output overlaps any of the inputs (if so, it cannot share the
+ // same register as one of the inputs); it is set to
+ // `Location::kOutputOverlap` by default for safety.
void SetOut(Location location, Location::OutputOverlap overlaps = Location::kOutputOverlap) {
DCHECK(output_.IsInvalid());
output_overlaps_ = overlaps;
diff --git a/compiler/utils/string_reference.h b/compiler/utils/string_reference.h
index 9e1058e..0ab45c8 100644
--- a/compiler/utils/string_reference.h
+++ b/compiler/utils/string_reference.h
@@ -20,12 +20,11 @@
#include <stdint.h>
#include "base/logging.h"
+#include "dex_file-inl.h"
#include "utf-inl.h"
namespace art {
-class DexFile;
-
// A string is located by its DexFile and the string_ids_ table index into that DexFile.
struct StringReference {
StringReference(const DexFile* file, uint32_t index) : dex_file(file), string_index(index) { }
@@ -48,13 +47,13 @@
sr1.string_index < sr2.string_index,
CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(
sr1.dex_file->GetStringData(sr1.dex_file->GetStringId(sr1.string_index)),
- sr1.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0);
+ sr2.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0);
return sr1.string_index < sr2.string_index;
} else {
// Cannot compare indexes, so do the string comparison.
return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(
sr1.dex_file->GetStringData(sr1.dex_file->GetStringId(sr1.string_index)),
- sr1.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0;
+ sr2.dex_file->GetStringData(sr2.dex_file->GetStringId(sr2.string_index))) < 0;
}
}
};
diff --git a/compiler/utils/string_reference_test.cc b/compiler/utils/string_reference_test.cc
new file mode 100644
index 0000000..df5080e
--- /dev/null
+++ b/compiler/utils/string_reference_test.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/string_reference.h"
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "utils/test_dex_file_builder.h"
+
+namespace art {
+
+TEST(StringReference, ValueComparator) {
+ // This is a regression test for the StringReferenceValueComparator using the wrong
+ // dex file to get the string data from a StringId. We construct two dex files with
+ // just a single string with the same length but different value. This creates dex
+ // files that have the same layout, so the byte offset read from the StringId in one
+ // dex file, when used in the other dex file still points to valid string data, except
+ // that it's the wrong string. Without the fix the strings would then compare equal.
+ TestDexFileBuilder builder1;
+ builder1.AddString("String1");
+ std::unique_ptr<const DexFile> dex_file1 = builder1.Build("dummy location 1");
+ ASSERT_EQ(1u, dex_file1->NumStringIds());
+ ASSERT_STREQ("String1", dex_file1->GetStringData(dex_file1->GetStringId(0)));
+ StringReference sr1(dex_file1.get(), 0);
+
+ TestDexFileBuilder builder2;
+ builder2.AddString("String2");
+ std::unique_ptr<const DexFile> dex_file2 = builder2.Build("dummy location 2");
+ ASSERT_EQ(1u, dex_file2->NumStringIds());
+ ASSERT_STREQ("String2", dex_file2->GetStringData(dex_file2->GetStringId(0)));
+ StringReference sr2(dex_file2.get(), 0);
+
+ StringReferenceValueComparator cmp;
+ EXPECT_TRUE(cmp(sr1, sr2)); // "String1" < "String2" is true.
+ EXPECT_FALSE(cmp(sr2, sr1)); // "String2" < "String1" is false.
+}
+
+TEST(StringReference, ValueComparator2) {
+ const char* const kDexFile1Strings[] = {
+ "",
+ "abc",
+ "abcxyz",
+ };
+ const char* const kDexFile2Strings[] = {
+ "a",
+ "abc",
+ "abcdef",
+ "def",
+ };
+ const bool expectedCmp12[arraysize(kDexFile1Strings)][arraysize(kDexFile2Strings)] = {
+ { true, true, true, true },
+ { false, false, true, true },
+ { false, false, false, true },
+ };
+ const bool expectedCmp21[arraysize(kDexFile2Strings)][arraysize(kDexFile1Strings)] = {
+ { false, true, true },
+ { false, false, true },
+ { false, false, true },
+ { false, false, false },
+ };
+
+ TestDexFileBuilder builder1;
+ for (const char* s : kDexFile1Strings) {
+ builder1.AddString(s);
+ }
+ std::unique_ptr<const DexFile> dex_file1 = builder1.Build("dummy location 1");
+ ASSERT_EQ(arraysize(kDexFile1Strings), dex_file1->NumStringIds());
+ for (size_t index = 0; index != arraysize(kDexFile1Strings); ++index) {
+ ASSERT_STREQ(kDexFile1Strings[index], dex_file1->GetStringData(dex_file1->GetStringId(index)));
+ }
+
+ TestDexFileBuilder builder2;
+ for (const char* s : kDexFile2Strings) {
+ builder2.AddString(s);
+ }
+ std::unique_ptr<const DexFile> dex_file2 = builder2.Build("dummy location 1");
+ ASSERT_EQ(arraysize(kDexFile2Strings), dex_file2->NumStringIds());
+ for (size_t index = 0; index != arraysize(kDexFile2Strings); ++index) {
+ ASSERT_STREQ(kDexFile2Strings[index], dex_file2->GetStringData(dex_file2->GetStringId(index)));
+ }
+
+ StringReferenceValueComparator cmp;
+ for (size_t index1 = 0; index1 != arraysize(kDexFile1Strings); ++index1) {
+ for (size_t index2 = 0; index2 != arraysize(kDexFile2Strings); ++index2) {
+ StringReference sr1(dex_file1.get(), index1);
+ StringReference sr2(dex_file2.get(), index2);
+ EXPECT_EQ(expectedCmp12[index1][index2], cmp(sr1, sr2)) << index1 << " " << index2;
+ EXPECT_EQ(expectedCmp21[index2][index1], cmp(sr2, sr1)) << index1 << " " << index2;
+ }
+ }
+}
+
+} // namespace art
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index cf9b8eb..96f2098 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -34,7 +34,7 @@
namespace mirror {
inline uint32_t String::ClassSize(size_t pointer_size) {
- uint32_t vtable_entries = Object::kVTableLength + 56;
+ uint32_t vtable_entries = Object::kVTableLength + 57;
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size);
}
diff --git a/test/004-UnsafeTest/src/Main.java b/test/004-UnsafeTest/src/Main.java
index b2f905e..9d4618a 100644
--- a/test/004-UnsafeTest/src/Main.java
+++ b/test/004-UnsafeTest/src/Main.java
@@ -39,16 +39,24 @@
}
}
- private static Unsafe getUnsafe() throws Exception {
+ private static Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
Class<?> unsafeClass = Unsafe.class;
Field f = unsafeClass.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
}
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
System.loadLibrary(args[0]);
Unsafe unsafe = getUnsafe();
+
+ testArrayBaseOffset(unsafe);
+ testArrayIndexScale(unsafe);
+ testGetAndPutAndCAS(unsafe);
+ testGetAndPutVolatile(unsafe);
+ }
+
+ private static void testArrayBaseOffset(Unsafe unsafe) {
check(unsafe.arrayBaseOffset(boolean[].class), vmArrayBaseOffset(boolean[].class),
"Unsafe.arrayBaseOffset(boolean[])");
check(unsafe.arrayBaseOffset(byte[].class), vmArrayBaseOffset(byte[].class),
@@ -65,7 +73,9 @@
"Unsafe.arrayBaseOffset(long[])");
check(unsafe.arrayBaseOffset(Object[].class), vmArrayBaseOffset(Object[].class),
"Unsafe.arrayBaseOffset(Object[])");
+ }
+ private static void testArrayIndexScale(Unsafe unsafe) {
check(unsafe.arrayIndexScale(boolean[].class), vmArrayIndexScale(boolean[].class),
"Unsafe.arrayIndexScale(boolean[])");
check(unsafe.arrayIndexScale(byte[].class), vmArrayIndexScale(byte[].class),
@@ -82,7 +92,9 @@
"Unsafe.arrayIndexScale(long[])");
check(unsafe.arrayIndexScale(Object[].class), vmArrayIndexScale(Object[].class),
"Unsafe.arrayIndexScale(Object[])");
+ }
+ private static void testGetAndPutAndCAS(Unsafe unsafe) throws NoSuchFieldException {
TestClass t = new TestClass();
int intValue = 12345678;
@@ -185,12 +197,58 @@
}
}
+ private static void testGetAndPutVolatile(Unsafe unsafe) throws NoSuchFieldException {
+ TestVolatileClass tv = new TestVolatileClass();
+
+ int intValue = 12345678;
+ Field volatileIntField = TestVolatileClass.class.getDeclaredField("volatileIntVar");
+ long volatileIntOffset = unsafe.objectFieldOffset(volatileIntField);
+ check(unsafe.getIntVolatile(tv, volatileIntOffset),
+ 0,
+ "Unsafe.getIntVolatile(Object, long) - initial");
+ unsafe.putIntVolatile(tv, volatileIntOffset, intValue);
+ check(tv.volatileIntVar, intValue, "Unsafe.putIntVolatile(Object, long, int)");
+ check(unsafe.getIntVolatile(tv, volatileIntOffset),
+ intValue,
+ "Unsafe.getIntVolatile(Object, long)");
+
+ long longValue = 1234567887654321L;
+ Field volatileLongField = TestVolatileClass.class.getDeclaredField("volatileLongVar");
+ long volatileLongOffset = unsafe.objectFieldOffset(volatileLongField);
+ check(unsafe.getLongVolatile(tv, volatileLongOffset),
+ 0,
+ "Unsafe.getLongVolatile(Object, long) - initial");
+ unsafe.putLongVolatile(tv, volatileLongOffset, longValue);
+ check(tv.volatileLongVar, longValue, "Unsafe.putLongVolatile(Object, long, long)");
+ check(unsafe.getLongVolatile(tv, volatileLongOffset),
+ longValue,
+ "Unsafe.getLongVolatile(Object, long)");
+
+ Object objectValue = new Object();
+ Field volatileObjectField = TestVolatileClass.class.getDeclaredField("volatileObjectVar");
+ long volatileObjectOffset = unsafe.objectFieldOffset(volatileObjectField);
+ check(unsafe.getObjectVolatile(tv, volatileObjectOffset),
+ null,
+ "Unsafe.getObjectVolatile(Object, long) - initial");
+ unsafe.putObjectVolatile(tv, volatileObjectOffset, objectValue);
+ check(tv.volatileObjectVar, objectValue, "Unsafe.putObjectVolatile(Object, long, Object)");
+ check(unsafe.getObjectVolatile(tv, volatileObjectOffset),
+ objectValue,
+ "Unsafe.getObjectVolatile(Object, long)");
+ }
+
private static class TestClass {
public int intVar = 0;
public long longVar = 0;
public Object objectVar = null;
}
+ private static class TestVolatileClass {
+ public volatile int volatileIntVar = 0;
+ public volatile long volatileLongVar = 0;
+ public volatile Object volatileObjectVar = null;
+ }
+
private static native int vmArrayBaseOffset(Class clazz);
private static native int vmArrayIndexScale(Class clazz);
}
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index d878e69..dd89d64 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -33,7 +33,7 @@
14 (class java.lang.Short)
[java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
-[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int)]
+[native void java.lang.String.getCharsNoCheck(int,int,char[],int), native void java.lang.String.setCharAt(int,char), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native int java.lang.String.fastIndexOf(int,int), private native java.lang.String java.lang.String.fastSubstring(int,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), void java.lang.String.getChars(char[],int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
0
diff --git a/test/201-built-in-exception-detail-messages/src/Main.java b/test/201-built-in-exception-detail-messages/src/Main.java
index 52d4259..f0bb6dd 100644
--- a/test/201-built-in-exception-detail-messages/src/Main.java
+++ b/test/201-built-in-exception-detail-messages/src/Main.java
@@ -461,7 +461,7 @@
"hello there".substring(9,14);
fail();
} catch (StringIndexOutOfBoundsException ex) {
- assertEquals("length=11; regionStart=9; regionLength=5", ex.getMessage());
+ assertEquals("length=11; index=14", ex.getMessage());
}
}
}