summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Marko <vmarko@google.com>2016-04-28 15:52:11 +0100
committerVladimir Marko <vmarko@google.com>2016-06-21 15:17:38 +0100
commit87f3fcbd0db352157fc59148e94647ef21b73bce (patch)
tree5bdeabb246f5de86704333b3fcbccc6e9146d246
parentb94b5706f0b8e2e1c7e1db22274f9f4bae0c4b5a (diff)
downloadart-87f3fcbd0db352157fc59148e94647ef21b73bce.tar.gz
art-87f3fcbd0db352157fc59148e94647ef21b73bce.tar.bz2
art-87f3fcbd0db352157fc59148e94647ef21b73bce.zip
Replace String.charAt() with HIR.
Replace String.charAt() with HArrayLength, HBoundsCheck and HArrayGet. This allows GVN on the HArrayLength and BCE on the HBoundsCheck as well as using the infrastructure for HArrayGet, i.e. better handling of constant indexes than the old intrinsic and using the HArm64IntermediateAddress. Bug: 28330359 Change-Id: I32bf1da7eeafe82537a60416abf6ac412baa80dc
-rw-r--r--compiler/oat_test.cc2
-rw-r--r--compiler/optimizing/code_generator.cc7
-rw-r--r--compiler/optimizing/code_generator.h5
-rw-r--r--compiler/optimizing/code_generator_arm.cc17
-rw-r--r--compiler/optimizing/code_generator_arm64.cc9
-rw-r--r--compiler/optimizing/code_generator_mips.cc17
-rw-r--r--compiler/optimizing/code_generator_mips64.cc17
-rw-r--r--compiler/optimizing/code_generator_x86.cc16
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc16
-rw-r--r--compiler/optimizing/graph_visualizer.cc10
-rw-r--r--compiler/optimizing/instruction_simplifier.cc27
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.cc13
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.h2
-rw-r--r--compiler/optimizing/intrinsics.h1
-rw-r--r--compiler/optimizing/intrinsics_arm.cc49
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc50
-rw-r--r--compiler/optimizing/intrinsics_mips.cc48
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc46
-rw-r--r--compiler/optimizing/intrinsics_x86.cc42
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc43
-rw-r--r--compiler/optimizing/nodes.h52
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.cc16
-rw-r--r--compiler/optimizing/stack_map_stream.cc7
-rw-r--r--compiler/optimizing/stack_map_stream.h2
-rw-r--r--runtime/arch/arm/quick_entrypoints_arm.S6
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S6
-rw-r--r--runtime/arch/mips/entrypoints_init_mips.cc2
-rw-r--r--runtime/arch/mips/quick_entrypoints_mips.S12
-rw-r--r--runtime/arch/mips64/quick_entrypoints_mips64.S13
-rw-r--r--runtime/arch/x86/quick_entrypoints_x86.S6
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S6
-rw-r--r--runtime/asm_support.h18
-rw-r--r--runtime/common_throws.cc7
-rw-r--r--runtime/common_throws.h5
-rw-r--r--runtime/entrypoints/quick/quick_default_externs.h1
-rw-r--r--runtime/entrypoints/quick/quick_default_init_entrypoints.h1
-rw-r--r--runtime/entrypoints/quick/quick_entrypoints_list.h1
-rw-r--r--runtime/entrypoints/quick/quick_throw_entrypoints.cc16
-rw-r--r--runtime/entrypoints_order_test.cc7
-rw-r--r--runtime/mirror/string-inl.h5
-rw-r--r--runtime/oat.h2
-rw-r--r--runtime/stack_map.h7
-rw-r--r--runtime/thread.h6
-rw-r--r--test/536-checker-intrinsic-optimization/src/Main.java201
44 files changed, 469 insertions, 373 deletions
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 6d1f94491e..672018b355 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -444,7 +444,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) {
EXPECT_EQ(72U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(20U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(132 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
+ EXPECT_EQ(133 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints));
}
TEST_F(OatTest, OatHeaderIsValid) {
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 6e851bf1ba..12aa15207c 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -146,6 +146,13 @@ uint32_t CodeGenerator::GetArrayLengthOffset(HArrayLength* array_length) {
: mirror::Array::LengthOffset().Uint32Value();
}
+uint32_t CodeGenerator::GetArrayDataOffset(HArrayGet* array_get) {
+ DCHECK(array_get->GetType() == Primitive::kPrimChar || !array_get->IsStringCharAt());
+ return array_get->IsStringCharAt()
+ ? mirror::String::ValueOffset().Uint32Value()
+ : mirror::Array::DataOffset(Primitive::ComponentSize(array_get->GetType())).Uint32Value();
+}
+
bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const {
DCHECK_EQ((*block_order_)[current_block_index_], current);
return GetNextBlockToEmit() == FirstNonEmptyBlock(next);
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 82a54d2ed1..f88ed9176a 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -345,6 +345,11 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> {
// accessing the String's `count` field in String intrinsics.
static uint32_t GetArrayLengthOffset(HArrayLength* array_length);
+ // Helper that returns the offset of the array's data.
+ // Note: Besides the normal arrays, we also use the HArrayGet for
+ // accessing the String's `value` field in String intrinsics.
+ static uint32_t GetArrayDataOffset(HArrayGet* array_get);
+
void EmitParallelMoves(Location from1,
Location to1,
Primitive::Type type1,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index eca9e2c299..e629a8e7a5 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -176,8 +176,11 @@ class BoundsCheckSlowPathARM : public SlowPathCode {
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- arm_codegen->InvokeRuntime(
- QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this);
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ arm_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -4286,11 +4289,11 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
Register obj = obj_loc.AsRegister<Register>();
Location index = locations->InAt(1);
Location out_loc = locations->Out();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4304,7 +4307,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4318,7 +4320,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4332,7 +4333,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4346,7 +4346,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -4363,7 +4362,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
@@ -4398,7 +4396,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
if (index.IsConstant()) {
size_t offset =
(index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
@@ -4411,7 +4408,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
SRegister out = out_loc.AsFpuRegister<SRegister>();
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
@@ -4424,7 +4420,6 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
SRegister out = out_loc.AsFpuRegisterPairLow<SRegister>();
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 5d3c8c5590..4bdfd57e7b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -237,8 +237,11 @@ class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
codegen->EmitParallelMoves(
locations->InAt(0), LocationFrom(calling_convention.GetRegisterAt(0)), Primitive::kPrimInt,
locations->InAt(1), LocationFrom(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt);
- arm64_codegen->InvokeRuntime(
- QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc(), this);
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ arm64_codegen->InvokeRuntime(entry_point_offset, instruction_, instruction_->GetDexPc(), this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -2051,8 +2054,8 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
Register obj = InputRegisterAt(instruction, 0);
LocationSummary* locations = instruction->GetLocations();
Location index = locations->InAt(1);
- uint32_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(type)).Uint32Value();
Location out = locations->Out();
+ uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope temps(masm);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index d5bad28dab..f48db1c02b 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -166,11 +166,15 @@ class BoundsCheckSlowPathMIPS : public SlowPathCodeMIPS {
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- mips_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds),
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ mips_codegen->InvokeRuntime(entry_point_offset,
instruction_,
instruction_->GetDexPc(),
this,
IsDirectEntrypoint(kQuickThrowArrayBounds));
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -1635,11 +1639,11 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
Register obj = locations->InAt(0).AsRegister<Register>();
Location index = locations->InAt(1);
- Primitive::Type type = instruction->GetType();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
+ Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1653,7 +1657,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1667,7 +1670,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1682,7 +1684,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1699,7 +1700,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimInt:
case Primitive::kPrimNot: {
DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
Register out = locations->Out().AsRegister<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1714,7 +1714,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
Register out = locations->Out().AsRegisterPairLow<Register>();
if (index.IsConstant()) {
size_t offset =
@@ -1729,7 +1728,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
FRegister out = locations->Out().AsFpuRegister<FRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1744,7 +1742,6 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
FRegister out = locations->Out().AsFpuRegister<FRegister>();
if (index.IsConstant()) {
size_t offset =
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 539abf1de8..c6a0c369bc 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -127,10 +127,14 @@ class BoundsCheckSlowPathMIPS64 : public SlowPathCodeMIPS64 {
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- mips64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds),
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ mips64_codegen->InvokeRuntime(entry_point_offset,
instruction_,
instruction_->GetDexPc(),
this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -1289,11 +1293,11 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
Location index = locations->InAt(1);
- Primitive::Type type = instruction->GetType();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
+ Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1307,7 +1311,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1321,7 +1324,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1336,7 +1338,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1353,7 +1354,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
case Primitive::kPrimInt:
case Primitive::kPrimNot: {
DCHECK_EQ(sizeof(mirror::HeapReference<mirror::Object>), sizeof(int32_t));
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
LoadOperandType load_type = (type == Primitive::kPrimNot) ? kLoadUnsignedWord : kLoadWord;
if (index.IsConstant()) {
@@ -1369,7 +1369,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
GpuRegister out = locations->Out().AsRegister<GpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1384,7 +1383,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
if (index.IsConstant()) {
size_t offset =
@@ -1399,7 +1397,6 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
FpuRegister out = locations->Out().AsFpuRegister<FpuRegister>();
if (index.IsConstant()) {
size_t offset =
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index a21c295274..c6a727d584 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -148,10 +148,14 @@ class BoundsCheckSlowPathX86 : public SlowPathCode {
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- x86_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds),
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ x86_codegen->InvokeRuntime(entry_point_offset,
instruction_,
instruction_->GetDexPc(),
this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -5040,11 +5044,11 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
Register obj = obj_loc.AsRegister<Register>();
Location index = locations->InAt(1);
Location out_loc = locations->Out();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movzxb(out, Address(obj,
@@ -5056,7 +5060,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movsxb(out, Address(obj,
@@ -5068,7 +5071,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movsxw(out, Address(obj,
@@ -5080,7 +5082,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movzxw(out, Address(obj,
@@ -5092,7 +5093,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
Register out = out_loc.AsRegister<Register>();
if (index.IsConstant()) {
__ movl(out, Address(obj,
@@ -5107,7 +5107,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
@@ -5141,7 +5140,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
DCHECK_NE(obj, out_loc.AsRegisterPairLow<Register>());
if (index.IsConstant()) {
size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
@@ -5159,7 +5157,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movss(out, Address(obj,
@@ -5171,7 +5168,6 @@ void InstructionCodeGeneratorX86::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movsd(out, Address(obj,
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 135f0c40d0..3f8a32aeda 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -204,10 +204,14 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode {
locations->InAt(1),
Location::RegisterLocation(calling_convention.GetRegisterAt(1)),
Primitive::kPrimInt);
- x86_64_codegen->InvokeRuntime(QUICK_ENTRY_POINT(pThrowArrayBounds),
+ uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt()
+ ? QUICK_ENTRY_POINT(pThrowStringBounds)
+ : QUICK_ENTRY_POINT(pThrowArrayBounds);
+ x86_64_codegen->InvokeRuntime(entry_point_offset,
instruction_,
instruction_->GetDexPc(),
this);
+ CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
}
@@ -4540,11 +4544,11 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
CpuRegister obj = obj_loc.AsRegister<CpuRegister>();
Location index = locations->InAt(1);
Location out_loc = locations->Out();
+ uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Primitive::Type type = instruction->GetType();
switch (type) {
case Primitive::kPrimBoolean: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movzxb(out, Address(obj,
@@ -4556,7 +4560,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimByte: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int8_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movsxb(out, Address(obj,
@@ -4568,7 +4571,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimShort: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int16_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movsxw(out, Address(obj,
@@ -4580,7 +4582,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimChar: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movzxw(out, Address(obj,
@@ -4592,7 +4593,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimInt: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movl(out, Address(obj,
@@ -4607,7 +4607,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
@@ -4641,7 +4640,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimLong: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
CpuRegister out = out_loc.AsRegister<CpuRegister>();
if (index.IsConstant()) {
__ movq(out, Address(obj,
@@ -4653,7 +4651,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimFloat: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movss(out, Address(obj,
@@ -4665,7 +4662,6 @@ void InstructionCodeGeneratorX86_64::VisitArrayGet(HArrayGet* instruction) {
}
case Primitive::kPrimDouble: {
- uint32_t data_offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
XmmRegister out = out_loc.AsFpuRegister<XmmRegister>();
if (index.IsConstant()) {
__ movsd(out, Address(obj,
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 3084a4ff2b..048ef3676e 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -399,6 +399,16 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
<< array_length->IsStringLength() << std::noboolalpha;
}
+ void VisitBoundsCheck(HBoundsCheck* bounds_check) OVERRIDE {
+ StartAttributeStream("is_string_char_at") << std::boolalpha
+ << bounds_check->IsStringCharAt() << std::noboolalpha;
+ }
+
+ void VisitArrayGet(HArrayGet* array_get) OVERRIDE {
+ StartAttributeStream("is_string_char_at") << std::boolalpha
+ << array_get->IsStringCharAt() << std::noboolalpha;
+ }
+
void VisitArraySet(HArraySet* array_set) OVERRIDE {
StartAttributeStream("value_can_be_null") << std::boolalpha
<< array_set->GetValueCanBeNull() << std::noboolalpha;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index eb1d1560db..62d637081d 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -101,6 +101,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void SimplifyCompare(HInvoke* invoke, bool is_signum, Primitive::Type type);
void SimplifyIsNaN(HInvoke* invoke);
void SimplifyFP2Int(HInvoke* invoke);
+ void SimplifyStringCharAt(HInvoke* invoke);
void SimplifyStringIsEmptyOrLength(HInvoke* invoke);
void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
@@ -1685,13 +1686,32 @@ void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) {
invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0
}
+void InstructionSimplifierVisitor::SimplifyStringCharAt(HInvoke* invoke) {
+ HInstruction* str = invoke->InputAt(0);
+ HInstruction* index = invoke->InputAt(1);
+ uint32_t dex_pc = invoke->GetDexPc();
+ ArenaAllocator* arena = GetGraph()->GetArena();
+ // We treat String as an array to allow DCE and BCE to seamlessly work on strings,
+ // so create the HArrayLength, HBoundsCheck and HArrayGet.
+ HArrayLength* length = new (arena) HArrayLength(str, dex_pc, /* is_string_length */ true);
+ invoke->GetBlock()->InsertInstructionBefore(length, invoke);
+ HBoundsCheck* bounds_check =
+ new (arena) HBoundsCheck(index, length, dex_pc, invoke->GetDexMethodIndex());
+ invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke);
+ HArrayGet* array_get =
+ new (arena) HArrayGet(str, index, Primitive::kPrimChar, dex_pc, /* is_string_char_at */ true);
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, array_get);
+ bounds_check->CopyEnvironmentFrom(invoke->GetEnvironment());
+ GetGraph()->SetHasBoundsChecks(true);
+}
+
void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke) {
HInstruction* str = invoke->InputAt(0);
uint32_t dex_pc = invoke->GetDexPc();
// We treat String as an array to allow DCE and BCE to seamlessly work on strings,
// so create the HArrayLength.
- HArrayLength* length = new (GetGraph()->GetArena()) HArrayLength(str, dex_pc);
- length->MarkAsStringLength();
+ HArrayLength* length =
+ new (GetGraph()->GetArena()) HArrayLength(str, dex_pc, /* is_string_length */ true);
HInstruction* replacement;
if (invoke->GetIntrinsic() == Intrinsics::kStringIsEmpty) {
// For String.isEmpty(), create the `HEqual` representing the `length == 0`.
@@ -1752,6 +1772,9 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
case Intrinsics::kDoubleDoubleToLongBits:
SimplifyFP2Int(instruction);
break;
+ case Intrinsics::kStringCharAt:
+ SimplifyStringCharAt(instruction);
+ break;
case Intrinsics::kStringIsEmpty:
case Intrinsics::kStringLength:
SimplifyStringIsEmptyOrLength(instruction);
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index e4a711ec83..983d31d168 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -19,6 +19,7 @@
#include "common_arm64.h"
#include "instruction_simplifier_shared.h"
#include "mirror/array-inl.h"
+#include "mirror/string.h"
namespace art {
namespace arm64 {
@@ -30,7 +31,7 @@ using helpers::ShifterOperandSupportsExtension;
void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstruction* access,
HInstruction* array,
HInstruction* index,
- int access_size) {
+ size_t data_offset) {
if (kEmitCompilerReadBarrier) {
// The read barrier instrumentation does not support the
// HArm64IntermediateAddress instruction yet.
@@ -55,8 +56,7 @@ void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstructio
// Proceed to extract the base address computation.
ArenaAllocator* arena = GetGraph()->GetArena();
- HIntConstant* offset =
- GetGraph()->GetIntConstant(mirror::Array::DataOffset(access_size).Uint32Value());
+ HIntConstant* offset = GetGraph()->GetIntConstant(data_offset);
HArm64IntermediateAddress* address =
new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc);
address->SetReferenceTypeInfo(array->GetReferenceTypeInfo());
@@ -189,17 +189,20 @@ void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) {
}
void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) {
+ size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
TryExtractArrayAccessAddress(instruction,
instruction->GetArray(),
instruction->GetIndex(),
- Primitive::ComponentSize(instruction->GetType()));
+ data_offset);
}
void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) {
+ size_t access_size = Primitive::ComponentSize(instruction->GetComponentType());
+ size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value();
TryExtractArrayAccessAddress(instruction,
instruction->GetArray(),
instruction->GetIndex(),
- Primitive::ComponentSize(instruction->GetComponentType()));
+ data_offset);
}
void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) {
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index da269980e8..4735f85ab0 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -38,7 +38,7 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor {
void TryExtractArrayAccessAddress(HInstruction* access,
HInstruction* array,
HInstruction* index,
- int access_size);
+ size_t data_offset);
bool TryMergeIntoUsersShifterOperand(HInstruction* instruction);
bool TryMergeIntoShifterOperand(HInstruction* use,
HInstruction* bitfield_op,
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 83a512738b..3429a8fdbb 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -239,6 +239,7 @@ UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \
UNREACHABLE_INTRINSIC(Arch, LongCompare) \
UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \
UNREACHABLE_INTRINSIC(Arch, LongSignum) \
+UNREACHABLE_INTRINSIC(Arch, StringCharAt) \
UNREACHABLE_INTRINSIC(Arch, StringIsEmpty) \
UNREACHABLE_INTRINSIC(Arch, StringLength) \
UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 7d1c2ebe0b..93950d58b5 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -935,55 +935,6 @@ void IntrinsicCodeGeneratorARM::VisitUnsafeCASObject(HInvoke* invoke) {
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
-void IntrinsicLocationsBuilderARM::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-
- locations->AddTemp(Location::RequiresRegister());
- locations->AddTemp(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorARM::VisitStringCharAt(HInvoke* invoke) {
- ArmAssembler* assembler = GetAssembler();
- LocationSummary* locations = invoke->GetLocations();
-
- // Location of reference to data array
- const MemberOffset value_offset = mirror::String::ValueOffset();
- // Location of count
- const MemberOffset count_offset = mirror::String::CountOffset();
-
- Register obj = locations->InAt(0).AsRegister<Register>(); // String object pointer.
- Register idx = locations->InAt(1).AsRegister<Register>(); // Index of character.
- Register out = locations->Out().AsRegister<Register>(); // Result character.
-
- Register temp = locations->GetTemp(0).AsRegister<Register>();
- Register array_temp = locations->GetTemp(1).AsRegister<Register>();
-
- // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
- // the cost.
- // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
- // we will not optimize the code for constants (which would save a register).
-
- SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathARM(invoke);
- codegen_->AddSlowPath(slow_path);
-
- __ ldr(temp, Address(obj, count_offset.Int32Value())); // temp = str.length.
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ cmp(idx, ShifterOperand(temp));
- __ b(slow_path->GetEntryLabel(), CS);
-
- __ add(array_temp, obj, ShifterOperand(value_offset.Int32Value())); // array_temp := str.value.
-
- // Load the value.
- __ ldrh(out, Address(array_temp, idx, LSL, 1)); // out := array_temp[idx].
-
- __ Bind(slow_path->GetExitLabel());
-}
-
void IntrinsicLocationsBuilderARM::VisitStringCompareTo(HInvoke* invoke) {
// The inputs plus one temp.
LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index c8d6ddc8f1..4da0843a76 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1122,56 +1122,6 @@ void IntrinsicCodeGeneratorARM64::VisitUnsafeCASObject(HInvoke* invoke) {
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
-void IntrinsicLocationsBuilderARM64::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- // In case we need to go in the slow path, we can't have the output be the same
- // as the input: the current liveness analysis considers the input to be live
- // at the point of the call.
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorARM64::VisitStringCharAt(HInvoke* invoke) {
- vixl::MacroAssembler* masm = GetVIXLAssembler();
- LocationSummary* locations = invoke->GetLocations();
-
- // Location of reference to data array
- const MemberOffset value_offset = mirror::String::ValueOffset();
- // Location of count
- const MemberOffset count_offset = mirror::String::CountOffset();
-
- Register obj = WRegisterFrom(locations->InAt(0)); // String object pointer.
- Register idx = WRegisterFrom(locations->InAt(1)); // Index of character.
- Register out = WRegisterFrom(locations->Out()); // Result character.
-
- UseScratchRegisterScope temps(masm);
- Register temp = temps.AcquireW();
- Register array_temp = temps.AcquireW(); // We can trade this for worse scheduling.
-
- // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
- // the cost.
- // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
- // we will not optimize the code for constants (which would save a register).
-
- SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
- codegen_->AddSlowPath(slow_path);
-
- __ Ldr(temp, HeapOperand(obj, count_offset)); // temp = str.length.
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ Cmp(idx, temp);
- __ B(hs, slow_path->GetEntryLabel());
-
- __ Add(array_temp, obj, Operand(value_offset.Int32Value())); // array_temp := str.value.
-
- // Load the value.
- __ Ldrh(out, MemOperand(array_temp.X(), idx, UXTW, 1)); // out := array_temp[idx].
-
- __ Bind(slow_path->GetExitLabel());
-}
-
void IntrinsicLocationsBuilderARM64::VisitStringCompareTo(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
invoke->InputAt(1)->CanBeNull()
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 140f56a870..d4f44d63e2 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1872,54 +1872,6 @@ void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASObject(HInvoke* invoke) {
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
-// char java.lang.String.charAt(int index)
-void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- // The inputs will be considered live at the last instruction and restored. This would overwrite
- // the output with kNoOutputOverlap.
- locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
-}
-
-void IntrinsicCodeGeneratorMIPS::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
- MipsAssembler* assembler = GetAssembler();
-
- // Location of reference to data array
- const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
- // Location of count
- const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-
- Register obj = locations->InAt(0).AsRegister<Register>();
- Register idx = locations->InAt(1).AsRegister<Register>();
- Register out = locations->Out().AsRegister<Register>();
-
- // TODO: Maybe we can support range check elimination. Overall,
- // though, I think it's not worth the cost.
- // TODO: For simplicity, the index parameter is requested in a
- // register, so different from Quick we will not optimize the
- // code for constants (which would save a register).
-
- SlowPathCodeMIPS* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS(invoke);
- codegen_->AddSlowPath(slow_path);
-
- // Load the string size
- __ Lw(TMP, obj, count_offset);
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- // Revert to slow path if idx is too large, or negative
- __ Bgeu(idx, TMP, slow_path->GetEntryLabel());
-
- // out = obj[2*idx].
- __ Sll(TMP, idx, 1); // idx * 2
- __ Addu(TMP, TMP, obj); // Address of char at location idx
- __ Lhu(out, TMP, value_offset); // Load char at location idx
-
- __ Bind(slow_path->GetExitLabel());
-}
-
// int java.lang.String.compareTo(String anotherString)
void IntrinsicLocationsBuilderMIPS::VisitStringCompareTo(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 6c4e64e4b1..9243f4c93f 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1371,52 +1371,6 @@ void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASObject(HInvoke* invoke) {
GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_);
}
-// char java.lang.String.charAt(int index)
-void IntrinsicLocationsBuilderMIPS64::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::SameAsFirstInput());
-}
-
-void IntrinsicCodeGeneratorMIPS64::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
- Mips64Assembler* assembler = GetAssembler();
-
- // Location of reference to data array
- const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
- // Location of count
- const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-
- GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>();
- GpuRegister idx = locations->InAt(1).AsRegister<GpuRegister>();
- GpuRegister out = locations->Out().AsRegister<GpuRegister>();
-
- // TODO: Maybe we can support range check elimination. Overall,
- // though, I think it's not worth the cost.
- // TODO: For simplicity, the index parameter is requested in a
- // register, so different from Quick we will not optimize the
- // code for constants (which would save a register).
-
- SlowPathCodeMIPS64* slow_path = new (GetAllocator()) IntrinsicSlowPathMIPS64(invoke);
- codegen_->AddSlowPath(slow_path);
-
- // Load the string size
- __ Lw(TMP, obj, count_offset);
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- // Revert to slow path if idx is too large, or negative
- __ Bgeuc(idx, TMP, slow_path->GetEntryLabel());
-
- // out = obj[2*idx].
- __ Sll(TMP, idx, 1); // idx * 2
- __ Daddu(TMP, TMP, obj); // Address of char at location idx
- __ Lhu(out, TMP, value_offset); // Load char at location idx
-
- __ Bind(slow_path->GetExitLabel());
-}
-
// int java.lang.String.compareTo(String anotherString)
void IntrinsicLocationsBuilderMIPS64::VisitStringCompareTo(HInvoke* invoke) {
LocationSummary* locations = new (arena_) LocationSummary(invoke,
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 05377f984b..4988398c92 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1030,48 +1030,6 @@ void IntrinsicCodeGeneratorX86::VisitMathNextAfter(HInvoke* invoke) {
GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
}
-void IntrinsicLocationsBuilderX86::VisitStringCharAt(HInvoke* invoke) {
- // The inputs plus one temp.
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::SameAsFirstInput());
-}
-
-void IntrinsicCodeGeneratorX86::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
-
- // Location of reference to data array.
- const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
- // Location of count.
- const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-
- Register obj = locations->InAt(0).AsRegister<Register>();
- Register idx = locations->InAt(1).AsRegister<Register>();
- Register out = locations->Out().AsRegister<Register>();
-
- // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
- // the cost.
- // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
- // we will not optimize the code for constants (which would save a register).
-
- SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke);
- codegen_->AddSlowPath(slow_path);
-
- X86Assembler* assembler = GetAssembler();
-
- __ cmpl(idx, Address(obj, count_offset));
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ j(kAboveEqual, slow_path->GetEntryLabel());
-
- // out = out[2*idx].
- __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset));
-
- __ Bind(slow_path->GetExitLabel());
-}
-
void IntrinsicLocationsBuilderX86::VisitSystemArrayCopyChar(HInvoke* invoke) {
// We need at least two of the positions or length to be an integer constant,
// or else we won't have enough free registers.
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 67c2f3a866..593c8f319b 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -891,49 +891,6 @@ void IntrinsicCodeGeneratorX86_64::VisitMathNextAfter(HInvoke* invoke) {
GenFPToFPCall(invoke, codegen_, kQuickNextAfter);
}
-void IntrinsicLocationsBuilderX86_64::VisitStringCharAt(HInvoke* invoke) {
- // The inputs plus one temp.
- LocationSummary* locations = new (arena_) LocationSummary(invoke,
- LocationSummary::kCallOnSlowPath,
- kIntrinsified);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetInAt(1, Location::RequiresRegister());
- locations->SetOut(Location::SameAsFirstInput());
- locations->AddTemp(Location::RequiresRegister());
-}
-
-void IntrinsicCodeGeneratorX86_64::VisitStringCharAt(HInvoke* invoke) {
- LocationSummary* locations = invoke->GetLocations();
-
- // Location of reference to data array.
- const int32_t value_offset = mirror::String::ValueOffset().Int32Value();
- // Location of count.
- const int32_t count_offset = mirror::String::CountOffset().Int32Value();
-
- CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>();
- CpuRegister idx = locations->InAt(1).AsRegister<CpuRegister>();
- CpuRegister out = locations->Out().AsRegister<CpuRegister>();
-
- // TODO: Maybe we can support range check elimination. Overall, though, I think it's not worth
- // the cost.
- // TODO: For simplicity, the index parameter is requested in a register, so different from Quick
- // we will not optimize the code for constants (which would save a register).
-
- SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke);
- codegen_->AddSlowPath(slow_path);
-
- X86_64Assembler* assembler = GetAssembler();
-
- __ cmpl(idx, Address(obj, count_offset));
- codegen_->MaybeRecordImplicitNullCheck(invoke);
- __ j(kAboveEqual, slow_path->GetEntryLabel());
-
- // out = out[2*idx].
- __ movzxw(out, Address(out, idx, ScaleFactor::TIMES_2, value_offset));
-
- __ Bind(slow_path->GetExitLabel());
-}
-
void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopyChar(HInvoke* invoke) {
// 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.
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 711a6c1b2d..8774b9b1a9 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,6 +26,7 @@
#include "base/arena_object.h"
#include "base/stl_util.h"
#include "dex/compiler_enums.h"
+#include "dex_file.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
@@ -1920,6 +1921,14 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
environment_ = environment;
}
+ void InsertRawEnvironment(HEnvironment* environment) {
+ DCHECK(environment_ != nullptr);
+ DCHECK_EQ(environment->GetHolder(), this);
+ DCHECK(environment->GetParent() == nullptr);
+ environment->parent_ = environment_;
+ environment_ = environment;
+ }
+
void RemoveEnvironment();
// Set the environment of this instruction, copying it from `environment`. While
@@ -5079,8 +5088,13 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> {
class HArrayGet FINAL : public HExpression<2> {
public:
- HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc)
+ HArrayGet(HInstruction* array,
+ HInstruction* index,
+ Primitive::Type type,
+ uint32_t dex_pc,
+ bool is_string_char_at = false)
: HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) {
+ SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
}
@@ -5114,12 +5128,24 @@ class HArrayGet FINAL : public HExpression<2> {
return result;
}
+ bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); }
+
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
DECLARE_INSTRUCTION(ArrayGet);
private:
+ // We treat a String as an array, creating the HArrayGet from String.charAt()
+ // intrinsic in the instruction simplifier. We can always determine whether
+ // a particular HArrayGet is actually a String.charAt() by looking at the type
+ // of the input but that requires holding the mutator lock, so we prefer to use
+ // a flag, so that code generators don't need to do the locking.
+ static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
+ static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1;
+ static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
DISALLOW_COPY_AND_ASSIGN(HArrayGet);
};
@@ -5225,8 +5251,9 @@ class HArraySet FINAL : public HTemplateInstruction<3> {
class HArrayLength FINAL : public HExpression<1> {
public:
- HArrayLength(HInstruction* array, uint32_t dex_pc)
+ HArrayLength(HInstruction* array, uint32_t dex_pc, bool is_string_length = false)
: HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
+ SetPackedFlag<kFlagIsStringLength>(is_string_length);
// Note that arrays do not change length, so the instruction does not
// depend on any write.
SetRawInputAt(0, array);
@@ -5240,7 +5267,6 @@ class HArrayLength FINAL : public HExpression<1> {
return obj == InputAt(0);
}
- void MarkAsStringLength() { SetPackedFlag<kFlagIsStringLength>(); }
bool IsStringLength() const { return GetPackedFlag<kFlagIsStringLength>(); }
DECLARE_INSTRUCTION(ArrayLength);
@@ -5263,8 +5289,12 @@ class HBoundsCheck FINAL : public HExpression<2> {
public:
// `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException`
// constructor.
- HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
- : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ HBoundsCheck(HInstruction* index,
+ HInstruction* length,
+ uint32_t dex_pc,
+ uint32_t string_char_at_method_index = DexFile::kDexNoIndex)
+ : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc),
+ string_char_at_method_index_(string_char_at_method_index) {
DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType()));
SetRawInputAt(0, index);
SetRawInputAt(1, length);
@@ -5279,11 +5309,23 @@ class HBoundsCheck FINAL : public HExpression<2> {
bool CanThrow() const OVERRIDE { return true; }
+ bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; }
+ uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; }
+
HInstruction* GetIndex() const { return InputAt(0); }
DECLARE_INSTRUCTION(BoundsCheck);
private:
+ // We treat a String as an array, creating the HBoundsCheck from String.charAt()
+ // intrinsic in the instruction simplifier. We want to include the String.charAt()
+ // in the stack trace if we actually throw the StringIndexOutOfBoundsException,
+ // so we need to create an HEnvironment which will be translated to an InlineInfo
+ // indicating the extra stack frame. Since we add this HEnvironment quite late,
+ // in the PrepareForRegisterAllocation pass, we need to remember the method index
+ // from the invoke as we don't want to look again at the dex bytecode.
+ uint32_t string_char_at_method_index_; // DexFile::kDexNoIndex if regular array.
+
DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
};
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index c941c0c086..696b8c6859 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -40,6 +40,22 @@ void PrepareForRegisterAllocation::VisitDivZeroCheck(HDivZeroCheck* check) {
void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) {
check->ReplaceWith(check->InputAt(0));
+ if (check->IsStringCharAt()) {
+ // Add a fake environment for String.charAt() inline info as we want
+ // the exception to appear as being thrown from there.
+ const DexFile& dex_file = check->GetEnvironment()->GetDexFile();
+ DCHECK_STREQ(PrettyMethod(check->GetStringCharAtMethodIndex(), dex_file).c_str(),
+ "char java.lang.String.charAt(int)");
+ ArenaAllocator* arena = GetGraph()->GetArena();
+ HEnvironment* environment = new (arena) HEnvironment(arena,
+ /* number_of_vregs */ 0u,
+ dex_file,
+ check->GetStringCharAtMethodIndex(),
+ /* dex_pc */ DexFile::kDexNoIndex,
+ kVirtual,
+ check);
+ check->InsertRawEnvironment(environment);
+ }
}
void PrepareForRegisterAllocation::VisitBoundType(HBoundType* bound_type) {
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 11a254ef63..fc8af6462a 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -228,7 +228,7 @@ size_t StackMapStream::ComputeDexRegisterMapsSize() const {
void StackMapStream::ComputeInlineInfoEncoding() {
uint32_t method_index_max = 0;
- uint32_t dex_pc_max = 0;
+ uint32_t dex_pc_max = DexFile::kDexNoIndex;
uint32_t invoke_type_max = 0;
uint32_t inline_info_index = 0;
@@ -236,7 +236,10 @@ void StackMapStream::ComputeInlineInfoEncoding() {
for (size_t j = 0; j < entry.inlining_depth; ++j) {
InlineInfoEntry inline_entry = inline_infos_[inline_info_index++];
method_index_max = std::max(method_index_max, inline_entry.method_index);
- dex_pc_max = std::max(dex_pc_max, inline_entry.dex_pc);
+ if (inline_entry.dex_pc != DexFile::kDexNoIndex &&
+ (dex_pc_max == DexFile::kDexNoIndex || dex_pc_max < inline_entry.dex_pc)) {
+ dex_pc_max = inline_entry.dex_pc;
+ }
invoke_type_max = std::max(invoke_type_max, static_cast<uint32_t>(inline_entry.invoke_type));
}
}
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 41f72f508b..53a9795d52 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -108,7 +108,7 @@ class StackMapStream : public ValueObject {
};
struct InlineInfoEntry {
- uint32_t dex_pc;
+ uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods.
uint32_t method_index;
InvokeType invoke_type;
uint32_t num_dex_registers;
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index dd7063f248..27a41f09ad 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -310,6 +310,12 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
+ */
+TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+
+ /*
* Called by managed code to create and deliver a StackOverflowError.
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 1fba09bae3..a6490aed33 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -417,6 +417,12 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode
TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
+ */
+TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+
+ /*
* Called by managed code to create and deliver a StackOverflowError.
*/
NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 45e33a8500..833ba1b612 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -268,6 +268,8 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) {
static_assert(!IsDirectEntrypoint(kQuickThrowNullPointer), "Non-direct C stub marked direct.");
qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
static_assert(!IsDirectEntrypoint(kQuickThrowStackOverflow), "Non-direct C stub marked direct.");
+ qpoints->pThrowStringBounds = art_quick_throw_string_bounds;
+ static_assert(!IsDirectEntrypoint(kQuickThrowStringBounds), "Non-direct C stub marked direct.");
// Deoptimization from compiled code.
qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3ee26afc4f..bb89674caf 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -529,6 +529,18 @@ ENTRY art_quick_throw_array_bounds
END art_quick_throw_array_bounds
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt().
+ */
+ .extern artThrowStringBoundsFromCode
+ENTRY art_quick_throw_string_bounds
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ la $t9, artThrowStringBoundsFromCode
+ jalr $zero, $t9 # artThrowStringBoundsFromCode(index, limit, Thread*)
+ move $a2, rSELF # pass Thread::Current
+END art_quick_throw_string_bounds
+
+ /*
* Called by managed code to create and deliver a StackOverflowError.
*/
.extern artThrowStackOverflowFromCode
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 8f1a35a693..78ac748e32 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -617,6 +617,19 @@ ENTRY art_quick_throw_array_bounds
END art_quick_throw_array_bounds
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt().
+ */
+ .extern artThrowStringBoundsFromCode
+ENTRY art_quick_throw_string_bounds
+.Lart_quick_throw_string_bounds_gp_set:
+ SETUP_SAVE_ALL_CALLEE_SAVE_FRAME
+ dla $t9, artThrowStringBoundsFromCode
+ jalr $zero, $t9 # artThrowStringBoundsFromCode(index, limit, Thread*)
+ move $a2, rSELF # pass Thread::Current
+END art_quick_throw_string_bounds
+
+ /*
* Called by managed code to create and deliver a StackOverflowError.
*/
.extern artThrowStackOverflowFromCode
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 2d7f664809..4ab47b9f07 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -310,6 +310,12 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr
TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
+ */
+TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+
+ /*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
* the method_idx. This wrapper will save arg1-arg3 and call the appropriate C helper.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 8064ed696f..205307ce67 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -336,6 +336,12 @@ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFr
TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode
/*
+ * Called by managed code to create and deliver a StringIndexOutOfBoundsException
+ * as if thrown from a call to String.charAt(). Arg1 holds index, arg2 holds limit.
+ */
+TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_string_bounds, artThrowStringBoundsFromCode
+
+ /*
* All generated callsites for interface invokes and invocation slow paths will load arguments
* as usual - except instead of loading arg0/rdi with the target Method*, arg0/rdi will contain
* the method_idx. This wrapper will save arg1-arg3, and call the appropriate C helper.
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 480644a5b2..2d702f6af7 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -127,32 +127,32 @@ ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET,
ADD_TEST_EQ(THREAD_SELF_OFFSET,
art::Thread::SelfOffset<__SIZEOF_POINTER__>().Int32Value())
-// Offset of field Thread::tlsPtr_.thread_local_objects.
-#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_CARD_TABLE_OFFSET + 166 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
- art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_pos.
-#define THREAD_LOCAL_POS_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + 2 * __SIZEOF_POINTER__)
+#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 168 * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
art::Thread::ThreadLocalPosOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_end.
#define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET,
art::Thread::ThreadLocalEndOffset<__SIZEOF_POINTER__>().Int32Value())
+// Offset of field Thread::tlsPtr_.thread_local_objects.
+#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__)
+ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
+ art::Thread::ThreadLocalObjectsOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_current_ibase.
-#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 2 * __SIZEOF_POINTER__)
+#define THREAD_CURRENT_IBASE_OFFSET (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
art::Thread::MterpCurrentIBaseOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_default_ibase.
-#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 3 * __SIZEOF_POINTER__)
+#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_CURRENT_IBASE_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_DEFAULT_IBASE_OFFSET,
art::Thread::MterpDefaultIBaseOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_alt_ibase.
-#define THREAD_ALT_IBASE_OFFSET (THREAD_LOCAL_POS_OFFSET + 4 * __SIZEOF_POINTER__)
+#define THREAD_ALT_IBASE_OFFSET (THREAD_DEFAULT_IBASE_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_ALT_IBASE_OFFSET,
art::Thread::MterpAltIBaseOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.rosalloc_runs.
-#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_LOCAL_POS_OFFSET + 5 * __SIZEOF_POINTER__)
+#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_ALT_IBASE_OFFSET + __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET,
art::Thread::RosAllocRunsOffset<__SIZEOF_POINTER__>().Int32Value())
// Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top.
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 75cce424e9..4f705f2056 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -654,6 +654,13 @@ void ThrowStackOverflowError(Thread* self) {
}
}
+// StringIndexOutOfBoundsException
+
+void ThrowStringIndexOutOfBoundsException(int index, int length) {
+ ThrowException("Ljava/lang/StringIndexOutOfBoundsException;", nullptr,
+ StringPrintf("length=%d; index=%d", length, index).c_str());
+}
+
// VerifyError
void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index c3a1f09db3..7a335859e5 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -211,6 +211,11 @@ void ThrowRuntimeException(const char* fmt, ...)
void ThrowStackOverflowError(Thread* self) SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR;
+// StringIndexOutOfBoundsException
+
+void ThrowStringIndexOutOfBoundsException(int index, int length)
+ SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR;
+
// VerifyError
void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...)
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index f3a0d2f3ef..771e14396e 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -121,5 +121,6 @@ extern "C" void art_quick_throw_div_zero();
extern "C" void art_quick_throw_no_such_method(int32_t method_idx);
extern "C" void art_quick_throw_null_pointer_exception();
extern "C" void art_quick_throw_stack_overflow(void*);
+extern "C" void art_quick_throw_string_bounds(int32_t index, int32_t limit);
#endif // ART_RUNTIME_ENTRYPOINTS_QUICK_QUICK_DEFAULT_EXTERNS_H_
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 5dafa8b599..f98de95fcb 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -114,6 +114,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints)
qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method;
qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception;
qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow;
+ qpoints->pThrowStringBounds = art_quick_throw_string_bounds;
// Deoptimize
qpoints->pDeoptimize = art_quick_deoptimize_from_compiled_code;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 79d1c1377b..30b639eaec 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -140,6 +140,7 @@
V(ThrowNoSuchMethod, void, int32_t) \
V(ThrowNullPointer, void, void) \
V(ThrowStackOverflow, void, void*) \
+ V(ThrowStringBounds, void, int32_t, int32_t) \
V(Deoptimize, void, void) \
\
V(A64Load, int64_t, volatile const int64_t *) \
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 5256feae2b..2778e32ece 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -29,7 +29,7 @@ extern "C" NO_RETURN void artDeliverPendingExceptionFromCode(Thread* self)
self->QuickDeliverException();
}
-// Called by generated call to throw an exception.
+// Called by generated code to throw an exception.
extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* exception, Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
/*
@@ -48,7 +48,7 @@ extern "C" NO_RETURN void artDeliverExceptionFromCode(mirror::Throwable* excepti
self->QuickDeliverException();
}
-// Called by generated call to throw a NPE exception.
+// Called by generated code to throw a NPE exception.
extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
@@ -58,7 +58,7 @@ extern "C" NO_RETURN void artThrowNullPointerExceptionFromCode(Thread* self)
self->QuickDeliverException();
}
-// Called by generated call to throw an arithmetic divide by zero exception.
+// Called by generated code to throw an arithmetic divide by zero exception.
extern "C" NO_RETURN void artThrowDivZeroFromCode(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
@@ -66,7 +66,7 @@ extern "C" NO_RETURN void artThrowDivZeroFromCode(Thread* self)
self->QuickDeliverException();
}
-// Called by generated call to throw an array index out of bounds exception.
+// Called by generated code to throw an array index out of bounds exception.
extern "C" NO_RETURN void artThrowArrayBoundsFromCode(int index, int length, Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
@@ -74,6 +74,14 @@ extern "C" NO_RETURN void artThrowArrayBoundsFromCode(int index, int length, Thr
self->QuickDeliverException();
}
+// Called by generated code to throw a string index out of bounds exception.
+extern "C" NO_RETURN void artThrowStringBoundsFromCode(int index, int length, Thread* self)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ ScopedQuickEntrypointChecks sqec(self);
+ ThrowStringIndexOutOfBoundsException(index, length);
+ self->QuickDeliverException();
+}
+
extern "C" NO_RETURN void artThrowStackOverflowFromCode(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 91deea0161..7a624b211c 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -121,10 +121,10 @@ class EntrypointsOrderTest : public CommonRuntimeTest {
// Skip across the entrypoints structures.
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, thread_local_start, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*));
- EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, mterp_current_ibase, sizeof(void*));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*));
+ EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_alt_ibase, rosalloc_runs, sizeof(void*));
@@ -287,7 +287,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest {
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowDivZero, pThrowNoSuchMethod, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNoSuchMethod, pThrowNullPointer, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowNullPointer, pThrowStackOverflow, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pDeoptimize, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStackOverflow, pThrowStringBounds, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pThrowStringBounds, pDeoptimize, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pDeoptimize, pA64Load, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Load, pA64Store, sizeof(void*));
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 628554225c..cf9b8ebfd7 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -20,6 +20,7 @@
#include "array.h"
#include "base/bit_utils.h"
#include "class.h"
+#include "common_throws.h"
#include "gc/heap-inl.h"
#include "globals.h"
#include "intern_table.h"
@@ -134,9 +135,7 @@ inline String* String::Intern() {
inline uint16_t String::CharAt(int32_t index) {
int32_t count = GetField32(OFFSET_OF_OBJECT_MEMBER(String, count_));
if (UNLIKELY((index < 0) || (index >= count))) {
- Thread* self = Thread::Current();
- self->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
- "length=%i; index=%i", count, index);
+ ThrowStringIndexOutOfBoundsException(index, count);
return 0;
}
return GetValue()[index];
diff --git a/runtime/oat.h b/runtime/oat.h
index 52d4c4209e..6243660494 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- static constexpr uint8_t kOatVersion[] = { '0', '8', '1', '\0' };
+ static constexpr uint8_t kOatVersion[] = { '0', '8', '2', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7c50f97d39..4647d67699 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -19,6 +19,7 @@
#include "base/bit_vector.h"
#include "base/bit_utils.h"
+#include "dex_file.h"
#include "memory_region.h"
#include "leb128.h"
@@ -892,7 +893,11 @@ class InlineInfoEncoding {
total_bit_size_ += MinimumBitsToStore(method_index_max);
dex_pc_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
- total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max);
+ // Note: We're not encoding the dex pc if there is none. That's the case
+ // for an intrinsified native method, such as String.charAt().
+ if (dex_pc_max != DexFile::kDexNoIndex) {
+ total_bit_size_ += MinimumBitsToStore(1 /* kNoDexPc */ + dex_pc_max);
+ }
invoke_type_bit_offset_ = dchecked_integral_cast<uint8_t>(total_bit_size_);
total_bit_size_ += MinimumBitsToStore(invoke_type_max);
diff --git a/runtime/thread.h b/runtime/thread.h
index 7ae9be5d1a..9b6a20ec59 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1349,8 +1349,8 @@ class Thread {
instrumentation_stack(nullptr), debug_invoke_req(nullptr), single_step_control(nullptr),
stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr),
frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0),
- last_no_thread_suspension_cause(nullptr), thread_local_objects(0),
- thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr),
+ last_no_thread_suspension_cause(nullptr), thread_local_start(nullptr),
+ thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_objects(0),
mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr),
thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr),
nested_signal_state(nullptr), flip_function(nullptr), method_verifier(nullptr),
@@ -1465,12 +1465,12 @@ class Thread {
QuickEntryPoints quick_entrypoints;
// Thread-local allocation pointer.
- size_t thread_local_objects;
uint8_t* thread_local_start;
// thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for
// potentially better performance.
uint8_t* thread_local_pos;
uint8_t* thread_local_end;
+ size_t thread_local_objects;
// Mterp jump table bases.
void* mterp_current_ibase;
diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java
index 24ed2feff7..26475ae55c 100644
--- a/test/536-checker-intrinsic-optimization/src/Main.java
+++ b/test/536-checker-intrinsic-optimization/src/Main.java
@@ -30,6 +30,18 @@ public class Main {
}
}
+ public static void assertCharEquals(char expected, char result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void assertStringContains(String searchTerm, String result) {
+ if (result == null || !result.contains(searchTerm)) {
+ throw new Error("Search term: " + searchTerm + ", not found in: " + result);
+ }
+ }
+
public static void main(String[] args) {
stringEqualsSame();
stringArgumentNotNull("Foo");
@@ -41,6 +53,57 @@ public class Main {
assertBooleanEquals(true, $opt$noinline$isStringEmpty(""));
assertBooleanEquals(false, $opt$noinline$isStringEmpty("abc"));
assertBooleanEquals(false, $opt$noinline$isStringEmpty("0123456789"));
+
+ assertCharEquals('a', $opt$noinline$stringCharAt("a", 0));
+ assertCharEquals('a', $opt$noinline$stringCharAt("abc", 0));
+ assertCharEquals('b', $opt$noinline$stringCharAt("abc", 1));
+ assertCharEquals('c', $opt$noinline$stringCharAt("abc", 2));
+ assertCharEquals('7', $opt$noinline$stringCharAt("0123456789", 7));
+
+ try {
+ $opt$noinline$stringCharAt("abc", -1);
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
+ }
+ try {
+ $opt$noinline$stringCharAt("abc", 3);
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
+ }
+ try {
+ $opt$noinline$stringCharAt("abc", Integer.MAX_VALUE);
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringCharAt", sioob.getStackTrace()[1].toString());
+ }
+
+ assertCharEquals('7', $opt$noinline$stringCharAtCatch("0123456789", 7));
+ assertCharEquals('\0', $opt$noinline$stringCharAtCatch("0123456789", 10));
+
+ assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumChars("abc"));
+ assertIntEquals('a' + 'b' + 'c', $opt$noinline$stringSumLeadingChars("abcdef", 3));
+ try {
+ $opt$noinline$stringSumLeadingChars("abcdef", 7);
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringSumLeadingChars",
+ sioob.getStackTrace()[1].toString());
+ }
+ assertIntEquals('a' + 'b' + 'c' + 'd', $opt$noinline$stringSum4LeadingChars("abcdef"));
+ try {
+ $opt$noinline$stringSum4LeadingChars("abc");
+ throw new Error("Should throw SIOOB.");
+ } catch (StringIndexOutOfBoundsException sioob) {
+ assertStringContains("java.lang.String.charAt", sioob.getStackTrace()[0].toString());
+ assertStringContains("Main.$opt$noinline$stringSum4LeadingChars",
+ sioob.getStackTrace()[1].toString());
+ }
}
/// CHECK-START: int Main.$opt$noinline$getStringLength(java.lang.String) instruction_simplifier (before)
@@ -81,6 +144,144 @@ public class Main {
return s.isEmpty();
}
+ /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<String:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Pos:i\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
+ /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+ /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAt(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ static public char $opt$noinline$stringCharAt(String s, int pos) {
+ if (doThrow) { throw new Error(); }
+ return s.charAt(pos);
+ }
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (before)
+ /// CHECK-DAG: <<Char:c\d+>> InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<String:l\d+>> ParameterValue
+ /// CHECK-DAG: <<Pos:i\d+>> ParameterValue
+ /// CHECK-DAG: <<NullCk:l\d+>> NullCheck [<<String>>]
+ /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<NullCk>>] is_string_length:true
+ /// CHECK-DAG: BoundsCheck [<<Pos>>,<<Length>>] is_string_char_at:true
+ /// CHECK-DAG: <<Char:c\d+>> ArrayGet [<<NullCk>>,<<Pos>>] is_string_char_at:true
+ /// CHECK-DAG: Return [<<Char>>]
+
+ /// CHECK-START: char Main.$opt$noinline$stringCharAtCatch(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ static public char $opt$noinline$stringCharAtCatch(String s, int pos) {
+ if (doThrow) { throw new Error(); }
+ try {
+ return s.charAt(pos);
+ } catch (StringIndexOutOfBoundsException ignored) {
+ return '\0';
+ }
+ }
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (before)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringLength
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after)
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringLength
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) GVN (after)
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-NOT: ArrayLength is_string_length:true
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumChars(java.lang.String) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+
+ static public int $opt$noinline$stringSumChars(String s) {
+ if (doThrow) { throw new Error(); }
+ int sum = 0;
+ int len = s.length();
+ for (int i = 0; i < len; ++i) {
+ sum += s.charAt(i);
+ }
+ return sum;
+ }
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (before)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after)
+ /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]]
+
+ /// CHECK-START: int Main.$opt$noinline$stringSumLeadingChars(java.lang.String, int) BCE (after)
+ /// CHECK-NOT: BoundsCheck is_string_char_at:true
+
+ static public int $opt$noinline$stringSumLeadingChars(String s, int n) {
+ if (doThrow) { throw new Error(); }
+ int sum = 0;
+ for (int i = 0; i < n; ++i) {
+ sum += s.charAt(i);
+ }
+ return sum;
+ }
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (before)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after)
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+ /// CHECK-DAG: ArrayLength is_string_length:true
+ /// CHECK-DAG: BoundsCheck is_string_char_at:true
+ /// CHECK-DAG: ArrayGet is_string_char_at:true
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:StringCharAt
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after)
+ /// CHECK-DAG: Deoptimize env:[[{{[^\]]*}}]]
+
+ /// CHECK-START: int Main.$opt$noinline$stringSum4LeadingChars(java.lang.String) BCE (after)
+ /// CHECK-NOT: BoundsCheck is_string_char_at:true
+
+ static public int $opt$noinline$stringSum4LeadingChars(String s) {
+ if (doThrow) { throw new Error(); }
+ int sum = s.charAt(0) + s.charAt(1) + s.charAt(2) + s.charAt(3);
+ return sum;
+ }
+
/// CHECK-START: boolean Main.stringEqualsSame() instruction_simplifier (before)
/// CHECK: InvokeStaticOrDirect