diff options
-rw-r--r-- | compiler/Android.mk | 1 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 58 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 63 | ||||
-rw-r--r-- | compiler/optimizing/graph_visualizer.cc | 3 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 20 | ||||
-rw-r--r-- | compiler/optimizing/x86_memory_gen.cc | 84 | ||||
-rw-r--r-- | compiler/optimizing/x86_memory_gen.h | 46 | ||||
-rw-r--r-- | test/609-checker-x86-bounds-check/expected.txt | 1 | ||||
-rw-r--r-- | test/609-checker-x86-bounds-check/info.txt | 1 | ||||
-rw-r--r-- | test/609-checker-x86-bounds-check/src/Main.java | 88 |
10 files changed, 345 insertions, 20 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index 02c176c8fa..2666835b12 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -75,6 +75,7 @@ LIBART_COMPILER_SRC_FILES := \ optimizing/ssa_liveness_analysis.cc \ optimizing/ssa_phi_elimination.cc \ optimizing/stack_map_stream.cc \ + optimizing/x86_memory_gen.cc \ trampolines/trampoline_compiler.cc \ utils/assembler.cc \ utils/swap_space.cc \ diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index be20f1f7cc..7f39661e6b 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -140,12 +140,29 @@ class BoundsCheckSlowPathX86 : public SlowPathCode { // Live registers will be restored in the catch block if caught. SaveLiveRegisters(codegen, instruction_->GetLocations()); } + + // Are we using an array length from memory? + HInstruction* array_length = instruction_->InputAt(1); + Location length_loc = locations->InAt(1); InvokeRuntimeCallingConvention calling_convention; + if (array_length->IsArrayLength() && array_length->IsEmittedAtUseSite()) { + // Load the array length into our temporary. + uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(array_length->AsArrayLength()); + Location array_loc = array_length->GetLocations()->InAt(0); + Address array_len(array_loc.AsRegister<Register>(), len_offset); + length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(1)); + // Check for conflicts with index. + if (length_loc.Equals(locations->InAt(0))) { + // We know we aren't using parameter 2. + length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(2)); + } + __ movl(length_loc.AsRegister<Register>(), array_len); + } x86_codegen->EmitParallelMoves( locations->InAt(0), Location::RegisterLocation(calling_convention.GetRegisterAt(0)), Primitive::kPrimInt, - locations->InAt(1), + length_loc, Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() @@ -5517,10 +5534,16 @@ void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) { void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + if (!instruction->IsEmittedAtUseSite()) { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } } void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) { + if (instruction->IsEmittedAtUseSite()) { + return; + } + LocationSummary* locations = instruction->GetLocations(); uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); Register obj = locations->InAt(0).AsRegister<Register>(); @@ -5535,7 +5558,10 @@ void LocationsBuilderX86::VisitBoundsCheck(HBoundsCheck* instruction) { : LocationSummary::kNoCall; LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); - locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + HInstruction* length = instruction->InputAt(1); + if (!length->IsEmittedAtUseSite()) { + locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + } if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); } @@ -5569,12 +5595,28 @@ void InstructionCodeGeneratorX86::VisitBoundsCheck(HBoundsCheck* instruction) { codegen_->AddSlowPath(slow_path); __ j(kAboveEqual, slow_path->GetEntryLabel()); } else { - Register length = length_loc.AsRegister<Register>(); - if (index_loc.IsConstant()) { - int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant()); - __ cmpl(length, Immediate(value)); + HInstruction* array_length = instruction->InputAt(1); + if (array_length->IsEmittedAtUseSite()) { + // Address the length field in the array. + DCHECK(array_length->IsArrayLength()); + uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(array_length->AsArrayLength()); + Location array_loc = array_length->GetLocations()->InAt(0); + Address array_len(array_loc.AsRegister<Register>(), len_offset); + if (index_loc.IsConstant()) { + int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant()); + __ cmpl(array_len, Immediate(value)); + } else { + __ cmpl(array_len, index_loc.AsRegister<Register>()); + } + codegen_->MaybeRecordImplicitNullCheck(array_length); } else { - __ cmpl(length, index_loc.AsRegister<Register>()); + Register length = length_loc.AsRegister<Register>(); + if (index_loc.IsConstant()) { + int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant()); + __ cmpl(length, Immediate(value)); + } else { + __ cmpl(length, index_loc.AsRegister<Register>()); + } } codegen_->AddSlowPath(slow_path); __ j(kBelowEqual, slow_path->GetEntryLabel()); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index cac33cddb8..2e28088db1 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -194,14 +194,31 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCode { // Live registers will be restored in the catch block if caught. SaveLiveRegisters(codegen, instruction_->GetLocations()); } + // Are we using an array length from memory? + HInstruction* array_length = instruction_->InputAt(1); + Location length_loc = locations->InAt(1); + InvokeRuntimeCallingConvention calling_convention; + if (array_length->IsArrayLength() && array_length->IsEmittedAtUseSite()) { + // Load the array length into our temporary. + uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(array_length->AsArrayLength()); + Location array_loc = array_length->GetLocations()->InAt(0); + Address array_len(array_loc.AsRegister<CpuRegister>(), len_offset); + length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(1)); + // Check for conflicts with index. + if (length_loc.Equals(locations->InAt(0))) { + // We know we aren't using parameter 2. + length_loc = Location::RegisterLocation(calling_convention.GetRegisterAt(2)); + } + __ movl(length_loc.AsRegister<CpuRegister>(), array_len); + } + // We're moving two locations to locations that could overlap, so we need a parallel // move resolver. - InvokeRuntimeCallingConvention calling_convention; codegen->EmitParallelMoves( locations->InAt(0), Location::RegisterLocation(calling_convention.GetRegisterAt(0)), Primitive::kPrimInt, - locations->InAt(1), + length_loc, Location::RegisterLocation(calling_convention.GetRegisterAt(1)), Primitive::kPrimInt); uint32_t entry_point_offset = instruction_->AsBoundsCheck()->IsStringCharAt() @@ -4987,10 +5004,16 @@ void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + if (!instruction->IsEmittedAtUseSite()) { + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); + } } void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) { + if (instruction->IsEmittedAtUseSite()) { + return; + } + LocationSummary* locations = instruction->GetLocations(); uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>(); @@ -5005,7 +5028,10 @@ void LocationsBuilderX86_64::VisitBoundsCheck(HBoundsCheck* instruction) { : LocationSummary::kNoCall; LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind); locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0))); - locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); + HInstruction* length = instruction->InputAt(1); + if (!length->IsEmittedAtUseSite()) { + locations->SetInAt(1, Location::RegisterOrConstant(length)); + } if (instruction->HasUses()) { locations->SetOut(Location::SameAsFirstInput()); } @@ -5015,8 +5041,7 @@ void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) LocationSummary* locations = instruction->GetLocations(); Location index_loc = locations->InAt(0); Location length_loc = locations->InAt(1); - SlowPathCode* slow_path = - new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(instruction); + SlowPathCode* slow_path = new (GetGraph()->GetArena()) BoundsCheckSlowPathX86_64(instruction); if (length_loc.IsConstant()) { int32_t length = CodeGenerator::GetInt32ValueOf(length_loc.GetConstant()); @@ -5039,12 +5064,28 @@ void InstructionCodeGeneratorX86_64::VisitBoundsCheck(HBoundsCheck* instruction) codegen_->AddSlowPath(slow_path); __ j(kAboveEqual, slow_path->GetEntryLabel()); } else { - CpuRegister length = length_loc.AsRegister<CpuRegister>(); - if (index_loc.IsConstant()) { - int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant()); - __ cmpl(length, Immediate(value)); + HInstruction* array_length = instruction->InputAt(1); + if (array_length->IsEmittedAtUseSite()) { + // Address the length field in the array. + DCHECK(array_length->IsArrayLength()); + uint32_t len_offset = CodeGenerator::GetArrayLengthOffset(array_length->AsArrayLength()); + Location array_loc = array_length->GetLocations()->InAt(0); + Address array_len(array_loc.AsRegister<CpuRegister>(), len_offset); + if (index_loc.IsConstant()) { + int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant()); + __ cmpl(array_len, Immediate(value)); + } else { + __ cmpl(array_len, index_loc.AsRegister<CpuRegister>()); + } + codegen_->MaybeRecordImplicitNullCheck(array_length); } else { - __ cmpl(length, index_loc.AsRegister<CpuRegister>()); + CpuRegister length = length_loc.AsRegister<CpuRegister>(); + if (index_loc.IsConstant()) { + int32_t value = CodeGenerator::GetInt32ValueOf(index_loc.GetConstant()); + __ cmpl(length, Immediate(value)); + } else { + __ cmpl(length, index_loc.AsRegister<CpuRegister>()); + } } codegen_->AddSlowPath(slow_path); __ j(kBelowEqual, slow_path->GetEntryLabel()); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 9d67373321..e14f603fe1 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -401,6 +401,9 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { void VisitArrayLength(HArrayLength* array_length) OVERRIDE { StartAttributeStream("is_string_length") << std::boolalpha << array_length->IsStringLength() << std::noboolalpha; + if (array_length->IsEmittedAtUseSite()) { + StartAttributeStream("emitted_at_use") << "true"; + } } void VisitBoundsCheck(HBoundsCheck* bounds_check) OVERRIDE { diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index d703b0f94f..d6e09d7acb 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -37,6 +37,10 @@ #include "pc_relative_fixups_x86.h" #endif +#if defined(ART_ENABLE_CODEGEN_x86) || defined(ART_ENABLE_CODEGEN_x86_64) +#include "x86_memory_gen.h" +#endif + #include "art_method-inl.h" #include "base/arena_allocator.h" #include "base/arena_containers.h" @@ -485,13 +489,27 @@ static void RunArchOptimizations(InstructionSet instruction_set, case kX86: { x86::PcRelativeFixups* pc_relative_fixups = new (arena) x86::PcRelativeFixups(graph, codegen, stats); + x86::X86MemoryOperandGeneration* memory_gen = + new(arena) x86::X86MemoryOperandGeneration(graph, stats, codegen); HOptimization* x86_optimizations[] = { - pc_relative_fixups + pc_relative_fixups, + memory_gen }; RunOptimizations(x86_optimizations, arraysize(x86_optimizations), pass_observer); break; } #endif +#ifdef ART_ENABLE_CODEGEN_x86_64 + case kX86_64: { + x86::X86MemoryOperandGeneration* memory_gen = + new(arena) x86::X86MemoryOperandGeneration(graph, stats, codegen); + HOptimization* x86_64_optimizations[] = { + memory_gen + }; + RunOptimizations(x86_64_optimizations, arraysize(x86_64_optimizations), pass_observer); + break; + } +#endif default: break; } diff --git a/compiler/optimizing/x86_memory_gen.cc b/compiler/optimizing/x86_memory_gen.cc new file mode 100644 index 0000000000..195159f61b --- /dev/null +++ b/compiler/optimizing/x86_memory_gen.cc @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 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 "x86_memory_gen.h" +#include "code_generator.h" + +namespace art { +namespace x86 { + +/** + * Replace instructions with memory operand forms. + */ +class MemoryOperandVisitor : public HGraphVisitor { + public: + MemoryOperandVisitor(HGraph* graph, bool do_implicit_null_checks) + : HGraphVisitor(graph), + do_implicit_null_checks_(do_implicit_null_checks) {} + + private: + void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE { + // Replace the length by the array itself, so that we can do compares to memory. + HArrayLength* array_len = check->InputAt(1)->AsArrayLength(); + + // We only want to replace an ArrayLength. + if (array_len == nullptr) { + return; + } + + HInstruction* array = array_len->InputAt(0); + DCHECK_EQ(array->GetType(), Primitive::kPrimNot); + + // Don't apply this optimization when the array is nullptr. + if (array->IsConstant() || (array->IsNullCheck() && array->InputAt(0)->IsConstant())) { + return; + } + + // Is there a null check that could be an implicit check? + if (array->IsNullCheck() && do_implicit_null_checks_) { + // The ArrayLen may generate the implicit null check. Can the + // bounds check do so as well? + if (array_len->GetNextDisregardingMoves() != check) { + // No, it won't. Leave as is. + return; + } + } + + // Can we suppress the ArrayLength and generate at BoundCheck? + if (array_len->HasOnlyOneNonEnvironmentUse()) { + array_len->MarkEmittedAtUseSite(); + // We need the ArrayLength just before the BoundsCheck. + array_len->MoveBefore(check); + } + } + + bool do_implicit_null_checks_; +}; + +X86MemoryOperandGeneration::X86MemoryOperandGeneration(HGraph* graph, + OptimizingCompilerStats* stats, + CodeGenerator* codegen) + : HOptimization(graph, kX86MemoryOperandGenerationPassName, stats), + do_implicit_null_checks_(codegen->GetCompilerOptions().GetImplicitNullChecks()) { +} + +void X86MemoryOperandGeneration::Run() { + MemoryOperandVisitor visitor(graph_, do_implicit_null_checks_); + visitor.VisitInsertionOrder(); +} + +} // namespace x86 +} // namespace art diff --git a/compiler/optimizing/x86_memory_gen.h b/compiler/optimizing/x86_memory_gen.h new file mode 100644 index 0000000000..7e886819bb --- /dev/null +++ b/compiler/optimizing/x86_memory_gen.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_X86_MEMORY_GEN_H_ +#define ART_COMPILER_OPTIMIZING_X86_MEMORY_GEN_H_ + +#include "nodes.h" +#include "optimization.h" + +namespace art { +class CodeGenerator; + +namespace x86 { + +class X86MemoryOperandGeneration : public HOptimization { + public: + X86MemoryOperandGeneration(HGraph* graph, + OptimizingCompilerStats* stats, + CodeGenerator* codegen); + + void Run() OVERRIDE; + + static constexpr const char* kX86MemoryOperandGenerationPassName = + "x86_memory_operand_generation"; + + private: + bool do_implicit_null_checks_; +}; + +} // namespace x86 +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_X86_MEMORY_GEN_H_ diff --git a/test/609-checker-x86-bounds-check/expected.txt b/test/609-checker-x86-bounds-check/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/609-checker-x86-bounds-check/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/609-checker-x86-bounds-check/info.txt b/test/609-checker-x86-bounds-check/info.txt new file mode 100644 index 0000000000..c0f26d0d6c --- /dev/null +++ b/test/609-checker-x86-bounds-check/info.txt @@ -0,0 +1 @@ +Checker test that we combine ArrayLength and BoundsCheck on x86/x86_64. diff --git a/test/609-checker-x86-bounds-check/src/Main.java b/test/609-checker-x86-bounds-check/src/Main.java new file mode 100644 index 0000000000..bfc2be88f7 --- /dev/null +++ b/test/609-checker-x86-bounds-check/src/Main.java @@ -0,0 +1,88 @@ +/* + * 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. + */ + +public class Main { + + public static void main(String args[]) { + int[] array = new int[51]; + testArrayLengthBoundsCheckX86(array, 10); + + System.out.println("passed"); + } + + /// CHECK-START-X86: void Main.testArrayLengthBoundsCheckX86(int[], int) x86_memory_operand_generation (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue + /// CHECK-DAG: <<Index:i\d+>> ParameterValue + /// CHECK-DAG: <<Value:i\d+>> IntConstant 9 + /// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>] + /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>] is_string_length:false loop:none + /// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>] + /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<Value>>] + + /// CHECK-START-X86: void Main.testArrayLengthBoundsCheckX86(int[], int) x86_memory_operand_generation (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue + /// CHECK-DAG: <<Index:i\d+>> ParameterValue + /// CHECK-DAG: <<Value:i\d+>> IntConstant 9 + /// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>] + /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>] is_string_length:false emitted_at_use:true loop:none + /// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>] + /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<Value>>] + + /// CHECK-START-X86: void Main.testArrayLengthBoundsCheckX86(int[], int) disassembly (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue + /// CHECK-DAG: <<Index:i\d+>> ParameterValue + /// CHECK-DAG: <<Value:i\d+>> IntConstant 9 + /// CHECK: <<CheckedArray:l\d+>> NullCheck [<<Array>>] + /// CHECK-NEXT: <<Length:i\d+>> ArrayLength [<<Array>>] is_string_length:false emitted_at_use:true loop:none + /// CHECK-NEXT: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>] + /// CHECK-NEXT: cmp [<<BaseReg:\w+>> + 8], <<IndexReg:\w+>> + /// CHECK: <<ArraySet:v\d+>> ArraySet [<<Array>>,<<Index>>,<<Value>>] + /// CHECK-NEXT: mov [<<BaseReg>> + <<IndexReg>> * 4 + 12], 9 + + /// CHECK-START-X86_64: void Main.testArrayLengthBoundsCheckX86(int[], int) x86_memory_operand_generation (before) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue + /// CHECK-DAG: <<Index:i\d+>> ParameterValue + /// CHECK-DAG: <<Value:i\d+>> IntConstant 9 + /// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>] + /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>] is_string_length:false loop:none + /// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>] + /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<Value>>] + + /// CHECK-START-X86_64: void Main.testArrayLengthBoundsCheckX86(int[], int) x86_memory_operand_generation (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue + /// CHECK-DAG: <<Index:i\d+>> ParameterValue + /// CHECK-DAG: <<Value:i\d+>> IntConstant 9 + /// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>] + /// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>] is_string_length:false emitted_at_use:true loop:none + /// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>] + /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<Value>>] + + // Test assumes parameter value is in lower 8 registers (it is passed in edx). + /// CHECK-START-X86_64: void Main.testArrayLengthBoundsCheckX86(int[], int) disassembly (after) + /// CHECK-DAG: <<Array:l\d+>> ParameterValue + /// CHECK-DAG: <<Index:i\d+>> ParameterValue + /// CHECK-DAG: <<Value:i\d+>> IntConstant 9 + /// CHECK: <<CheckedArray:l\d+>> NullCheck [<<Array>>] + /// CHECK-NEXT: <<Length:i\d+>> ArrayLength [<<Array>>] is_string_length:false emitted_at_use:true loop:none + /// CHECK-NEXT: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>] + /// CHECK-NEXT: cmp [<<BaseReg:\w+>> + 8], e<<IndexReg:\w+>> + /// CHECK: <<ArraySet:v\d+>> ArraySet [<<Array>>,<<Index>>,<<Value>>] + /// CHECK-NEXT: mov [<<BaseReg>> + r<<IndexReg>> * 4 + 12], 9 + + static void testArrayLengthBoundsCheckX86(int[] array, int index) { + array[index] = 9; + } +} |