OSDN Git Service

Merge "Add a cpplint preupload hook."
authorTreehugger Robot <treehugger-gerrit@google.com>
Mon, 3 Apr 2017 19:52:18 +0000 (19:52 +0000)
committerGerrit Code Review <noreply-gerritcodereview@google.com>
Mon, 3 Apr 2017 19:52:19 +0000 (19:52 +0000)
114 files changed:
compiler/Android.bp
compiler/optimizing/code_generator_arm64.h
compiler/optimizing/code_generator_vector_arm.cc [new file with mode: 0644]
compiler/optimizing/code_generator_vector_arm64.cc [new file with mode: 0644]
compiler/optimizing/code_generator_vector_arm_vixl.cc [new file with mode: 0644]
compiler/optimizing/code_generator_vector_mips.cc [new file with mode: 0644]
compiler/optimizing/code_generator_vector_mips64.cc [new file with mode: 0644]
compiler/optimizing/code_generator_vector_x86.cc [new file with mode: 0644]
compiler/optimizing/code_generator_vector_x86_64.cc [new file with mode: 0644]
compiler/optimizing/inliner.cc
compiler/optimizing/loop_optimization.cc
compiler/optimizing/loop_optimization.h
compiler/optimizing/nodes.cc
compiler/optimizing/nodes.h
compiler/optimizing/nodes_vector.h [new file with mode: 0644]
compiler/optimizing/ssa_liveness_analysis.cc
compiler/utils/x86/assembler_x86.cc
compiler/utils/x86/assembler_x86.h
compiler/utils/x86/assembler_x86_test.cc
compiler/utils/x86_64/assembler_x86_64.cc
compiler/utils/x86_64/assembler_x86_64.h
compiler/utils/x86_64/assembler_x86_64_test.cc
dexlayout/dex_ir.cc
dexlayout/dex_ir.h
dexlayout/dex_visualize.cc
dexlayout/dexlayout_test.cc
disassembler/disassembler_x86.cc
runtime/arch/arm64/quick_entrypoints_arm64.S
runtime/jit/profile_compilation_info.cc
runtime/native/dalvik_system_VMRuntime.cc
runtime/openjdkjvmti/ti_class.cc
runtime/openjdkjvmti/ti_class_definition.cc
runtime/openjdkjvmti/ti_class_definition.h
runtime/openjdkjvmti/ti_heap.cc
runtime/openjdkjvmti/ti_redefine.cc
runtime/openjdkjvmti/ti_redefine.h
runtime/openjdkjvmti/transform.cc
runtime/openjdkjvmti/transform.h
runtime/thread_list.cc
test/640-checker-boolean-simd/expected.txt [new file with mode: 0644]
test/640-checker-boolean-simd/info.txt [new file with mode: 0644]
test/640-checker-boolean-simd/src/Main.java [new file with mode: 0644]
test/640-checker-byte-simd/expected.txt [new file with mode: 0644]
test/640-checker-byte-simd/info.txt [new file with mode: 0644]
test/640-checker-byte-simd/src/Main.java [new file with mode: 0644]
test/640-checker-char-simd/expected.txt [new file with mode: 0644]
test/640-checker-char-simd/info.txt [new file with mode: 0644]
test/640-checker-char-simd/src/Main.java [new file with mode: 0644]
test/640-checker-double-simd/expected.txt [new file with mode: 0644]
test/640-checker-double-simd/info.txt [new file with mode: 0644]
test/640-checker-double-simd/src/Main.java [new file with mode: 0644]
test/640-checker-float-simd/expected.txt [new file with mode: 0644]
test/640-checker-float-simd/info.txt [new file with mode: 0644]
test/640-checker-float-simd/src/Main.java [new file with mode: 0644]
test/640-checker-int-simd/expected.txt [new file with mode: 0644]
test/640-checker-int-simd/info.txt [new file with mode: 0644]
test/640-checker-int-simd/src/Main.java [new file with mode: 0644]
test/640-checker-long-simd/expected.txt [new file with mode: 0644]
test/640-checker-long-simd/info.txt [new file with mode: 0644]
test/640-checker-long-simd/src/Main.java [new file with mode: 0644]
test/640-checker-short-simd/expected.txt [new file with mode: 0644]
test/640-checker-short-simd/info.txt [new file with mode: 0644]
test/640-checker-short-simd/src/Main.java [new file with mode: 0644]
test/901-hello-ti-agent/basics.cc
test/903-hello-tagging/tagging.cc
test/904-object-allocation/tracking.cc
test/905-object-free/tracking_free.cc
test/906-iterate-heap/iterate_heap.cc
test/907-get-loaded-classes/get_loaded_classes.cc
test/908-gc-start-finish/gc_callbacks.cc
test/909-attach-agent/attach.cc
test/910-methods/methods.cc
test/911-get-stack-trace/stack_trace.cc
test/912-classes/classes.cc
test/913-heaps/expected.txt
test/913-heaps/heaps.cc
test/913-heaps/src/Main.java
test/918-fields/fields.cc
test/920-objects/objects.cc
test/922-properties/properties.cc
test/923-monitors/monitors.cc
test/924-threads/threads.cc
test/925-threadgroups/threadgroups.cc
test/927-timers/timers.cc
test/928-jni-table/jni_table.cc
test/929-search/search.cc
test/931-agent-thread/agent_thread.cc
test/933-misc-events/misc_events.cc
test/936-search-onload/search_onload.cc
test/944-transform-classloaders/classloader.cc
test/945-obsolete-native/obsolete_native.cc
test/980-redefine-object/redefine_object.cc
test/981-dedup-original-dex/src/Main.java
test/983-source-transform-verify/source_transform.cc
test/Android.bp
test/knownfailures.json
test/ti-agent/agent_startup.cc [new file with mode: 0644]
test/ti-agent/agent_startup.h [new file with mode: 0644]
test/ti-agent/common_helper.cc
test/ti-agent/common_helper.h
test/ti-agent/common_load.cc
test/ti-agent/jni_binder.cc [new file with mode: 0644]
test/ti-agent/jni_binder.h [new file with mode: 0644]
test/ti-agent/jni_helper.h [new file with mode: 0644]
test/ti-agent/jvmti_helper.cc [new file with mode: 0644]
test/ti-agent/jvmti_helper.h [new file with mode: 0644]
test/ti-agent/scoped_local_ref.h [new file with mode: 0644]
test/ti-agent/scoped_primitive_array.h [new file with mode: 0644]
test/ti-agent/scoped_utf_chars.h [new file with mode: 0644]
test/ti-agent/test_env.cc [new file with mode: 0644]
test/ti-agent/test_env.h [moved from test/ti-agent/common_load.h with 82% similarity]
test/ti-agent/ti_macros.h [new file with mode: 0644]
test/ti-agent/ti_utf.h [new file with mode: 0644]
tools/setup-buildbot-device.sh

index b444fff..312fc7b 100644 (file)
@@ -106,7 +106,9 @@ art_cc_defaults {
                 "linker/arm/relative_patcher_arm_base.cc",
                 "linker/arm/relative_patcher_thumb2.cc",
                 "optimizing/code_generator_arm.cc",
+                "optimizing/code_generator_vector_arm.cc",
                 "optimizing/code_generator_arm_vixl.cc",
+                "optimizing/code_generator_vector_arm_vixl.cc",
                 "optimizing/dex_cache_array_fixups_arm.cc",
                 "optimizing/instruction_simplifier_arm.cc",
                 "optimizing/instruction_simplifier_shared.cc",
@@ -126,6 +128,7 @@ art_cc_defaults {
                 "jni/quick/arm64/calling_convention_arm64.cc",
                 "linker/arm64/relative_patcher_arm64.cc",
                 "optimizing/code_generator_arm64.cc",
+                "optimizing/code_generator_vector_arm64.cc",
                 "optimizing/scheduler_arm64.cc",
                 "optimizing/instruction_simplifier_arm64.cc",
                 "optimizing/intrinsics_arm64.cc",
@@ -139,6 +142,7 @@ art_cc_defaults {
                 "jni/quick/mips/calling_convention_mips.cc",
                 "linker/mips/relative_patcher_mips.cc",
                 "optimizing/code_generator_mips.cc",
+                "optimizing/code_generator_vector_mips.cc",
                 "optimizing/dex_cache_array_fixups_mips.cc",
                 "optimizing/intrinsics_mips.cc",
                 "optimizing/pc_relative_fixups_mips.cc",
@@ -151,6 +155,7 @@ art_cc_defaults {
                 "jni/quick/mips64/calling_convention_mips64.cc",
                 "linker/mips64/relative_patcher_mips64.cc",
                 "optimizing/code_generator_mips64.cc",
+                "optimizing/code_generator_vector_mips64.cc",
                 "optimizing/intrinsics_mips64.cc",
                 "utils/mips64/assembler_mips64.cc",
                 "utils/mips64/managed_register_mips64.cc",
@@ -162,6 +167,7 @@ art_cc_defaults {
                 "linker/x86/relative_patcher_x86.cc",
                 "linker/x86/relative_patcher_x86_base.cc",
                 "optimizing/code_generator_x86.cc",
+                "optimizing/code_generator_vector_x86.cc",
                 "optimizing/intrinsics_x86.cc",
                 "optimizing/pc_relative_fixups_x86.cc",
                 "optimizing/x86_memory_gen.cc",
@@ -176,6 +182,7 @@ art_cc_defaults {
                 "linker/x86_64/relative_patcher_x86_64.cc",
                 "optimizing/intrinsics_x86_64.cc",
                 "optimizing/code_generator_x86_64.cc",
+                "optimizing/code_generator_vector_x86_64.cc",
                 "utils/x86_64/assembler_x86_64.cc",
                 "utils/x86_64/jni_macro_assembler_x86_64.cc",
                 "utils/x86_64/managed_register_x86_64.cc",
index 7471cd5..10d8b84 100644 (file)
@@ -318,6 +318,11 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator {
   void GenerateDivRemIntegral(HBinaryOperation* instruction);
   void HandleGoto(HInstruction* got, HBasicBlock* successor);
 
+  vixl::aarch64::MemOperand CreateVecMemRegisters(
+      HVecMemoryOperation* instruction,
+      Location* reg_loc,
+      bool is_load);
+
   Arm64Assembler* const assembler_;
   CodeGeneratorARM64* const codegen_;
 
diff --git a/compiler/optimizing/code_generator_vector_arm.cc b/compiler/optimizing/code_generator_vector_arm.cc
new file mode 100644 (file)
index 0000000..ba2b2cb
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 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 "code_generator_arm.h"
+
+namespace art {
+namespace arm {
+
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<ArmAssembler*>(GetAssembler())->  // NOLINT
+
+void LocationsBuilderARM::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARM::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARM::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARM::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector unary operations.
+static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM::VisitVecCnv(HVecCnv* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecCnv(HVecCnv* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecNeg(HVecNeg* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecNeg(HVecNeg* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecNot(HVecNot* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecNot(HVecNot* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector binary operations.
+static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM::VisitVecAdd(HVecAdd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecAdd(HVecAdd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecSub(HVecSub* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecSub(HVecSub* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecMul(HVecMul* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecMul(HVecMul* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecDiv(HVecDiv* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecDiv(HVecDiv* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecAnd(HVecAnd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecAnd(HVecAnd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecAndNot(HVecAndNot* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecAndNot(HVecAndNot* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecOr(HVecOr* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecOr(HVecOr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecXor(HVecXor* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecXor(HVecXor* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector shift operations.
+static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM::VisitVecShl(HVecShl* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecShl(HVecShl* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecShr(HVecShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecShr(HVecShr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecUShr(HVecUShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM::VisitVecUShr(HVecUShr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecLoad(HVecLoad* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARM::VisitVecLoad(HVecLoad* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM::VisitVecStore(HVecStore* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARM::VisitVecStore(HVecStore* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+#undef __
+
+}  // namespace arm
+}  // namespace art
diff --git a/compiler/optimizing/code_generator_vector_arm64.cc b/compiler/optimizing/code_generator_vector_arm64.cc
new file mode 100644 (file)
index 0000000..96d0021
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2017 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 "code_generator_arm64.h"
+#include "mirror/array-inl.h"
+
+using namespace vixl::aarch64;  // NOLINT(build/namespaces)
+
+namespace art {
+namespace arm64 {
+
+using helpers::DRegisterFrom;
+using helpers::HeapOperand;
+using helpers::InputRegisterAt;
+using helpers::Int64ConstantFrom;
+using helpers::XRegisterFrom;
+
+#define __ GetVIXLAssembler()->
+
+void LocationsBuilderARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    case Primitive::kPrimFloat:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void InstructionCodeGeneratorARM64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Dup(dst.V8B(), InputRegisterAt(instruction, 0));
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Dup(dst.V4H(), InputRegisterAt(instruction, 0));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Dup(dst.V2S(), InputRegisterAt(instruction, 0));
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Dup(dst.V2S(), DRegisterFrom(locations->InAt(0)).V2S(), 0);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARM64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARM64::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARM64::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector unary operations.
+static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(),
+                        instruction->IsVecNot() ? Location::kOutputOverlap
+                                                : Location::kNoOutputOverlap);
+      break;
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecCnv(HVecCnv* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecCnv(HVecCnv* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister src = DRegisterFrom(locations->InAt(0));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  Primitive::Type from = instruction->GetInputType();
+  Primitive::Type to = instruction->GetResultType();
+  if (from == Primitive::kPrimInt && to == Primitive::kPrimFloat) {
+    DCHECK_EQ(2u, instruction->GetVectorLength());
+    __ Scvtf(dst.V2S(), src.V2S());
+  } else {
+    LOG(FATAL) << "Unsupported SIMD type";
+  }
+}
+
+void LocationsBuilderARM64::VisitVecNeg(HVecNeg* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecNeg(HVecNeg* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister src = DRegisterFrom(locations->InAt(0));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Neg(dst.V8B(), src.V8B());
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Neg(dst.V4H(), src.V4H());
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Neg(dst.V2S(), src.V2S());
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Fneg(dst.V2S(), src.V2S());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecNot(HVecNot* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecNot(HVecNot* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister src = DRegisterFrom(locations->InAt(0));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:  // special case boolean-not
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Movi(dst.V8B(), 1);
+      __ Eor(dst.V8B(), dst.V8B(), src.V8B());
+      break;
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+      __ Not(dst.V8B(), src.V8B());  // lanes do not matter
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector binary operations.
+static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecAdd(HVecAdd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecAdd(HVecAdd* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister rhs = DRegisterFrom(locations->InAt(1));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Add(dst.V8B(), lhs.V8B(), rhs.V8B());
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Add(dst.V4H(), lhs.V4H(), rhs.V4H());
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Add(dst.V2S(), lhs.V2S(), rhs.V2S());
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Fadd(dst.V2S(), lhs.V2S(), rhs.V2S());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecSub(HVecSub* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecSub(HVecSub* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister rhs = DRegisterFrom(locations->InAt(1));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Sub(dst.V8B(), lhs.V8B(), rhs.V8B());
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Sub(dst.V4H(), lhs.V4H(), rhs.V4H());
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Sub(dst.V2S(), lhs.V2S(), rhs.V2S());
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Fsub(dst.V2S(), lhs.V2S(), rhs.V2S());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecMul(HVecMul* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecMul(HVecMul* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister rhs = DRegisterFrom(locations->InAt(1));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Mul(dst.V8B(), lhs.V8B(), rhs.V8B());
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Mul(dst.V4H(), lhs.V4H(), rhs.V4H());
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Mul(dst.V2S(), lhs.V2S(), rhs.V2S());
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Fmul(dst.V2S(), lhs.V2S(), rhs.V2S());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecDiv(HVecDiv* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecDiv(HVecDiv* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister rhs = DRegisterFrom(locations->InAt(1));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Fdiv(dst.V2S(), lhs.V2S(), rhs.V2S());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecAnd(HVecAnd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecAnd(HVecAnd* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister rhs = DRegisterFrom(locations->InAt(1));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      __ And(dst.V8B(), lhs.V8B(), rhs.V8B());  // lanes do not matter
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecAndNot(HVecAndNot* instruction) {
+  LOG(FATAL) << "Unsupported SIMD instruction " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARM64::VisitVecAndNot(HVecAndNot* instruction) {
+  LOG(FATAL) << "Unsupported SIMD instruction " << instruction->GetId();
+}
+
+void LocationsBuilderARM64::VisitVecOr(HVecOr* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecOr(HVecOr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister rhs = DRegisterFrom(locations->InAt(1));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      __ Orr(dst.V8B(), lhs.V8B(), rhs.V8B());  // lanes do not matter
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecXor(HVecXor* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecXor(HVecXor* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister rhs = DRegisterFrom(locations->InAt(1));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      __ Eor(dst.V8B(), lhs.V8B(), rhs.V8B());  // lanes do not matter
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector shift operations.
+static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+      locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecShl(HVecShl* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecShl(HVecShl* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Shl(dst.V8B(), lhs.V8B(), value);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Shl(dst.V4H(), lhs.V4H(), value);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Shl(dst.V2S(), lhs.V2S(), value);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecShr(HVecShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecShr(HVecShr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Sshr(dst.V8B(), lhs.V8B(), value);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Sshr(dst.V4H(), lhs.V4H(), value);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Sshr(dst.V2S(), lhs.V2S(), value);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecUShr(HVecUShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecUShr(HVecUShr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  FPRegister lhs = DRegisterFrom(locations->InAt(0));
+  FPRegister dst = DRegisterFrom(locations->Out());
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Ushr(dst.V8B(), lhs.V8B(), value);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Ushr(dst.V4H(), lhs.V4H(), value);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Ushr(dst.V2S(), lhs.V2S(), value);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector memory operations.
+static void CreateVecMemLocations(ArenaAllocator* arena,
+                                  HVecMemoryOperation* instruction,
+                                  bool is_load) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+      if (is_load) {
+        locations->SetOut(Location::RequiresFpuRegister());
+      } else {
+        locations->SetInAt(2, Location::RequiresFpuRegister());
+      }
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up registers and address for vector memory operations.
+MemOperand InstructionCodeGeneratorARM64::CreateVecMemRegisters(
+    HVecMemoryOperation* instruction,
+    Location* reg_loc,
+    bool is_load) {
+  LocationSummary* locations = instruction->GetLocations();
+  Register base = InputRegisterAt(instruction, 0);
+  Location index = locations->InAt(1);
+  *reg_loc = is_load ? locations->Out() : locations->InAt(2);
+
+  Primitive::Type packed_type = instruction->GetPackedType();
+  uint32_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(packed_type)).Uint32Value();
+  size_t shift = Primitive::ComponentSizeShift(packed_type);
+
+  UseScratchRegisterScope temps(GetVIXLAssembler());
+  Register temp = temps.AcquireSameSizeAs(base);
+  if (index.IsConstant()) {
+    offset += Int64ConstantFrom(index) << shift;
+    __ Add(temp, base, offset);
+  } else {
+    if (instruction->InputAt(0)->IsIntermediateAddress()) {
+      temp = base;
+    } else {
+      __ Add(temp, base, offset);
+    }
+    __ Add(temp.X(), temp.X(), Operand(XRegisterFrom(index), LSL, shift));
+  }
+  return HeapOperand(temp);
+}
+
+void LocationsBuilderARM64::VisitVecLoad(HVecLoad* instruction) {
+  CreateVecMemLocations(GetGraph()->GetArena(), instruction, /*is_load*/ true);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecLoad(HVecLoad* instruction) {
+  Location reg_loc = Location::NoLocation();
+  MemOperand mem = CreateVecMemRegisters(instruction, &reg_loc, /*is_load*/ true);
+  FPRegister reg = DRegisterFrom(reg_loc);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ Ld1(reg.V8B(), mem);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ Ld1(reg.V4H(), mem);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ Ld1(reg.V2S(), mem);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARM64::VisitVecStore(HVecStore* instruction) {
+  CreateVecMemLocations(GetGraph()->GetArena(), instruction, /*is_load*/ false);
+}
+
+void InstructionCodeGeneratorARM64::VisitVecStore(HVecStore* instruction) {
+  Location reg_loc = Location::NoLocation();
+  MemOperand mem = CreateVecMemRegisters(instruction, &reg_loc, /*is_load*/ false);
+  FPRegister reg = DRegisterFrom(reg_loc);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ St1(reg.V8B(), mem);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ St1(reg.V4H(), mem);
+      break;
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ St1(reg.V2S(), mem);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+#undef __
+
+}  // namespace arm64
+}  // namespace art
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
new file mode 100644 (file)
index 0000000..1711989
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 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 "code_generator_arm_vixl.h"
+
+namespace art {
+namespace arm {
+
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()->  // NOLINT
+
+void LocationsBuilderARMVIXL::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector unary operations.
+static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitVecCnv(HVecCnv* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecCnv(HVecCnv* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecNeg(HVecNeg* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecNeg(HVecNeg* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecNot(HVecNot* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecNot(HVecNot* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector binary operations.
+static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitVecAdd(HVecAdd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecAdd(HVecAdd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecSub(HVecSub* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecSub(HVecSub* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecMul(HVecMul* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecMul(HVecMul* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecDiv(HVecDiv* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecDiv(HVecDiv* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecAnd(HVecAnd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecAnd(HVecAnd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecAndNot(HVecAndNot* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecAndNot(HVecAndNot* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecOr(HVecOr* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecOr(HVecOr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecXor(HVecXor* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecXor(HVecXor* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector shift operations.
+static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderARMVIXL::VisitVecShl(HVecShl* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecShl(HVecShl* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecShr(HVecShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecShr(HVecShr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecUShr(HVecUShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecUShr(HVecUShr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecLoad(HVecLoad* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecLoad(HVecLoad* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderARMVIXL::VisitVecStore(HVecStore* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitVecStore(HVecStore* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+#undef __
+
+}  // namespace arm
+}  // namespace art
diff --git a/compiler/optimizing/code_generator_vector_mips.cc b/compiler/optimizing/code_generator_vector_mips.cc
new file mode 100644 (file)
index 0000000..6f5fe0d
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 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 "code_generator_mips.h"
+
+namespace art {
+namespace mips {
+
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<MipsAssembler*>(GetAssembler())->  // NOLINT
+
+void LocationsBuilderMIPS::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector unary operations.
+static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderMIPS::VisitVecCnv(HVecCnv* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecCnv(HVecCnv* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecNeg(HVecNeg* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecNeg(HVecNeg* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecNot(HVecNot* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecNot(HVecNot* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector binary operations.
+static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderMIPS::VisitVecAdd(HVecAdd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecAdd(HVecAdd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecSub(HVecSub* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecSub(HVecSub* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecMul(HVecMul* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecMul(HVecMul* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecDiv(HVecDiv* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecDiv(HVecDiv* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecAnd(HVecAnd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecAnd(HVecAnd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecAndNot(HVecAndNot* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecAndNot(HVecAndNot* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecOr(HVecOr* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecOr(HVecOr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecXor(HVecXor* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecXor(HVecXor* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector shift operations.
+static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderMIPS::VisitVecShl(HVecShl* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecShl(HVecShl* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecShr(HVecShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecShr(HVecShr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecUShr(HVecUShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecUShr(HVecUShr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecLoad(HVecLoad* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecLoad(HVecLoad* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS::VisitVecStore(HVecStore* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS::VisitVecStore(HVecStore* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+#undef __
+
+}  // namespace mips
+}  // namespace art
diff --git a/compiler/optimizing/code_generator_vector_mips64.cc b/compiler/optimizing/code_generator_vector_mips64.cc
new file mode 100644 (file)
index 0000000..2ee7ac9
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 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 "code_generator_mips64.h"
+
+namespace art {
+namespace mips64 {
+
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<Mips64Assembler*>(GetAssembler())->  // NOLINT
+
+void LocationsBuilderMIPS64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector unary operations.
+static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderMIPS64::VisitVecCnv(HVecCnv* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecCnv(HVecCnv* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecNeg(HVecNeg* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecNeg(HVecNeg* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecNot(HVecNot* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecNot(HVecNot* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector binary operations.
+static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderMIPS64::VisitVecAdd(HVecAdd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecAdd(HVecAdd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecSub(HVecSub* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecSub(HVecSub* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecMul(HVecMul* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecMul(HVecMul* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecDiv(HVecDiv* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecDiv(HVecDiv* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecAnd(HVecAnd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecAnd(HVecAnd* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecAndNot(HVecAndNot* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecAndNot(HVecAndNot* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecOr(HVecOr* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecOr(HVecOr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecXor(HVecXor* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecXor(HVecXor* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector shift operations.
+static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK(locations);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderMIPS64::VisitVecShl(HVecShl* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecShl(HVecShl* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecShr(HVecShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecShr(HVecShr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecUShr(HVecUShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecUShr(HVecUShr* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecLoad(HVecLoad* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecLoad(HVecLoad* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderMIPS64::VisitVecStore(HVecStore* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorMIPS64::VisitVecStore(HVecStore* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+#undef __
+
+}  // namespace mips64
+}  // namespace art
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
new file mode 100644 (file)
index 0000000..4f3988e
--- /dev/null
@@ -0,0 +1,767 @@
+/*
+ * Copyright (C) 2017 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 "code_generator_x86.h"
+#include "mirror/array-inl.h"
+
+namespace art {
+namespace x86 {
+
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86Assembler*>(GetAssembler())->  // NOLINT
+
+void LocationsBuilderX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimLong:
+      // Long needs extra temporary to load the register pair.
+      locations->AddTemp(Location::RequiresFpuRegister());
+      FALLTHROUGH_INTENDED;
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister reg = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ movd(reg, locations->InAt(0).AsRegister<Register>());
+      __ punpcklbw(reg, reg);
+      __ punpcklwd(reg, reg);
+      __ pshufd(reg, reg, Immediate(0));
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ movd(reg, locations->InAt(0).AsRegister<Register>());
+      __ punpcklwd(reg, reg);
+      __ pshufd(reg, reg, Immediate(0));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ movd(reg, locations->InAt(0).AsRegister<Register>());
+      __ pshufd(reg, reg, Immediate(0));
+      break;
+    case Primitive::kPrimLong: {
+      XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ movd(reg, locations->InAt(0).AsRegisterPairLow<Register>());
+      __ movd(tmp, locations->InAt(0).AsRegisterPairHigh<Register>());
+      __ punpckldq(reg, tmp);
+      __ punpcklqdq(reg, reg);
+      break;
+    }
+    case Primitive::kPrimFloat:
+      DCHECK(locations->InAt(0).Equals(locations->Out()));
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ shufps(reg, reg, Immediate(0));
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK(locations->InAt(0).Equals(locations->Out()));
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ shufpd(reg, reg, Immediate(0));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorX86::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderX86::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorX86::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector unary operations.
+static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecCnv(HVecCnv* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecCnv(HVecCnv* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  Primitive::Type from = instruction->GetInputType();
+  Primitive::Type to = instruction->GetResultType();
+  if (from == Primitive::kPrimInt && to == Primitive::kPrimFloat) {
+    DCHECK_EQ(4u, instruction->GetVectorLength());
+    __ cvtdq2ps(dst, src);
+  } else {
+    LOG(FATAL) << "Unsupported SIMD type";
+  }
+}
+
+void LocationsBuilderX86::VisitVecNeg(HVecNeg* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecNeg(HVecNeg* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ pxor(dst, dst);
+      __ psubb(dst, src);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ pxor(dst, dst);
+      __ psubw(dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ pxor(dst, dst);
+      __ psubd(dst, src);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ pxor(dst, dst);
+      __ psubq(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ xorps(dst, dst);
+      __ subps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ xorpd(dst, dst);
+      __ subpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecNot(HVecNot* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+  // Boolean-not requires a temporary to construct the 16 x one.
+  if (instruction->GetPackedType() == Primitive::kPrimBoolean) {
+    instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
+  }
+}
+
+void InstructionCodeGeneratorX86::VisitVecNot(HVecNot* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean: {  // special case boolean-not
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+      __ pxor(dst, dst);
+      __ pcmpeqb(tmp, tmp);  // all ones
+      __ psubb(dst, tmp);  // 16 x one
+      __ pxor(dst, src);
+      break;
+    }
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ pcmpeqb(dst, dst);  // all ones
+      __ pxor(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ pcmpeqb(dst, dst);  // all ones
+      __ xorps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ pcmpeqb(dst, dst);  // all ones
+      __ xorpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector binary operations.
+static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecAdd(HVecAdd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecAdd(HVecAdd* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ paddb(dst, src);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ paddw(dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ paddd(dst, src);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ paddq(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ addps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ addpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecSub(HVecSub* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecSub(HVecSub* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ psubb(dst, src);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ psubw(dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ psubd(dst, src);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ psubq(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ subps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ subpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecMul(HVecMul* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecMul(HVecMul* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ pmullw(dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ pmulld(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ mulps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ mulpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecDiv(HVecDiv* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecDiv(HVecDiv* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ divps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ divpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecAnd(HVecAnd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecAnd(HVecAnd* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ pand(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ andps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ andpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecAndNot(HVecAndNot* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecAndNot(HVecAndNot* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ pandn(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ andnps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ andnpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecOr(HVecOr* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecOr(HVecOr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ por(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ orps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ orpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecXor(HVecXor* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecXor(HVecXor* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ pxor(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ xorps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ xorpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector shift operations.
+static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecShl(HVecShl* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecShl(HVecShl* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ psllw(dst, Immediate(static_cast<uint8_t>(value)));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ pslld(dst, Immediate(static_cast<uint8_t>(value)));
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ psllq(dst, Immediate(static_cast<uint8_t>(value)));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecShr(HVecShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecShr(HVecShr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ psraw(dst, Immediate(static_cast<uint8_t>(value)));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ psrad(dst, Immediate(static_cast<uint8_t>(value)));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecUShr(HVecUShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86::VisitVecUShr(HVecUShr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ psrlw(dst, Immediate(static_cast<uint8_t>(value)));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ psrld(dst, Immediate(static_cast<uint8_t>(value)));
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ psrlq(dst, Immediate(static_cast<uint8_t>(value)));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector memory operations.
+static void CreateVecMemLocations(ArenaAllocator* arena,
+                                  HVecMemoryOperation* instruction,
+                                  bool is_load) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+      if (is_load) {
+        locations->SetOut(Location::RequiresFpuRegister());
+      } else {
+        locations->SetInAt(2, Location::RequiresFpuRegister());
+      }
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up registers and address for vector memory operations.
+static Address CreateVecMemRegisters(HVecMemoryOperation* instruction,
+                                     Location* reg_loc,
+                                     bool is_load) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location base = locations->InAt(0);
+  Location index = locations->InAt(1);
+  *reg_loc = is_load ? locations->Out() : locations->InAt(2);
+  size_t size = Primitive::ComponentSize(instruction->GetPackedType());
+  uint32_t offset = mirror::Array::DataOffset(size).Uint32Value();
+  ScaleFactor scale = TIMES_1;
+  switch (size) {
+    case 2: scale = TIMES_2; break;
+    case 4: scale = TIMES_4; break;
+    case 8: scale = TIMES_8; break;
+    default: break;
+  }
+  return CodeGeneratorX86::ArrayAddress(base.AsRegister<Register>(), index, scale, offset);
+}
+
+void LocationsBuilderX86::VisitVecLoad(HVecLoad* instruction) {
+  CreateVecMemLocations(GetGraph()->GetArena(), instruction, /*is_load*/ true);
+}
+
+void InstructionCodeGeneratorX86::VisitVecLoad(HVecLoad* instruction) {
+  Location reg_loc = Location::NoLocation();
+  Address address = CreateVecMemRegisters(instruction, &reg_loc, /*is_load*/ true);
+  XmmRegister reg = reg_loc.AsFpuRegister<XmmRegister>();
+  bool is_aligned16 = instruction->GetAlignment().IsAlignedAt(16);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      is_aligned16 ? __ movdqa(reg, address) : __ movdqu(reg, address);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      is_aligned16 ? __ movaps(reg, address) : __ movups(reg, address);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      is_aligned16 ? __ movapd(reg, address) : __ movupd(reg, address);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86::VisitVecStore(HVecStore* instruction) {
+  CreateVecMemLocations(GetGraph()->GetArena(), instruction, /*is_load*/ false);
+}
+
+void InstructionCodeGeneratorX86::VisitVecStore(HVecStore* instruction) {
+  Location reg_loc = Location::NoLocation();
+  Address address = CreateVecMemRegisters(instruction, &reg_loc, /*is_load*/ false);
+  XmmRegister reg = reg_loc.AsFpuRegister<XmmRegister>();
+  bool is_aligned16 = instruction->GetAlignment().IsAlignedAt(16);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      is_aligned16 ? __ movdqa(address, reg) : __ movdqu(address, reg);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      is_aligned16 ? __ movaps(address, reg) : __ movups(address, reg);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      is_aligned16 ? __ movapd(address, reg) : __ movupd(address, reg);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+#undef __
+
+}  // namespace x86
+}  // namespace art
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
new file mode 100644 (file)
index 0000000..b1c1494
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ * Copyright (C) 2017 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 "code_generator_x86_64.h"
+#include "mirror/array-inl.h"
+
+namespace art {
+namespace x86_64 {
+
+// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
+#define __ down_cast<X86_64Assembler*>(GetAssembler())->  // NOLINT
+
+void LocationsBuilderX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecReplicateScalar(HVecReplicateScalar* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister reg = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
+      __ punpcklbw(reg, reg);
+      __ punpcklwd(reg, reg);
+      __ pshufd(reg, reg, Immediate(0));
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
+      __ punpcklwd(reg, reg);
+      __ pshufd(reg, reg, Immediate(0));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());
+      __ pshufd(reg, reg, Immediate(0));
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ movd(reg, locations->InAt(0).AsRegister<CpuRegister>());  // is 64-bit
+      __ punpcklqdq(reg, reg);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK(locations->InAt(0).Equals(locations->Out()));
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ shufps(reg, reg, Immediate(0));
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK(locations->InAt(0).Equals(locations->Out()));
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ shufpd(reg, reg, Immediate(0));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecSetScalars(HVecSetScalars* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void LocationsBuilderX86_64::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecSumReduce(HVecSumReduce* instruction) {
+  LOG(FATAL) << "No SIMD for " << instruction->GetId();
+}
+
+// Helper to set up locations for vector unary operations.
+static void CreateVecUnOpLocations(ArenaAllocator* arena, HVecUnaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetOut(Location::RequiresFpuRegister());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecCnv(HVecCnv* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecCnv(HVecCnv* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  Primitive::Type from = instruction->GetInputType();
+  Primitive::Type to = instruction->GetResultType();
+  if (from == Primitive::kPrimInt && to == Primitive::kPrimFloat) {
+    DCHECK_EQ(4u, instruction->GetVectorLength());
+    __ cvtdq2ps(dst, src);
+  } else {
+    LOG(FATAL) << "Unsupported SIMD type";
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecNeg(HVecNeg* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecNeg(HVecNeg* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ pxor(dst, dst);
+      __ psubb(dst, src);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ pxor(dst, dst);
+      __ psubw(dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ pxor(dst, dst);
+      __ psubd(dst, src);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ pxor(dst, dst);
+      __ psubq(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ xorps(dst, dst);
+      __ subps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ xorpd(dst, dst);
+      __ subpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecNot(HVecNot* instruction) {
+  CreateVecUnOpLocations(GetGraph()->GetArena(), instruction);
+  // Boolean-not requires a temporary to construct the 16 x one.
+  if (instruction->GetPackedType() == Primitive::kPrimBoolean) {
+    instruction->GetLocations()->AddTemp(Location::RequiresFpuRegister());
+  }
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecNot(HVecNot* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  XmmRegister src = locations->InAt(0).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean: {  // special case boolean-not
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+      __ pxor(dst, dst);
+      __ pcmpeqb(tmp, tmp);  // all ones
+      __ psubb(dst, tmp);  // 16 x one
+      __ pxor(dst, src);
+      break;
+    }
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ pcmpeqb(dst, dst);  // all ones
+      __ pxor(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ pcmpeqb(dst, dst);  // all ones
+      __ xorps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ pcmpeqb(dst, dst);  // all ones
+      __ xorpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector binary operations.
+static void CreateVecBinOpLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::RequiresFpuRegister());
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecAdd(HVecAdd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecAdd(HVecAdd* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ paddb(dst, src);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ paddw(dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ paddd(dst, src);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ paddq(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ addps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ addpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecSub(HVecSub* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecSub(HVecSub* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimByte:
+      DCHECK_EQ(16u, instruction->GetVectorLength());
+      __ psubb(dst, src);
+      break;
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ psubw(dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ psubd(dst, src);
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ psubq(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ subps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ subpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecMul(HVecMul* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecMul(HVecMul* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ pmullw(dst, src);
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ pmulld(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ mulps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ mulpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecDiv(HVecDiv* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecDiv(HVecDiv* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ divps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ divpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecAnd(HVecAnd* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecAnd(HVecAnd* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ pand(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ andps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ andpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecAndNot(HVecAndNot* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecAndNot(HVecAndNot* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ pandn(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ andnps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ andnpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecOr(HVecOr* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecOr(HVecOr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ por(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ orps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ orpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecXor(HVecXor* instruction) {
+  CreateVecBinOpLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecXor(HVecXor* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  XmmRegister src = locations->InAt(1).AsFpuRegister<XmmRegister>();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      __ pxor(dst, src);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ xorps(dst, src);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ xorpd(dst, src);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector shift operations.
+static void CreateVecShiftLocations(ArenaAllocator* arena, HVecBinaryOperation* instruction) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      locations->SetInAt(0, Location::RequiresFpuRegister());
+      locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
+      locations->SetOut(Location::SameAsFirstInput());
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecShl(HVecShl* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecShl(HVecShl* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ psllw(dst, Immediate(static_cast<int8_t>(value)));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ pslld(dst, Immediate(static_cast<int8_t>(value)));
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ psllq(dst, Immediate(static_cast<int8_t>(value)));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecShr(HVecShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecShr(HVecShr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ psraw(dst, Immediate(static_cast<int8_t>(value)));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ psrad(dst, Immediate(static_cast<int8_t>(value)));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecUShr(HVecUShr* instruction) {
+  CreateVecShiftLocations(GetGraph()->GetArena(), instruction);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecUShr(HVecUShr* instruction) {
+  LocationSummary* locations = instruction->GetLocations();
+  DCHECK(locations->InAt(0).Equals(locations->Out()));
+  int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+  XmmRegister dst = locations->Out().AsFpuRegister<XmmRegister>();
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+      DCHECK_EQ(8u, instruction->GetVectorLength());
+      __ psrlw(dst, Immediate(static_cast<int8_t>(value)));
+      break;
+    case Primitive::kPrimInt:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      __ psrld(dst, Immediate(static_cast<int8_t>(value)));
+      break;
+    case Primitive::kPrimLong:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      __ psrlq(dst, Immediate(static_cast<int8_t>(value)));
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up locations for vector memory operations.
+static void CreateVecMemLocations(ArenaAllocator* arena,
+                                  HVecMemoryOperation* instruction,
+                                  bool is_load) {
+  LocationSummary* locations = new (arena) LocationSummary(instruction);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+    case Primitive::kPrimFloat:
+    case Primitive::kPrimDouble:
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
+      if (is_load) {
+        locations->SetOut(Location::RequiresFpuRegister());
+      } else {
+        locations->SetInAt(2, Location::RequiresFpuRegister());
+      }
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+// Helper to set up registers and address for vector memory operations.
+static Address CreateVecMemRegisters(HVecMemoryOperation* instruction,
+                                     Location* reg_loc,
+                                     bool is_load) {
+  LocationSummary* locations = instruction->GetLocations();
+  Location base = locations->InAt(0);
+  Location index = locations->InAt(1);
+  *reg_loc = is_load ? locations->Out() : locations->InAt(2);
+  size_t size = Primitive::ComponentSize(instruction->GetPackedType());
+  uint32_t offset = mirror::Array::DataOffset(size).Uint32Value();
+  ScaleFactor scale = TIMES_1;
+  switch (size) {
+    case 2: scale = TIMES_2; break;
+    case 4: scale = TIMES_4; break;
+    case 8: scale = TIMES_8; break;
+    default: break;
+  }
+  return CodeGeneratorX86_64::ArrayAddress(base.AsRegister<CpuRegister>(), index, scale, offset);
+}
+
+void LocationsBuilderX86_64::VisitVecLoad(HVecLoad* instruction) {
+  CreateVecMemLocations(GetGraph()->GetArena(), instruction, /*is_load*/ true);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecLoad(HVecLoad* instruction) {
+  Location reg_loc = Location::NoLocation();
+  Address address = CreateVecMemRegisters(instruction, &reg_loc, /*is_load*/ true);
+  XmmRegister reg = reg_loc.AsFpuRegister<XmmRegister>();
+  bool is_aligned16 = instruction->GetAlignment().IsAlignedAt(16);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      is_aligned16 ? __ movdqa(reg, address) : __ movdqu(reg, address);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      is_aligned16 ? __ movaps(reg, address) : __ movups(reg, address);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      is_aligned16 ? __ movapd(reg, address) : __ movupd(reg, address);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+void LocationsBuilderX86_64::VisitVecStore(HVecStore* instruction) {
+  CreateVecMemLocations(GetGraph()->GetArena(), instruction, /*is_load*/ false);
+}
+
+void InstructionCodeGeneratorX86_64::VisitVecStore(HVecStore* instruction) {
+  Location reg_loc = Location::NoLocation();
+  Address address = CreateVecMemRegisters(instruction, &reg_loc, /*is_load*/ false);
+  XmmRegister reg = reg_loc.AsFpuRegister<XmmRegister>();
+  bool is_aligned16 = instruction->GetAlignment().IsAlignedAt(16);
+  switch (instruction->GetPackedType()) {
+    case Primitive::kPrimBoolean:
+    case Primitive::kPrimByte:
+    case Primitive::kPrimChar:
+    case Primitive::kPrimShort:
+    case Primitive::kPrimInt:
+    case Primitive::kPrimLong:
+      DCHECK_LE(2u, instruction->GetVectorLength());
+      DCHECK_LE(instruction->GetVectorLength(), 16u);
+      is_aligned16 ? __ movdqa(address, reg) : __ movdqu(address, reg);
+      break;
+    case Primitive::kPrimFloat:
+      DCHECK_EQ(4u, instruction->GetVectorLength());
+      is_aligned16 ? __ movaps(address, reg) : __ movups(address, reg);
+      break;
+    case Primitive::kPrimDouble:
+      DCHECK_EQ(2u, instruction->GetVectorLength());
+      is_aligned16 ? __ movapd(address, reg) : __ movupd(address, reg);
+      break;
+    default:
+      LOG(FATAL) << "Unsupported SIMD type";
+      UNREACHABLE();
+  }
+}
+
+#undef __
+
+}  // namespace x86_64
+}  // namespace art
index e1cf248..79cd704 100644 (file)
@@ -63,7 +63,7 @@ static constexpr size_t kMaximumNumberOfCumulatedDexRegisters = 64;
 static constexpr size_t kMaximumNumberOfRecursiveCalls = 4;
 
 // Controls the use of inline caches in AOT mode.
-static constexpr bool kUseAOTInlineCaches = false;
+static constexpr bool kUseAOTInlineCaches = true;
 
 // We check for line numbers to make sure the DepthString implementation
 // aligns the output nicely.
index 8df513f..42ed04d 100644 (file)
 
 #include "loop_optimization.h"
 
+#include "arch/instruction_set.h"
+#include "arch/arm/instruction_set_features_arm.h"
+#include "arch/arm64/instruction_set_features_arm64.h"
+#include "arch/mips/instruction_set_features_mips.h"
+#include "arch/mips64/instruction_set_features_mips64.h"
+#include "arch/x86/instruction_set_features_x86.h"
+#include "arch/x86_64/instruction_set_features_x86_64.h"
 #include "driver/compiler_driver.h"
 #include "linear_order.h"
 
 namespace art {
 
+// Enables vectorization (SIMDization) in the loop optimizer.
+static constexpr bool kEnableVectorization = true;
+
 // Remove the instruction from the graph. A bit more elaborate than the usual
 // instruction removal, since there may be a cycle in the use structure.
 static void RemoveFromCycle(HInstruction* instruction) {
@@ -53,6 +63,19 @@ static bool IsEarlyExit(HLoopInformation* loop_info) {
   return false;
 }
 
+// Test vector restrictions.
+static bool HasVectorRestrictions(uint64_t restrictions, uint64_t tested) {
+  return (restrictions & tested) != 0;
+}
+
+// Inserts an instruction.
+static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) {
+  DCHECK(block != nullptr);
+  DCHECK(instruction != nullptr);
+  block->InsertInstructionBefore(instruction, block->GetLastInstruction());
+  return instruction;
+}
+
 //
 // Class methods.
 //
@@ -64,11 +87,15 @@ HLoopOptimization::HLoopOptimization(HGraph* graph,
       compiler_driver_(compiler_driver),
       induction_range_(induction_analysis),
       loop_allocator_(nullptr),
+      global_allocator_(graph_->GetArena()),
       top_loop_(nullptr),
       last_loop_(nullptr),
       iset_(nullptr),
       induction_simplication_count_(0),
-      simplified_(false) {
+      simplified_(false),
+      vector_length_(0),
+      vector_refs_(nullptr),
+      vector_map_(nullptr) {
 }
 
 void HLoopOptimization::Run() {
@@ -81,15 +108,13 @@ void HLoopOptimization::Run() {
   // Phase-local allocator that draws from the global pool. Since the allocator
   // itself resides on the stack, it is destructed on exiting Run(), which
   // implies its underlying memory is released immediately.
-  ArenaAllocator allocator(graph_->GetArena()->GetArenaPool());
+  ArenaAllocator allocator(global_allocator_->GetArenaPool());
   loop_allocator_ = &allocator;
 
   // Perform loop optimizations.
   LocalRun();
-
   if (top_loop_ == nullptr) {
-    // All loops have been eliminated.
-    graph_->SetHasLoops(false);
+    graph_->SetHasLoops(false);  // no more loops
   }
 
   // Detach.
@@ -111,18 +136,29 @@ void HLoopOptimization::LocalRun() {
   }
 
   // Traverse the loop hierarchy inner-to-outer and optimize. Traversal can use
-  // a temporary set that stores instructions using the phase-local allocator.
+  // temporary data structures using the phase-local allocator. All new HIR
+  // should use the global allocator.
   if (top_loop_ != nullptr) {
     ArenaSet<HInstruction*> iset(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+    ArenaSet<ArrayReference> refs(loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+    ArenaSafeMap<HInstruction*, HInstruction*> map(
+        std::less<HInstruction*>(), loop_allocator_->Adapter(kArenaAllocLoopOptimization));
+    // Attach.
     iset_ = &iset;
+    vector_refs_ = &refs;
+    vector_map_ = &map;
+    // Traverse.
     TraverseLoopsInnerToOuter(top_loop_);
-    iset_ = nullptr;  // detach
+    // Detach.
+    iset_ = nullptr;
+    vector_refs_ = nullptr;
+    vector_map_ = nullptr;
   }
 }
 
 void HLoopOptimization::AddLoop(HLoopInformation* loop_info) {
   DCHECK(loop_info != nullptr);
-  LoopNode* node = new (loop_allocator_) LoopNode(loop_info);  // phase-local allocator
+  LoopNode* node = new (loop_allocator_) LoopNode(loop_info);
   if (last_loop_ == nullptr) {
     // First loop.
     DCHECK(top_loop_ == nullptr);
@@ -170,7 +206,7 @@ void HLoopOptimization::RemoveLoop(LoopNode* node) {
 void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
   for ( ; node != nullptr; node = node->next) {
     // Visit inner loops first.
-    int current_induction_simplification_count = induction_simplication_count_;
+    uint32_t current_induction_simplification_count = induction_simplication_count_;
     if (node->inner != nullptr) {
       TraverseLoopsInnerToOuter(node->inner);
     }
@@ -179,7 +215,7 @@ void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
     if (current_induction_simplification_count != induction_simplication_count_) {
       induction_range_.ReVisit(node->loop_info);
     }
-    // Repeat simplifications in the body of this loop until no more changes occur.
+    // Repeat simplifications in the loop-body until no more changes occur.
     // Note that since each simplification consists of eliminating code (without
     // introducing new code), this process is always finite.
     do {
@@ -187,13 +223,17 @@ void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
       SimplifyInduction(node);
       SimplifyBlocks(node);
     } while (simplified_);
-    // Simplify inner loop.
+    // Optimize inner loop.
     if (node->inner == nullptr) {
-      SimplifyInnerLoop(node);
+      OptimizeInnerLoop(node);
     }
   }
 }
 
+//
+// Optimization.
+//
+
 void HLoopOptimization::SimplifyInduction(LoopNode* node) {
   HBasicBlock* header = node->loop_info->GetHeader();
   HBasicBlock* preheader = node->loop_info->GetPreHeader();
@@ -204,13 +244,9 @@ void HLoopOptimization::SimplifyInduction(LoopNode* node) {
   //           for (int i = 0; i < 10; i++, k++) { .... no k .... } return k;
   for (HInstructionIterator it(header->GetPhis()); !it.Done(); it.Advance()) {
     HPhi* phi = it.Current()->AsPhi();
-    iset_->clear();
-    int32_t use_count = 0;
-    if (IsPhiInduction(phi) &&
-        IsOnlyUsedAfterLoop(node->loop_info, phi, /*collect_loop_uses*/ false, &use_count) &&
-        // No uses, or no early-exit with proper replacement.
-        (use_count == 0 ||
-         (!IsEarlyExit(node->loop_info) && TryReplaceWithLastValue(phi, preheader)))) {
+    iset_->clear();  // prepare phi induction
+    if (TrySetPhiInduction(phi, /*restrict_uses*/ true) &&
+        TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ false)) {
       for (HInstruction* i : *iset_) {
         RemoveFromCycle(i);
       }
@@ -256,49 +292,47 @@ void HLoopOptimization::SimplifyBlocks(LoopNode* node) {
   }
 }
 
-bool HLoopOptimization::SimplifyInnerLoop(LoopNode* node) {
+void HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
   HBasicBlock* header = node->loop_info->GetHeader();
   HBasicBlock* preheader = node->loop_info->GetPreHeader();
   // Ensure loop header logic is finite.
-  int64_t tc = 0;
-  if (!induction_range_.IsFinite(node->loop_info, &tc)) {
-    return false;
+  int64_t trip_count = 0;
+  if (!induction_range_.IsFinite(node->loop_info, &trip_count)) {
+    return;
   }
+
   // Ensure there is only a single loop-body (besides the header).
   HBasicBlock* body = nullptr;
   for (HBlocksInLoopIterator it(*node->loop_info); !it.Done(); it.Advance()) {
     if (it.Current() != header) {
       if (body != nullptr) {
-        return false;
+        return;
       }
       body = it.Current();
     }
   }
   // Ensure there is only a single exit point.
   if (header->GetSuccessors().size() != 2) {
-    return false;
+    return;
   }
   HBasicBlock* exit = (header->GetSuccessors()[0] == body)
       ? header->GetSuccessors()[1]
       : header->GetSuccessors()[0];
   // Ensure exit can only be reached by exiting loop.
   if (exit->GetPredecessors().size() != 1) {
-    return false;
+    return;
   }
   // Detect either an empty loop (no side effects other than plain iteration) or
   // a trivial loop (just iterating once). Replace subsequent index uses, if any,
   // with the last value and remove the loop, possibly after unrolling its body.
   HInstruction* phi = header->GetFirstPhi();
-  iset_->clear();
-  int32_t use_count = 0;
-  if (IsEmptyHeader(header)) {
+  iset_->clear();  // prepare phi induction
+  if (TrySetSimpleLoopHeader(header)) {
     bool is_empty = IsEmptyBody(body);
-    if ((is_empty || tc == 1) &&
-        IsOnlyUsedAfterLoop(node->loop_info, phi, /*collect_loop_uses*/ true, &use_count) &&
-        // No uses, or proper replacement.
-        (use_count == 0 || TryReplaceWithLastValue(phi, preheader))) {
+    if ((is_empty || trip_count == 1) &&
+        TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ true)) {
       if (!is_empty) {
-        // Unroll the loop body, which sees initial value of the index.
+        // Unroll the loop-body, which sees initial value of the index.
         phi->ReplaceWith(phi->InputAt(0));
         preheader->MergeInstructionsWith(body);
       }
@@ -308,28 +342,649 @@ bool HLoopOptimization::SimplifyInnerLoop(LoopNode* node) {
       header->RemoveDominatedBlock(exit);
       header->DisconnectAndDelete();
       preheader->AddSuccessor(exit);
-      preheader->AddInstruction(new (graph_->GetArena()) HGoto());  // global allocator
+      preheader->AddInstruction(new (global_allocator_) HGoto());
       preheader->AddDominatedBlock(exit);
       exit->SetDominator(preheader);
       RemoveLoop(node);  // update hierarchy
+      return;
+    }
+  }
+
+  // Vectorize loop, if possible and valid.
+  if (kEnableVectorization) {
+    iset_->clear();  // prepare phi induction
+    if (TrySetSimpleLoopHeader(header) &&
+        CanVectorize(node, body, trip_count) &&
+        TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ true)) {
+      Vectorize(node, body, exit, trip_count);
+      graph_->SetHasSIMD(true);  // flag SIMD usage
+      return;
+    }
+  }
+}
+
+//
+// Loop vectorization. The implementation is based on the book by Aart J.C. Bik:
+// "The Software Vectorization Handbook. Applying Multimedia Extensions for Maximum Performance."
+// Intel Press, June, 2004 (http://www.aartbik.com/).
+//
+
+bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count) {
+  // Reset vector bookkeeping.
+  vector_length_ = 0;
+  vector_refs_->clear();
+  vector_runtime_test_a_ =
+  vector_runtime_test_b_= nullptr;
+
+  // Phis in the loop-body prevent vectorization.
+  if (!block->GetPhis().IsEmpty()) {
+    return false;
+  }
+
+  // Scan the loop-body, starting a right-hand-side tree traversal at each left-hand-side
+  // occurrence, which allows passing down attributes down the use tree.
+  for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+    if (!VectorizeDef(node, it.Current(), /*generate_code*/ false)) {
+      return false;  // failure to vectorize a left-hand-side
+    }
+  }
+
+  // Heuristics. Does vectorization seem profitable?
+  // TODO: refine
+  if (vector_length_ == 0) {
+    return false;  // nothing found
+  } else if (0 < trip_count && trip_count < vector_length_) {
+    return false;  // insufficient iterations
+  }
+
+  // Data dependence analysis. Find each pair of references with same type, where
+  // at least one is a write. Each such pair denotes a possible data dependence.
+  // This analysis exploits the property that differently typed arrays cannot be
+  // aliased, as well as the property that references either point to the same
+  // array or to two completely disjoint arrays, i.e., no partial aliasing.
+  // Other than a few simply heuristics, no detailed subscript analysis is done.
+  for (auto i = vector_refs_->begin(); i != vector_refs_->end(); ++i) {
+    for (auto j = i; ++j != vector_refs_->end(); ) {
+      if (i->type == j->type && (i->lhs || j->lhs)) {
+        // Found same-typed a[i+x] vs. b[i+y], where at least one is a write.
+        HInstruction* a = i->base;
+        HInstruction* b = j->base;
+        HInstruction* x = i->offset;
+        HInstruction* y = j->offset;
+        if (a == b) {
+          // Found a[i+x] vs. a[i+y]. Accept if x == y (loop-independent data dependence).
+          // Conservatively assume a loop-carried data dependence otherwise, and reject.
+          if (x != y) {
+            return false;
+          }
+        } else {
+          // Found a[i+x] vs. b[i+y]. Accept if x == y (at worst loop-independent data dependence).
+          // Conservatively assume a potential loop-carried data dependence otherwise, avoided by
+          // generating an explicit a != b disambiguation runtime test on the two references.
+          if (x != y) {
+            // For now, we reject after one test to avoid excessive overhead.
+            if (vector_runtime_test_a_ != nullptr) {
+              return false;
+            }
+            vector_runtime_test_a_ = a;
+            vector_runtime_test_b_ = b;
+          }
+        }
+      }
+    }
+  }
+
+  // Success!
+  return true;
+}
+
+void HLoopOptimization::Vectorize(LoopNode* node,
+                                  HBasicBlock* block,
+                                  HBasicBlock* exit,
+                                  int64_t trip_count) {
+  Primitive::Type induc_type = Primitive::kPrimInt;
+  HBasicBlock* header = node->loop_info->GetHeader();
+  HBasicBlock* preheader = node->loop_info->GetPreHeader();
+
+  // A cleanup is needed for any unknown trip count or for a known trip count
+  // with remainder iterations after vectorization.
+  bool needs_cleanup = trip_count == 0 || (trip_count % vector_length_) != 0;
+
+  // Adjust vector bookkeeping.
+  iset_->clear();  // prepare phi induction
+  bool is_simple_loop_header = TrySetSimpleLoopHeader(header);  // fills iset_
+  DCHECK(is_simple_loop_header);
+
+  // Generate preheader:
+  // stc = <trip-count>;
+  // vtc = stc - stc % VL;
+  HInstruction* stc = induction_range_.GenerateTripCount(node->loop_info, graph_, preheader);
+  HInstruction* vtc = stc;
+  if (needs_cleanup) {
+    DCHECK(IsPowerOfTwo(vector_length_));
+    HInstruction* rem = Insert(
+        preheader, new (global_allocator_) HAnd(induc_type,
+                                                stc,
+                                                graph_->GetIntConstant(vector_length_ - 1)));
+    vtc = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, rem));
+  }
+
+  // Generate runtime disambiguation test:
+  // vtc = a != b ? vtc : 0;
+  if (vector_runtime_test_a_ != nullptr) {
+    HInstruction* rt = Insert(
+        preheader,
+        new (global_allocator_) HNotEqual(vector_runtime_test_a_, vector_runtime_test_b_));
+    vtc = Insert(preheader,
+                 new (global_allocator_) HSelect(rt, vtc, graph_->GetIntConstant(0), kNoDexPc));
+    needs_cleanup = true;
+  }
+
+  // Generate vector loop:
+  // for (i = 0; i < vtc; i += VL)
+  //    <vectorized-loop-body>
+  vector_mode_ = kVector;
+  GenerateNewLoop(node,
+                  block,
+                  graph_->TransformLoopForVectorization(header, block, exit),
+                  graph_->GetIntConstant(0),
+                  vtc,
+                  graph_->GetIntConstant(vector_length_));
+  HLoopInformation* vloop = vector_header_->GetLoopInformation();
+
+  // Generate cleanup loop, if needed:
+  // for ( ; i < stc; i += 1)
+  //    <loop-body>
+  if (needs_cleanup) {
+    vector_mode_ = kSequential;
+    GenerateNewLoop(node,
+                    block,
+                    graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
+                    vector_phi_,
+                    stc,
+                    graph_->GetIntConstant(1));
+  }
+
+  // Remove the original loop by disconnecting the body block
+  // and removing all instructions from the header.
+  block->DisconnectAndDelete();
+  while (!header->GetFirstInstruction()->IsGoto()) {
+    header->RemoveInstruction(header->GetFirstInstruction());
+  }
+  // Update loop hierarchy: the old header now resides in the
+  // same outer loop as the old preheader.
+  header->SetLoopInformation(preheader->GetLoopInformation());  // outward
+  node->loop_info = vloop;
+}
+
+void HLoopOptimization::GenerateNewLoop(LoopNode* node,
+                                        HBasicBlock* block,
+                                        HBasicBlock* new_preheader,
+                                        HInstruction* lo,
+                                        HInstruction* hi,
+                                        HInstruction* step) {
+  Primitive::Type induc_type = Primitive::kPrimInt;
+  // Prepare new loop.
+  vector_map_->clear();
+  vector_preheader_ = new_preheader,
+  vector_header_ = vector_preheader_->GetSingleSuccessor();
+  vector_body_ = vector_header_->GetSuccessors()[1];
+  vector_phi_ = new (global_allocator_) HPhi(global_allocator_,
+                                             kNoRegNumber,
+                                             0,
+                                             HPhi::ToPhiType(induc_type));
+  // Generate header.
+  // for (i = lo; i < hi; i += step)
+  //    <loop-body>
+  HInstruction* cond = new (global_allocator_) HAboveOrEqual(vector_phi_, hi);
+  vector_header_->AddPhi(vector_phi_);
+  vector_header_->AddInstruction(cond);
+  vector_header_->AddInstruction(new (global_allocator_) HIf(cond));
+  // Suspend check and environment.
+  HInstruction* suspend = vector_header_->GetFirstInstruction();
+  suspend->CopyEnvironmentFromWithLoopPhiAdjustment(
+      node->loop_info->GetSuspendCheck()->GetEnvironment(), vector_header_);
+  // Generate body.
+  for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+    bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
+    DCHECK(vectorized_def);
+  }
+  for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+    auto i = vector_map_->find(it.Current());
+    if (i != vector_map_->end() && !i->second->IsInBlock()) {
+      Insert(vector_body_, i->second);  // lays out in original order
+      if (i->second->NeedsEnvironment()) {
+        i->second->CopyEnvironmentFromWithLoopPhiAdjustment(
+            suspend->GetEnvironment(), vector_header_);
+      }
+    }
+  }
+  // Finalize increment and phi.
+  HInstruction* inc = new (global_allocator_) HAdd(induc_type, vector_phi_, step);
+  vector_phi_->AddInput(lo);
+  vector_phi_->AddInput(Insert(vector_body_, inc));
+}
+
+// TODO: accept reductions at left-hand-side, mixed-type store idioms, etc.
+bool HLoopOptimization::VectorizeDef(LoopNode* node,
+                                     HInstruction* instruction,
+                                     bool generate_code) {
+  // Accept a left-hand-side array base[index] for
+  // (1) supported vector type,
+  // (2) loop-invariant base,
+  // (3) unit stride index,
+  // (4) vectorizable right-hand-side value.
+  uint64_t restrictions = kNone;
+  if (instruction->IsArraySet()) {
+    Primitive::Type type = instruction->AsArraySet()->GetComponentType();
+    HInstruction* base = instruction->InputAt(0);
+    HInstruction* index = instruction->InputAt(1);
+    HInstruction* value = instruction->InputAt(2);
+    HInstruction* offset = nullptr;
+    if (TrySetVectorType(type, &restrictions) &&
+        node->loop_info->IsDefinedOutOfTheLoop(base) &&
+        induction_range_.IsUnitStride(index, &offset) &&
+        VectorizeUse(node, value, generate_code, type, restrictions)) {
+      if (generate_code) {
+        GenerateVecSub(index, offset);
+        GenerateVecMem(instruction, vector_map_->Get(index), vector_map_->Get(value), type);
+      } else {
+        vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ true));
+      }
       return true;
     }
+    return false;
+  }
+  // Branch back okay.
+  if (instruction->IsGoto()) {
+    return true;
+  }
+  // Otherwise accept only expressions with no effects outside the immediate loop-body.
+  // Note that actual uses are inspected during right-hand-side tree traversal.
+  return !IsUsedOutsideLoop(node->loop_info, instruction) && !instruction->DoesAnyWrite();
+}
+
+// TODO: more operations and intrinsics, detect saturation arithmetic, etc.
+bool HLoopOptimization::VectorizeUse(LoopNode* node,
+                                     HInstruction* instruction,
+                                     bool generate_code,
+                                     Primitive::Type type,
+                                     uint64_t restrictions) {
+  // Accept anything for which code has already been generated.
+  if (generate_code) {
+    if (vector_map_->find(instruction) != vector_map_->end()) {
+      return true;
+    }
+  }
+  // Continue the right-hand-side tree traversal, passing in proper
+  // types and vector restrictions along the way. During code generation,
+  // all new nodes are drawn from the global allocator.
+  if (node->loop_info->IsDefinedOutOfTheLoop(instruction)) {
+    // Accept invariant use, using scalar expansion.
+    if (generate_code) {
+      GenerateVecInv(instruction, type);
+    }
+    return true;
+  } else if (instruction->IsArrayGet()) {
+    // Accept a right-hand-side array base[index] for
+    // (1) exact matching vector type,
+    // (2) loop-invariant base,
+    // (3) unit stride index,
+    // (4) vectorizable right-hand-side value.
+    HInstruction* base = instruction->InputAt(0);
+    HInstruction* index = instruction->InputAt(1);
+    HInstruction* offset = nullptr;
+    if (type == instruction->GetType() &&
+        node->loop_info->IsDefinedOutOfTheLoop(base) &&
+        induction_range_.IsUnitStride(index, &offset)) {
+      if (generate_code) {
+        GenerateVecSub(index, offset);
+        GenerateVecMem(instruction, vector_map_->Get(index), nullptr, type);
+      } else {
+        vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ false));
+      }
+      return true;
+    }
+  } else if (instruction->IsTypeConversion()) {
+    // Accept particular type conversions.
+    HTypeConversion* conversion = instruction->AsTypeConversion();
+    HInstruction* opa = conversion->InputAt(0);
+    Primitive::Type from = conversion->GetInputType();
+    Primitive::Type to = conversion->GetResultType();
+    if ((to == Primitive::kPrimByte ||
+         to == Primitive::kPrimChar ||
+         to == Primitive::kPrimShort) && from == Primitive::kPrimInt) {
+      // Accept a "narrowing" type conversion from a "wider" computation for
+      // (1) conversion into final required type,
+      // (2) vectorizable operand,
+      // (3) "wider" operations cannot bring in higher order bits.
+      if (to == type && VectorizeUse(node, opa, generate_code, type, restrictions | kNoHiBits)) {
+        if (generate_code) {
+          if (vector_mode_ == kVector) {
+            vector_map_->Put(instruction, vector_map_->Get(opa));  // operand pass-through
+          } else {
+            GenerateVecOp(instruction, vector_map_->Get(opa), nullptr, type);
+          }
+        }
+        return true;
+      }
+    } else if (to == Primitive::kPrimFloat && from == Primitive::kPrimInt) {
+      DCHECK_EQ(to, type);
+      // Accept int to float conversion for
+      // (1) supported int,
+      // (2) vectorizable operand.
+      if (TrySetVectorType(from, &restrictions) &&
+          VectorizeUse(node, opa, generate_code, from, restrictions)) {
+        if (generate_code) {
+          GenerateVecOp(instruction, vector_map_->Get(opa), nullptr, type);
+        }
+        return true;
+      }
+    }
+    return false;
+  } else if (instruction->IsNeg() || instruction->IsNot() || instruction->IsBooleanNot()) {
+    // Accept unary operator for vectorizable operand.
+    HInstruction* opa = instruction->InputAt(0);
+    if (VectorizeUse(node, opa, generate_code, type, restrictions)) {
+      if (generate_code) {
+        GenerateVecOp(instruction, vector_map_->Get(opa), nullptr, type);
+      }
+      return true;
+    }
+  } else if (instruction->IsAdd() || instruction->IsSub() ||
+             instruction->IsMul() || instruction->IsDiv() ||
+             instruction->IsAnd() || instruction->IsOr()  || instruction->IsXor()) {
+    // Deal with vector restrictions.
+    if ((instruction->IsMul() && HasVectorRestrictions(restrictions, kNoMul)) ||
+        (instruction->IsDiv() && HasVectorRestrictions(restrictions, kNoDiv))) {
+      return false;
+    }
+    // Accept binary operator for vectorizable operands.
+    HInstruction* opa = instruction->InputAt(0);
+    HInstruction* opb = instruction->InputAt(1);
+    if (VectorizeUse(node, opa, generate_code, type, restrictions) &&
+        VectorizeUse(node, opb, generate_code, type, restrictions)) {
+      if (generate_code) {
+        GenerateVecOp(instruction, vector_map_->Get(opa), vector_map_->Get(opb), type);
+      }
+      return true;
+    }
+  } else if (instruction->IsShl() || instruction->IsShr() || instruction->IsUShr()) {
+    // Deal with vector restrictions.
+    if ((HasVectorRestrictions(restrictions, kNoShift)) ||
+        (instruction->IsShr() && HasVectorRestrictions(restrictions, kNoShr))) {
+      return false;  // unsupported instruction
+    } else if ((instruction->IsShr() || instruction->IsUShr()) &&
+               HasVectorRestrictions(restrictions, kNoHiBits)) {
+      return false;  // hibits may impact lobits; TODO: we can do better!
+    }
+    // Accept shift operator for vectorizable/invariant operands.
+    // TODO: accept symbolic, albeit loop invariant shift factors.
+    HInstruction* opa = instruction->InputAt(0);
+    HInstruction* opb = instruction->InputAt(1);
+    if (VectorizeUse(node, opa, generate_code, type, restrictions) && opb->IsIntConstant()) {
+      if (generate_code) {
+        // Make sure shift factor only looks at lower bits, as defined for sequential shifts.
+        // Note that even the narrower SIMD shifts do the right thing after that.
+        int32_t mask = (instruction->GetType() == Primitive::kPrimLong)
+            ? kMaxLongShiftDistance
+            : kMaxIntShiftDistance;
+        HInstruction* s = graph_->GetIntConstant(opb->AsIntConstant()->GetValue() & mask);
+        GenerateVecOp(instruction, vector_map_->Get(opa), s, type);
+      }
+      return true;
+    }
+  } else if (instruction->IsInvokeStaticOrDirect()) {
+    // TODO: coming soon.
+    return false;
   }
   return false;
 }
 
-bool HLoopOptimization::IsPhiInduction(HPhi* phi) {
+bool HLoopOptimization::TrySetVectorType(Primitive::Type type, uint64_t* restrictions) {
+  const InstructionSetFeatures* features = compiler_driver_->GetInstructionSetFeatures();
+  switch (compiler_driver_->GetInstructionSet()) {
+    case kArm:
+    case kThumb2:
+      return false;
+    case kArm64:
+      // Allow vectorization for all ARM devices, because Android assumes that
+      // ARMv8 AArch64 always supports advanced SIMD. For now, only D registers
+      // (64-bit vectors) not Q registers (128-bit vectors).
+      switch (type) {
+        case Primitive::kPrimBoolean:
+        case Primitive::kPrimByte:
+          *restrictions |= kNoDiv;
+          return TrySetVectorLength(8);
+        case Primitive::kPrimChar:
+        case Primitive::kPrimShort:
+          *restrictions |= kNoDiv;
+          return TrySetVectorLength(4);
+        case Primitive::kPrimInt:
+          *restrictions |= kNoDiv;
+          return TrySetVectorLength(2);
+        case Primitive::kPrimFloat:
+          return TrySetVectorLength(2);
+        default:
+          return false;
+      }
+    case kX86:
+    case kX86_64:
+      // Allow vectorization for SSE4-enabled X86 devices only (128-bit vectors).
+      if (features->AsX86InstructionSetFeatures()->HasSSE4_1()) {
+        switch (type) {
+          case Primitive::kPrimBoolean:
+          case Primitive::kPrimByte:
+            *restrictions |= kNoMul | kNoDiv | kNoShift;
+            return TrySetVectorLength(16);
+          case Primitive::kPrimChar:
+          case Primitive::kPrimShort:
+            *restrictions |= kNoDiv;
+            return TrySetVectorLength(8);
+          case Primitive::kPrimInt:
+            *restrictions |= kNoDiv;
+            return TrySetVectorLength(4);
+          case Primitive::kPrimLong:
+            *restrictions |= kNoMul | kNoDiv | kNoShr;
+            return TrySetVectorLength(2);
+          case Primitive::kPrimFloat:
+            return TrySetVectorLength(4);
+          case Primitive::kPrimDouble:
+            return TrySetVectorLength(2);
+          default:
+            break;
+        }  // switch type
+      }
+      return false;
+    case kMips:
+    case kMips64:
+      // TODO: implement MIPS SIMD.
+      return false;
+    default:
+      return false;
+  }  // switch instruction set
+}
+
+bool HLoopOptimization::TrySetVectorLength(uint32_t length) {
+  DCHECK(IsPowerOfTwo(length) && length >= 2u);
+  // First time set?
+  if (vector_length_ == 0) {
+    vector_length_ = length;
+  }
+  // Different types are acceptable within a loop-body, as long as all the corresponding vector
+  // lengths match exactly to obtain a uniform traversal through the vector iteration space
+  // (idiomatic exceptions to this rule can be handled by further unrolling sub-expressions).
+  return vector_length_ == length;
+}
+
+void HLoopOptimization::GenerateVecInv(HInstruction* org, Primitive::Type type) {
+  if (vector_map_->find(org) == vector_map_->end()) {
+    // In scalar code, just use a self pass-through for scalar invariants
+    // (viz. expression remains itself).
+    if (vector_mode_ == kSequential) {
+      vector_map_->Put(org, org);
+      return;
+    }
+    // In vector code, explicit scalar expansion is needed.
+    HInstruction* vector = new (global_allocator_) HVecReplicateScalar(
+        global_allocator_, org, type, vector_length_);
+    vector_map_->Put(org, Insert(vector_preheader_, vector));
+  }
+}
+
+void HLoopOptimization::GenerateVecSub(HInstruction* org, HInstruction* offset) {
+  if (vector_map_->find(org) == vector_map_->end()) {
+    HInstruction* subscript = vector_phi_;
+    if (offset != nullptr) {
+      subscript = new (global_allocator_) HAdd(Primitive::kPrimInt, subscript, offset);
+      if (org->IsPhi()) {
+        Insert(vector_body_, subscript);  // lacks layout placeholder
+      }
+    }
+    vector_map_->Put(org, subscript);
+  }
+}
+
+void HLoopOptimization::GenerateVecMem(HInstruction* org,
+                                       HInstruction* opa,
+                                       HInstruction* opb,
+                                       Primitive::Type type) {
+  HInstruction* vector = nullptr;
+  if (vector_mode_ == kVector) {
+    // Vector store or load.
+    if (opb != nullptr) {
+      vector = new (global_allocator_) HVecStore(
+          global_allocator_, org->InputAt(0), opa, opb, type, vector_length_);
+    } else  {
+      vector = new (global_allocator_) HVecLoad(
+          global_allocator_, org->InputAt(0), opa, type, vector_length_);
+    }
+  } else {
+    // Scalar store or load.
+    DCHECK(vector_mode_ == kSequential);
+    if (opb != nullptr) {
+      vector = new (global_allocator_) HArraySet(org->InputAt(0), opa, opb, type, kNoDexPc);
+    } else  {
+      vector = new (global_allocator_) HArrayGet(org->InputAt(0), opa, type, kNoDexPc);
+    }
+  }
+  vector_map_->Put(org, vector);
+}
+
+#define GENERATE_VEC(x, y) \
+  if (vector_mode_ == kVector) { \
+    vector = (x); \
+  } else { \
+    DCHECK(vector_mode_ == kSequential); \
+    vector = (y); \
+  } \
+  break;
+
+void HLoopOptimization::GenerateVecOp(HInstruction* org,
+                                      HInstruction* opa,
+                                      HInstruction* opb,
+                                      Primitive::Type type) {
+  if (vector_mode_ == kSequential) {
+    // Scalar code follows implicit integral promotion.
+    if (type == Primitive::kPrimBoolean ||
+        type == Primitive::kPrimByte ||
+        type == Primitive::kPrimChar ||
+        type == Primitive::kPrimShort) {
+      type = Primitive::kPrimInt;
+    }
+  }
+  HInstruction* vector = nullptr;
+  switch (org->GetKind()) {
+    case HInstruction::kNeg:
+      DCHECK(opb == nullptr);
+      GENERATE_VEC(
+          new (global_allocator_) HVecNeg(global_allocator_, opa, type, vector_length_),
+          new (global_allocator_) HNeg(type, opa));
+    case HInstruction::kNot:
+      DCHECK(opb == nullptr);
+      GENERATE_VEC(
+          new (global_allocator_) HVecNot(global_allocator_, opa, type, vector_length_),
+          new (global_allocator_) HNot(type, opa));
+    case HInstruction::kBooleanNot:
+      DCHECK(opb == nullptr);
+      GENERATE_VEC(
+          new (global_allocator_) HVecNot(global_allocator_, opa, type, vector_length_),
+          new (global_allocator_) HBooleanNot(opa));
+    case HInstruction::kTypeConversion:
+      DCHECK(opb == nullptr);
+      GENERATE_VEC(
+          new (global_allocator_) HVecCnv(global_allocator_, opa, type, vector_length_),
+          new (global_allocator_) HTypeConversion(type, opa, kNoDexPc));
+    case HInstruction::kAdd:
+      GENERATE_VEC(
+          new (global_allocator_) HVecAdd(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HAdd(type, opa, opb));
+    case HInstruction::kSub:
+      GENERATE_VEC(
+          new (global_allocator_) HVecSub(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HSub(type, opa, opb));
+    case HInstruction::kMul:
+      GENERATE_VEC(
+          new (global_allocator_) HVecMul(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HMul(type, opa, opb));
+    case HInstruction::kDiv:
+      GENERATE_VEC(
+          new (global_allocator_) HVecDiv(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HDiv(type, opa, opb, kNoDexPc));
+    case HInstruction::kAnd:
+      GENERATE_VEC(
+          new (global_allocator_) HVecAnd(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HAnd(type, opa, opb));
+    case HInstruction::kOr:
+      GENERATE_VEC(
+          new (global_allocator_) HVecOr(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HOr(type, opa, opb));
+    case HInstruction::kXor:
+      GENERATE_VEC(
+          new (global_allocator_) HVecXor(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HXor(type, opa, opb));
+    case HInstruction::kShl:
+      GENERATE_VEC(
+          new (global_allocator_) HVecShl(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HShl(type, opa, opb));
+    case HInstruction::kShr:
+      GENERATE_VEC(
+          new (global_allocator_) HVecShr(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HShr(type, opa, opb));
+    case HInstruction::kUShr:
+      GENERATE_VEC(
+          new (global_allocator_) HVecUShr(global_allocator_, opa, opb, type, vector_length_),
+          new (global_allocator_) HUShr(type, opa, opb));
+    case HInstruction::kInvokeStaticOrDirect: {
+      // TODO: coming soon.
+      break;
+    }
+    default:
+      break;
+  }  // switch
+  CHECK(vector != nullptr) << "Unsupported SIMD operator";
+  vector_map_->Put(org, vector);
+}
+
+#undef GENERATE_VEC
+
+//
+// Helpers.
+//
+
+bool HLoopOptimization::TrySetPhiInduction(HPhi* phi, bool restrict_uses) {
+  DCHECK(iset_->empty());
   ArenaSet<HInstruction*>* set = induction_range_.LookupCycle(phi);
   if (set != nullptr) {
-    DCHECK(iset_->empty());
     for (HInstruction* i : *set) {
       // Check that, other than instructions that are no longer in the graph (removed earlier)
-      // each instruction is removable and, other than the phi, uses are contained in the cycle.
+      // each instruction is removable and, when restrict uses are requested, other than for phi,
+      // all uses are contained within the cycle.
       if (!i->IsInBlock()) {
         continue;
       } else if (!i->IsRemovable()) {
         return false;
-      } else if (i != phi) {
+      } else if (i != phi && restrict_uses) {
         for (const HUseListNode<HInstruction*>& use : i->GetUses()) {
           if (set->find(use.GetUser()) == set->end()) {
             return false;
@@ -348,10 +1003,12 @@ bool HLoopOptimization::IsPhiInduction(HPhi* phi) {
 //       c:   Condition(phi, bound)
 //       i:   If(c)
 // TODO: Find a less pattern matching approach?
-bool HLoopOptimization::IsEmptyHeader(HBasicBlock* block) {
+bool HLoopOptimization::TrySetSimpleLoopHeader(HBasicBlock* block) {
   DCHECK(iset_->empty());
   HInstruction* phi = block->GetFirstPhi();
-  if (phi != nullptr && phi->GetNext() == nullptr && IsPhiInduction(phi->AsPhi())) {
+  if (phi != nullptr &&
+      phi->GetNext() == nullptr &&
+      TrySetPhiInduction(phi->AsPhi(), /*restrict_uses*/ false)) {
     HInstruction* s = block->GetFirstInstruction();
     if (s != nullptr && s->IsSuspendCheck()) {
       HInstruction* c = s->GetNext();
@@ -369,14 +1026,24 @@ bool HLoopOptimization::IsEmptyHeader(HBasicBlock* block) {
 }
 
 bool HLoopOptimization::IsEmptyBody(HBasicBlock* block) {
-  if (block->GetFirstPhi() == nullptr) {
-    for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
-      HInstruction* instruction = it.Current();
-      if (!instruction->IsGoto() && iset_->find(instruction) == iset_->end()) {
-        return false;
-      }
+  if (!block->GetPhis().IsEmpty()) {
+    return false;
+  }
+  for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+    HInstruction* instruction = it.Current();
+    if (!instruction->IsGoto() && iset_->find(instruction) == iset_->end()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool HLoopOptimization::IsUsedOutsideLoop(HLoopInformation* loop_info,
+                                          HInstruction* instruction) {
+  for (const HUseListNode<HInstruction*>& use : instruction->GetUses()) {
+    if (use.GetUser()->GetBlock()->GetLoopInformation() != loop_info) {
+      return true;
     }
-    return true;
   }
   return false;
 }
@@ -438,6 +1105,19 @@ bool HLoopOptimization::TryReplaceWithLastValue(HInstruction* instruction, HBasi
   return false;
 }
 
+bool HLoopOptimization::TryAssignLastValue(HLoopInformation* loop_info,
+                                           HInstruction* instruction,
+                                           HBasicBlock* block,
+                                           bool collect_loop_uses) {
+  // Assigning the last value is always successful if there are no uses.
+  // Otherwise, it succeeds in a no early-exit loop by generating the
+  // proper last value assignment.
+  int32_t use_count = 0;
+  return IsOnlyUsedAfterLoop(loop_info, instruction, collect_loop_uses, &use_count) &&
+      (use_count == 0 ||
+       (!IsEarlyExit(loop_info) && TryReplaceWithLastValue(instruction, block)));
+}
+
 void HLoopOptimization::RemoveDeadInstructions(const HInstructionList& list) {
   for (HBackwardInstructionIterator i(list); !i.Done(); i.Advance()) {
     HInstruction* instruction = i.Current();
index 0b798fc..16f7691 100644 (file)
@@ -27,7 +27,8 @@ class CompilerDriver;
 
 /**
  * Loop optimizations. Builds a loop hierarchy and applies optimizations to
- * the detected nested loops, such as removal of dead induction and empty loops.
+ * the detected nested loops, such as removal of dead induction and empty loops
+ * and inner loop vectorization.
  */
 class HLoopOptimization : public HOptimization {
  public:
@@ -50,34 +51,105 @@ class HLoopOptimization : public HOptimization {
           inner(nullptr),
           previous(nullptr),
           next(nullptr) {}
-    HLoopInformation* const loop_info;
+    HLoopInformation* loop_info;
     LoopNode* outer;
     LoopNode* inner;
     LoopNode* previous;
     LoopNode* next;
   };
 
-  void LocalRun();
+  /*
+   * Vectorization restrictions (bit mask).
+   */
+  enum VectorRestrictions {
+    kNone     = 0,   // no restrictions
+    kNoMul    = 1,   // no multiplication
+    kNoDiv    = 2,   // no division
+    kNoShift  = 4,   // no shift
+    kNoShr    = 8,   // no arithmetic shift right
+    kNoHiBits = 16,  // "wider" operations cannot bring in higher order bits
+  };
+
+  /*
+   * Vectorization mode during synthesis
+   * (sequential peeling/cleanup loop or vector loop).
+   */
+  enum VectorMode {
+    kSequential,
+    kVector
+  };
+
+  /*
+   * Representation of a unit-stride array reference.
+   */
+  struct ArrayReference {
+    ArrayReference(HInstruction* b, HInstruction* o, Primitive::Type t, bool l)
+        : base(b), offset(o), type(t), lhs(l) { }
+    bool operator<(const ArrayReference& other) const {
+      return
+          (base < other.base) ||
+          (base == other.base &&
+           (offset < other.offset || (offset == other.offset &&
+                                      (type < other.type ||
+                                       (type == other.type && lhs < other.lhs)))));
+    }
+    HInstruction* base;    // base address
+    HInstruction* offset;  // offset + i
+    Primitive::Type type;  // component type
+    bool lhs;              // def/use
+  };
 
+  // Loop setup and traversal.
+  void LocalRun();
   void AddLoop(HLoopInformation* loop_info);
   void RemoveLoop(LoopNode* node);
-
   void TraverseLoopsInnerToOuter(LoopNode* node);
 
-  // Simplification.
+  // Optimization.
   void SimplifyInduction(LoopNode* node);
   void SimplifyBlocks(LoopNode* node);
-  bool SimplifyInnerLoop(LoopNode* node);
+  void OptimizeInnerLoop(LoopNode* node);
+
+  // Vectorization analysis and synthesis.
+  bool CanVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count);
+  void Vectorize(LoopNode* node, HBasicBlock* block, HBasicBlock* exit, int64_t trip_count);
+  void GenerateNewLoop(LoopNode* node,
+                       HBasicBlock* block,
+                       HBasicBlock* new_preheader,
+                       HInstruction* lo,
+                       HInstruction* hi,
+                       HInstruction* step);
+  bool VectorizeDef(LoopNode* node, HInstruction* instruction, bool generate_code);
+  bool VectorizeUse(LoopNode* node,
+                    HInstruction* instruction,
+                    bool generate_code,
+                    Primitive::Type type,
+                    uint64_t restrictions);
+  bool TrySetVectorType(Primitive::Type type, /*out*/ uint64_t* restrictions);
+  bool TrySetVectorLength(uint32_t length);
+  void GenerateVecInv(HInstruction* org, Primitive::Type type);
+  void GenerateVecSub(HInstruction* org, HInstruction* off);
+  void GenerateVecMem(HInstruction* org,
+                      HInstruction* opa,
+                      HInstruction* opb,
+                      Primitive::Type type);
+  void GenerateVecOp(HInstruction* org, HInstruction* opa, HInstruction* opb, Primitive::Type type);
 
   // Helpers.
-  bool IsPhiInduction(HPhi* phi);
-  bool IsEmptyHeader(HBasicBlock* block);
+  bool TrySetPhiInduction(HPhi* phi, bool restrict_uses);
+  bool TrySetSimpleLoopHeader(HBasicBlock* block);
   bool IsEmptyBody(HBasicBlock* block);
   bool IsOnlyUsedAfterLoop(HLoopInformation* loop_info,
                            HInstruction* instruction,
                            bool collect_loop_uses,
                            /*out*/ int32_t* use_count);
+  bool IsUsedOutsideLoop(HLoopInformation* loop_info,
+                         HInstruction* instruction);
   bool TryReplaceWithLastValue(HInstruction* instruction, HBasicBlock* block);
+  bool TryAssignLastValue(HLoopInformation* loop_info,
+                          HInstruction* instruction,
+                          HBasicBlock* block,
+                          bool collect_loop_uses);
   void RemoveDeadInstructions(const HInstructionList& list);
 
   // Compiler driver (to query ISA features).
@@ -90,6 +162,9 @@ class HLoopOptimization : public HOptimization {
   // through this allocator is immediately released when the loop optimizer is done.
   ArenaAllocator* loop_allocator_;
 
+  // Global heap memory allocator. Used to build HIR.
+  ArenaAllocator* global_allocator_;
+
   // Entries into the loop hierarchy representation. The hierarchy resides
   // in phase-local heap memory.
   LoopNode* top_loop_;
@@ -102,11 +177,33 @@ class HLoopOptimization : public HOptimization {
   // Counter that tracks how many induction cycles have been simplified. Useful
   // to trigger incremental updates of induction variable analysis of outer loops
   // when the induction of inner loops has changed.
-  int32_t induction_simplication_count_;
+  uint32_t induction_simplication_count_;
 
   // Flag that tracks if any simplifications have occurred.
   bool simplified_;
 
+  // Number of "lanes" for selected packed type.
+  uint32_t vector_length_;
+
+  // Set of array references in the vector loop.
+  // Contents reside in phase-local heap memory.
+  ArenaSet<ArrayReference>* vector_refs_;
+
+  // Mapping used during vectorization synthesis for both the scalar peeling/cleanup
+  // loop (simd_ is false) and the actual vector loop (simd_ is true). The data
+  // structure maps original instructions into the new instructions.
+  // Contents reside in phase-local heap memory.
+  ArenaSafeMap<HInstruction*, HInstruction*>* vector_map_;
+
+  // Temporary vectorization bookkeeping.
+  HBasicBlock* vector_preheader_;  // preheader of the new loop
+  HBasicBlock* vector_header_;  // header of the new loop
+  HBasicBlock* vector_body_;  // body of the new loop
+  HInstruction* vector_runtime_test_a_;
+  HInstruction* vector_runtime_test_b_;  // defines a != b runtime test
+  HPhi* vector_phi_;  // the Phi representing the normalized loop index
+  VectorMode vector_mode_;  // selects synthesis mode
+
   friend class LoopOptimizationTest;
 
   DISALLOW_COPY_AND_ASSIGN(HLoopOptimization);
index caada8b..5617e4b 100644 (file)
@@ -2340,6 +2340,66 @@ void HGraph::TransformLoopHeaderForBCE(HBasicBlock* header) {
       new_pre_header, old_pre_header, /* replace_if_back_edge */ false);
 }
 
+HBasicBlock* HGraph::TransformLoopForVectorization(HBasicBlock* header,
+                                                   HBasicBlock* body,
+                                                   HBasicBlock* exit) {
+  DCHECK(header->IsLoopHeader());
+  HLoopInformation* loop = header->GetLoopInformation();
+
+  // Add new loop blocks.
+  HBasicBlock* new_pre_header = new (arena_) HBasicBlock(this, header->GetDexPc());
+  HBasicBlock* new_header = new (arena_) HBasicBlock(this, header->GetDexPc());
+  HBasicBlock* new_body = new (arena_) HBasicBlock(this, header->GetDexPc());
+  AddBlock(new_pre_header);
+  AddBlock(new_header);
+  AddBlock(new_body);
+
+  // Set up control flow.
+  header->ReplaceSuccessor(exit, new_pre_header);
+  new_pre_header->AddSuccessor(new_header);
+  new_header->AddSuccessor(exit);
+  new_header->AddSuccessor(new_body);
+  new_body->AddSuccessor(new_header);
+
+  // Set up dominators.
+  header->ReplaceDominatedBlock(exit, new_pre_header);
+  new_pre_header->SetDominator(header);
+  new_pre_header->dominated_blocks_.push_back(new_header);
+  new_header->SetDominator(new_pre_header);
+  new_header->dominated_blocks_.push_back(new_body);
+  new_body->SetDominator(new_header);
+  new_header->dominated_blocks_.push_back(exit);
+  exit->SetDominator(new_header);
+
+  // Fix reverse post order.
+  size_t index_of_header = IndexOfElement(reverse_post_order_, header);
+  MakeRoomFor(&reverse_post_order_, 2, index_of_header);
+  reverse_post_order_[++index_of_header] = new_pre_header;
+  reverse_post_order_[++index_of_header] = new_header;
+  size_t index_of_body = IndexOfElement(reverse_post_order_, body);
+  MakeRoomFor(&reverse_post_order_, 1, index_of_body - 1);
+  reverse_post_order_[index_of_body] = new_body;
+
+  // Add gotos and suspend check (client must add conditional in header and copy environment).
+  new_pre_header->AddInstruction(new (arena_) HGoto());
+  HSuspendCheck* suspend_check = new (arena_) HSuspendCheck(header->GetDexPc());
+  new_header->AddInstruction(suspend_check);
+  new_body->AddInstruction(new (arena_) HGoto());
+
+  // Update loop information.
+  new_header->AddBackEdge(new_body);
+  new_header->GetLoopInformation()->SetSuspendCheck(suspend_check);
+  new_header->GetLoopInformation()->Populate();
+  new_pre_header->SetLoopInformation(loop->GetPreHeader()->GetLoopInformation());  // outward
+  HLoopInformationOutwardIterator it(*new_header);
+  for (it.Advance(); !it.Done(); it.Advance()) {
+    it.Current()->Add(new_pre_header);
+    it.Current()->Add(new_header);
+    it.Current()->Add(new_body);
+  }
+  return new_pre_header;
+}
+
 static void CheckAgainstUpperBound(ReferenceTypeInfo rti, ReferenceTypeInfo upper_bound_rti)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (rti.IsValid()) {
index 5f5a28c..52a02c2 100644 (file)
@@ -400,6 +400,12 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
   // put deoptimization instructions, etc.
   void TransformLoopHeaderForBCE(HBasicBlock* header);
 
+  // Adds a new loop directly after the loop with the given header and exit.
+  // Returns the new preheader.
+  HBasicBlock* TransformLoopForVectorization(HBasicBlock* header,
+                                             HBasicBlock* body,
+                                             HBasicBlock* exit);
+
   // Removes `block` from the graph. Assumes `block` has been disconnected from
   // other blocks and has no instructions or phis.
   void DeleteDeadEmptyBlock(HBasicBlock* block);
@@ -1363,6 +1369,25 @@ class HLoopInformationOutwardIterator : public ValueObject {
   M(TypeConversion, Instruction)                                        \
   M(UShr, BinaryOperation)                                              \
   M(Xor, BinaryOperation)                                               \
+  M(VecReplicateScalar, VecUnaryOperation)                              \
+  M(VecSetScalars, VecUnaryOperation)                                   \
+  M(VecSumReduce, VecUnaryOperation)                                    \
+  M(VecCnv, VecUnaryOperation)                                          \
+  M(VecNeg, VecUnaryOperation)                                          \
+  M(VecNot, VecUnaryOperation)                                          \
+  M(VecAdd, VecBinaryOperation)                                         \
+  M(VecSub, VecBinaryOperation)                                         \
+  M(VecMul, VecBinaryOperation)                                         \
+  M(VecDiv, VecBinaryOperation)                                         \
+  M(VecAnd, VecBinaryOperation)                                         \
+  M(VecAndNot, VecBinaryOperation)                                      \
+  M(VecOr, VecBinaryOperation)                                          \
+  M(VecXor, VecBinaryOperation)                                         \
+  M(VecShl, VecBinaryOperation)                                         \
+  M(VecShr, VecBinaryOperation)                                         \
+  M(VecUShr, VecBinaryOperation)                                        \
+  M(VecLoad, VecMemoryOperation)                                        \
+  M(VecStore, VecMemoryOperation)                                       \
 
 /*
  * Instructions, shared across several (not all) architectures.
@@ -1424,7 +1449,11 @@ class HLoopInformationOutwardIterator : public ValueObject {
   M(Constant, Instruction)                                              \
   M(UnaryOperation, Instruction)                                        \
   M(BinaryOperation, Instruction)                                       \
-  M(Invoke, Instruction)
+  M(Invoke, Instruction)                                                \
+  M(VecOperation, Instruction)                                          \
+  M(VecUnaryOperation, VecOperation)                                    \
+  M(VecBinaryOperation, VecOperation)                                   \
+  M(VecMemoryOperation, VecOperation)
 
 #define FOR_EACH_INSTRUCTION(M)                                         \
   FOR_EACH_CONCRETE_INSTRUCTION(M)                                      \
@@ -6689,6 +6718,8 @@ class HParallelMove FINAL : public HTemplateInstruction<0> {
 
 }  // namespace art
 
+#include "nodes_vector.h"
+
 #if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
 #include "nodes_shared.h"
 #endif
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
new file mode 100644 (file)
index 0000000..9f9b918
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2017 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_NODES_VECTOR_H_
+#define ART_COMPILER_OPTIMIZING_NODES_VECTOR_H_
+
+// This #include should never be used by compilation, because this header file (nodes_vector.h)
+// is included in the header file nodes.h itself. However it gives editing tools better context.
+#include "nodes.h"
+
+namespace art {
+
+// Memory alignment, represented as an offset relative to a base, where 0 <= offset < base,
+// and base is a power of two. For example, the value Alignment(16, 0) means memory is
+// perfectly aligned at a 16-byte boundary, whereas the value Alignment(16, 4) means
+// memory is always exactly 4 bytes above such a boundary.
+class Alignment {
+ public:
+  Alignment(size_t base, size_t offset) : base_(base), offset_(offset) {
+    DCHECK_LT(offset, base);
+    DCHECK(IsPowerOfTwo(base));
+  }
+
+  // Returns true if memory is "at least" aligned at the given boundary.
+  // Assumes requested base is power of two.
+  bool IsAlignedAt(size_t base) const {
+    DCHECK_NE(0u, base);
+    DCHECK(IsPowerOfTwo(base));
+    return ((offset_ | base_) & (base - 1u)) == 0;
+  }
+
+  std::string ToString() const {
+    return "ALIGN(" + std::to_string(base_) + "," + std::to_string(offset_) + ")";
+  }
+
+ private:
+  size_t base_;
+  size_t offset_;
+};
+
+//
+// Definitions of abstract vector operations in HIR.
+//
+
+// Abstraction of a vector operation, i.e., an operation that performs
+// GetVectorLength() x GetPackedType() operations simultaneously.
+class HVecOperation : public HVariableInputSizeInstruction {
+ public:
+  HVecOperation(ArenaAllocator* arena,
+                Primitive::Type packed_type,
+                SideEffects side_effects,
+                size_t number_of_inputs,
+                size_t vector_length,
+                uint32_t dex_pc)
+      : HVariableInputSizeInstruction(side_effects,
+                                      dex_pc,
+                                      arena,
+                                      number_of_inputs,
+                                      kArenaAllocVectorNode),
+        vector_length_(vector_length) {
+    SetPackedField<TypeField>(packed_type);
+    DCHECK_LT(1u, vector_length);
+  }
+
+  // Returns the number of elements packed in a vector.
+  size_t GetVectorLength() const {
+    return vector_length_;
+  }
+
+  // Returns the number of bytes in a full vector.
+  size_t GetVectorNumberOfBytes() const {
+    return vector_length_ * Primitive::ComponentSize(GetPackedType());
+  }
+
+  // Returns the type of the vector operation: a SIMD operation looks like a FPU location.
+  // TODO: we could introduce SIMD types in HIR.
+  Primitive::Type GetType() const OVERRIDE {
+    return Primitive::kPrimDouble;
+  }
+
+  // Returns the true component type packed in a vector.
+  Primitive::Type GetPackedType() const {
+    return GetPackedField<TypeField>();
+  }
+
+  DECLARE_ABSTRACT_INSTRUCTION(VecOperation);
+
+ private:
+  // Additional packed bits.
+  static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
+  static constexpr size_t kFieldTypeSize =
+      MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+  static constexpr size_t kNumberOfVectorOpPackedBits = kFieldType + kFieldTypeSize;
+  static_assert(kNumberOfVectorOpPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+  using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
+
+  const size_t vector_length_;
+
+  DISALLOW_COPY_AND_ASSIGN(HVecOperation);
+};
+
+// Abstraction of a unary vector operation.
+class HVecUnaryOperation : public HVecOperation {
+ public:
+  HVecUnaryOperation(ArenaAllocator* arena,
+                     Primitive::Type packed_type,
+                     size_t vector_length,
+                     uint32_t dex_pc)
+      : HVecOperation(arena,
+                      packed_type,
+                      SideEffects::None(),
+                      /*number_of_inputs*/ 1,
+                      vector_length,
+                      dex_pc) { }
+  DECLARE_ABSTRACT_INSTRUCTION(VecUnaryOperation);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecUnaryOperation);
+};
+
+// Abstraction of a binary vector operation.
+class HVecBinaryOperation : public HVecOperation {
+ public:
+  HVecBinaryOperation(ArenaAllocator* arena,
+                      Primitive::Type packed_type,
+                      size_t vector_length,
+                      uint32_t dex_pc)
+      : HVecOperation(arena,
+                      packed_type,
+                      SideEffects::None(),
+                      /*number_of_inputs*/ 2,
+                      vector_length,
+                      dex_pc) { }
+  DECLARE_ABSTRACT_INSTRUCTION(VecBinaryOperation);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecBinaryOperation);
+};
+
+// Abstraction of a vector operation that references memory, with an alignment.
+// The Android runtime guarantees at least "component size" alignment for array
+// elements and, thus, vectors.
+class HVecMemoryOperation : public HVecOperation {
+ public:
+  HVecMemoryOperation(ArenaAllocator* arena,
+                      Primitive::Type packed_type,
+                      SideEffects side_effects,
+                      size_t number_of_inputs,
+                      size_t vector_length,
+                      uint32_t dex_pc)
+      : HVecOperation(arena, packed_type, side_effects, number_of_inputs, vector_length, dex_pc),
+        alignment_(Primitive::ComponentSize(packed_type), 0) { }
+
+  void SetAlignment(Alignment alignment) { alignment_ = alignment; }
+
+  Alignment GetAlignment() const { return alignment_; }
+
+  DECLARE_ABSTRACT_INSTRUCTION(VecMemoryOperation);
+
+ private:
+  Alignment alignment_;
+
+  DISALLOW_COPY_AND_ASSIGN(HVecMemoryOperation);
+};
+
+//
+// Definitions of concrete vector operations in HIR.
+//
+
+// Replicates the given scalar into a vector,
+// viz. replicate(x) = [ x, .. , x ].
+class HVecReplicateScalar FINAL : public HVecUnaryOperation {
+ public:
+  HVecReplicateScalar(ArenaAllocator* arena,
+                      HInstruction* scalar,
+                      Primitive::Type packed_type,
+                      size_t vector_length,
+                      uint32_t dex_pc = kNoDexPc)
+      : HVecUnaryOperation(arena, packed_type, vector_length, dex_pc) {
+    SetRawInputAt(0, scalar);
+  }
+  DECLARE_INSTRUCTION(VecReplicateScalar);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecReplicateScalar);
+};
+
+// Assigns the given scalar elements to a vector,
+// viz. set( array(x1, .., xn) ) = [ x1, .. , xn ].
+class HVecSetScalars FINAL : public HVecUnaryOperation {
+  HVecSetScalars(ArenaAllocator* arena,
+                 HInstruction** scalars,  // array
+                 Primitive::Type packed_type,
+                 size_t vector_length,
+                 uint32_t dex_pc = kNoDexPc)
+      : HVecUnaryOperation(arena, packed_type, vector_length, dex_pc) {
+    for (size_t i = 0; i < vector_length; i++) {
+      SetRawInputAt(0, scalars[i]);
+    }
+  }
+  DECLARE_INSTRUCTION(VecSetScalars);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecSetScalars);
+};
+
+// Sum-reduces the given vector into a shorter vector (m < n) or scalar (m = 1),
+// viz. sum-reduce[ x1, .. , xn ] = [ y1, .., ym ], where yi = sum_j x_j.
+class HVecSumReduce FINAL : public HVecUnaryOperation {
+  HVecSumReduce(ArenaAllocator* arena,
+                HInstruction* input,
+                Primitive::Type packed_type,
+                size_t vector_length,
+                uint32_t dex_pc = kNoDexPc)
+      : HVecUnaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(input->IsVecOperation());
+    DCHECK_EQ(input->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, input);
+  }
+
+  // TODO: probably integral promotion
+  Primitive::Type GetType() const OVERRIDE { return GetPackedType(); }
+
+  DECLARE_INSTRUCTION(VecSumReduce);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecSumReduce);
+};
+
+// Converts every component in the vector,
+// viz. cnv[ x1, .. , xn ]  = [ cnv(x1), .. , cnv(xn) ].
+class HVecCnv FINAL : public HVecUnaryOperation {
+ public:
+  HVecCnv(ArenaAllocator* arena,
+          HInstruction* input,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecUnaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(input->IsVecOperation());
+    DCHECK_NE(input->AsVecOperation()->GetPackedType(), packed_type);  // actual convert
+    SetRawInputAt(0, input);
+  }
+
+  Primitive::Type GetInputType() const { return InputAt(0)->AsVecOperation()->GetPackedType(); }
+  Primitive::Type GetResultType() const { return GetPackedType(); }
+
+  DECLARE_INSTRUCTION(VecCnv);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecCnv);
+};
+
+// Negates every component in the vector,
+// viz. neg[ x1, .. , xn ]  = [ -x1, .. , -xn ].
+class HVecNeg FINAL : public HVecUnaryOperation {
+ public:
+  HVecNeg(ArenaAllocator* arena,
+          HInstruction* input,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecUnaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(input->IsVecOperation());
+    DCHECK_EQ(input->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, input);
+  }
+  DECLARE_INSTRUCTION(VecNeg);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecNeg);
+};
+
+// Bitwise- or boolean-nots every component in the vector,
+// viz. not[ x1, .. , xn ]  = [ ~x1, .. , ~xn ], or
+//      not[ x1, .. , xn ]  = [ !x1, .. , !xn ] for boolean.
+class HVecNot FINAL : public HVecUnaryOperation {
+ public:
+  HVecNot(ArenaAllocator* arena,
+          HInstruction* input,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecUnaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(input->IsVecOperation());
+    SetRawInputAt(0, input);
+  }
+  DECLARE_INSTRUCTION(VecNot);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecNot);
+};
+
+// Adds every component in the two vectors,
+// viz. [ x1, .. , xn ] + [ y1, .. , yn ] = [ x1 + y1, .. , xn + yn ].
+class HVecAdd FINAL : public HVecBinaryOperation {
+ public:
+  HVecAdd(ArenaAllocator* arena,
+          HInstruction* left,
+          HInstruction* right,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation() && right->IsVecOperation());
+    DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+    DCHECK_EQ(right->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecAdd);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecAdd);
+};
+
+// Subtracts every component in the two vectors,
+// viz. [ x1, .. , xn ] - [ y1, .. , yn ] = [ x1 - y1, .. , xn - yn ].
+class HVecSub FINAL : public HVecBinaryOperation {
+ public:
+  HVecSub(ArenaAllocator* arena,
+          HInstruction* left,
+          HInstruction* right,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation() && right->IsVecOperation());
+    DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+    DCHECK_EQ(right->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecSub);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecSub);
+};
+
+// Multiplies every component in the two vectors,
+// viz. [ x1, .. , xn ] * [ y1, .. , yn ] = [ x1 * y1, .. , xn * yn ].
+class HVecMul FINAL : public HVecBinaryOperation {
+ public:
+  HVecMul(ArenaAllocator* arena,
+          HInstruction* left,
+          HInstruction* right,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation() && right->IsVecOperation());
+    DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+    DCHECK_EQ(right->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecMul);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecMul);
+};
+
+// Divides every component in the two vectors,
+// viz. [ x1, .. , xn ] / [ y1, .. , yn ] = [ x1 / y1, .. , xn / yn ].
+class HVecDiv FINAL : public HVecBinaryOperation {
+ public:
+  HVecDiv(ArenaAllocator* arena,
+          HInstruction* left,
+          HInstruction* right,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation() && right->IsVecOperation());
+    DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+    DCHECK_EQ(right->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecDiv);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecDiv);
+};
+
+// Bitwise-ands every component in the two vectors,
+// viz. [ x1, .. , xn ] & [ y1, .. , yn ] = [ x1 & y1, .. , xn & yn ].
+class HVecAnd FINAL : public HVecBinaryOperation {
+ public:
+  HVecAnd(ArenaAllocator* arena,
+          HInstruction* left,
+          HInstruction* right,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation() && right->IsVecOperation());
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecAnd);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecAnd);
+};
+
+// Bitwise-and-nots every component in the two vectors,
+// viz. [ x1, .. , xn ] and-not [ y1, .. , yn ] = [ ~x1 & y1, .. , ~xn & yn ].
+class HVecAndNot FINAL : public HVecBinaryOperation {
+ public:
+  HVecAndNot(ArenaAllocator* arena,
+             HInstruction* left,
+             HInstruction* right,
+             Primitive::Type packed_type,
+             size_t vector_length,
+             uint32_t dex_pc = kNoDexPc)
+         : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation() && right->IsVecOperation());
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecAndNot);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecAndNot);
+};
+
+// Bitwise-ors every component in the two vectors,
+// viz. [ x1, .. , xn ] | [ y1, .. , yn ] = [ x1 | y1, .. , xn | yn ].
+class HVecOr FINAL : public HVecBinaryOperation {
+ public:
+  HVecOr(ArenaAllocator* arena,
+         HInstruction* left,
+         HInstruction* right,
+         Primitive::Type packed_type,
+         size_t vector_length,
+         uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation() && right->IsVecOperation());
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecOr);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecOr);
+};
+
+// Bitwise-xors every component in the two vectors,
+// viz. [ x1, .. , xn ] ^ [ y1, .. , yn ] = [ x1 ^ y1, .. , xn ^ yn ].
+class HVecXor FINAL : public HVecBinaryOperation {
+ public:
+  HVecXor(ArenaAllocator* arena,
+          HInstruction* left,
+          HInstruction* right,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation() && right->IsVecOperation());
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecXor);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecXor);
+};
+
+// Logically shifts every component in the vector left by the given distance,
+// viz. [ x1, .. , xn ] << d = [ x1 << d, .. , xn << d ].
+class HVecShl FINAL : public HVecBinaryOperation {
+ public:
+  HVecShl(ArenaAllocator* arena,
+          HInstruction* left,
+          HInstruction* right,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation());
+    DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecShl);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecShl);
+};
+
+// Arithmetically shifts every component in the vector right by the given distance,
+// viz. [ x1, .. , xn ] >> d = [ x1 >> d, .. , xn >> d ].
+class HVecShr FINAL : public HVecBinaryOperation {
+ public:
+  HVecShr(ArenaAllocator* arena,
+          HInstruction* left,
+          HInstruction* right,
+          Primitive::Type packed_type,
+          size_t vector_length,
+          uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation());
+    DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecShr);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecShr);
+};
+
+// Logically shifts every component in the vector right by the given distance,
+// viz. [ x1, .. , xn ] >>> d = [ x1 >>> d, .. , xn >>> d ].
+class HVecUShr FINAL : public HVecBinaryOperation {
+ public:
+  HVecUShr(ArenaAllocator* arena,
+           HInstruction* left,
+           HInstruction* right,
+           Primitive::Type packed_type,
+           size_t vector_length,
+           uint32_t dex_pc = kNoDexPc)
+      : HVecBinaryOperation(arena, packed_type, vector_length, dex_pc) {
+    DCHECK(left->IsVecOperation());
+    DCHECK_EQ(left->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, left);
+    SetRawInputAt(1, right);
+  }
+  DECLARE_INSTRUCTION(VecUShr);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecUShr);
+};
+
+// Loads a vector from memory, viz. load(mem, 1)
+// yield the vector [ mem(1), .. , mem(n) ].
+class HVecLoad FINAL : public HVecMemoryOperation {
+ public:
+  HVecLoad(ArenaAllocator* arena,
+           HInstruction* base,
+           HInstruction* index,
+           Primitive::Type packed_type,
+           size_t vector_length,
+           uint32_t dex_pc = kNoDexPc)
+      : HVecMemoryOperation(arena,
+                            packed_type,
+                            SideEffects::ArrayReadOfType(packed_type),
+                            /*number_of_inputs*/ 2,
+                            vector_length,
+                            dex_pc) {
+    SetRawInputAt(0, base);
+    SetRawInputAt(1, index);
+  }
+  DECLARE_INSTRUCTION(VecLoad);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecLoad);
+};
+
+// Stores a vector to memory, viz. store(m, 1, [x1, .. , xn] )
+// sets mem(1) = x1, .. , mem(n) = xn.
+class HVecStore FINAL : public HVecMemoryOperation {
+ public:
+  HVecStore(ArenaAllocator* arena,
+            HInstruction* base,
+            HInstruction* index,
+            HInstruction* value,
+            Primitive::Type packed_type,
+            size_t vector_length,
+            uint32_t dex_pc = kNoDexPc)
+      : HVecMemoryOperation(arena,
+                            packed_type,
+                            SideEffects::ArrayWriteOfType(packed_type),
+                            /*number_of_inputs*/ 3,
+                            vector_length,
+                            dex_pc) {
+    DCHECK(value->IsVecOperation());
+    DCHECK_EQ(value->AsVecOperation()->GetPackedType(), packed_type);
+    SetRawInputAt(0, base);
+    SetRawInputAt(1, index);
+    SetRawInputAt(2, value);
+  }
+  DECLARE_INSTRUCTION(VecStore);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HVecStore);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_NODES_VECTOR_H_
index 36ee5a9..b538a89 100644 (file)
@@ -470,7 +470,12 @@ bool LiveInterval::SameRegisterKind(Location other) const {
 }
 
 size_t LiveInterval::NumberOfSpillSlotsNeeded() const {
-  // TODO: detect vector operation.
+  // For a SIMD operation, compute the number of needed spill slots.
+  // TODO: do through vector type?
+  HInstruction* definition = GetParent()->GetDefinedBy();
+  if (definition != nullptr && definition->IsVecOperation()) {
+    return definition->AsVecOperation()->GetVectorNumberOfBytes() / kVRegSize;
+  }
   // Return number of needed spill slots based on type.
   return (type_ == Primitive::kPrimLong || type_ == Primitive::kPrimDouble) ? 2 : 1;
 }
index 5307dc0..9c934b7 100644 (file)
@@ -1221,6 +1221,24 @@ void X86Assembler::por(XmmRegister dst, XmmRegister src) {
 }
 
 
+void X86Assembler::pavgb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xE0);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
+void X86Assembler::pavgw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitUint8(0x0F);
+  EmitUint8(0xE3);
+  EmitXmmRegisterOperand(dst, src);
+}
+
+
 void X86Assembler::pcmpeqb(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
index f52cf16..b87522a 100644 (file)
@@ -495,6 +495,9 @@ class X86Assembler FINAL : public Assembler {
   void orps(XmmRegister dst, XmmRegister src);
   void por(XmmRegister dst, XmmRegister src);
 
+  void pavgb(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
+  void pavgw(XmmRegister dst, XmmRegister src);
+
   void pcmpeqb(XmmRegister dst, XmmRegister src);
   void pcmpeqw(XmmRegister dst, XmmRegister src);
   void pcmpeqd(XmmRegister dst, XmmRegister src);
index 2304907..a01eb6d 100644 (file)
@@ -605,6 +605,14 @@ TEST_F(AssemblerX86Test, POr) {
   DriverStr(RepeatFF(&x86::X86Assembler::por, "por %{reg2}, %{reg1}"), "por");
 }
 
+TEST_F(AssemblerX86Test, PAvgB) {
+  DriverStr(RepeatFF(&x86::X86Assembler::pavgb, "pavgb %{reg2}, %{reg1}"), "pavgb");
+}
+
+TEST_F(AssemblerX86Test, PAvgW) {
+  DriverStr(RepeatFF(&x86::X86Assembler::pavgw, "pavgw %{reg2}, %{reg1}"), "pavgw");
+}
+
 TEST_F(AssemblerX86Test, PCmpeqB) {
   DriverStr(RepeatFF(&x86::X86Assembler::pcmpeqb, "pcmpeqb %{reg2}, %{reg1}"), "cmpeqb");
 }
index d20a696..488c75d 100644 (file)
@@ -1427,6 +1427,24 @@ void X86_64Assembler::por(XmmRegister dst, XmmRegister src) {
   EmitXmmRegisterOperand(dst.LowBits(), src);
 }
 
+void X86_64Assembler::pavgb(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xE0);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
+void X86_64Assembler::pavgw(XmmRegister dst, XmmRegister src) {
+  AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+  EmitUint8(0x66);
+  EmitOptionalRex32(dst, src);
+  EmitUint8(0x0F);
+  EmitUint8(0xE3);
+  EmitXmmRegisterOperand(dst.LowBits(), src);
+}
+
 void X86_64Assembler::pcmpeqb(XmmRegister dst, XmmRegister src) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   EmitUint8(0x66);
index 08e17e8..fc2b117 100644 (file)
@@ -523,6 +523,9 @@ class X86_64Assembler FINAL : public Assembler {
   void orps(XmmRegister dst, XmmRegister src);
   void por(XmmRegister dst, XmmRegister src);
 
+  void pavgb(XmmRegister dst, XmmRegister src);  // no addr variant (for now)
+  void pavgw(XmmRegister dst, XmmRegister src);
+
   void pcmpeqb(XmmRegister dst, XmmRegister src);
   void pcmpeqw(XmmRegister dst, XmmRegister src);
   void pcmpeqd(XmmRegister dst, XmmRegister src);
index 20062fd..4adf210 100644 (file)
@@ -1293,6 +1293,14 @@ TEST_F(AssemblerX86_64Test, Por) {
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::por, "por %{reg2}, %{reg1}"), "por");
 }
 
+TEST_F(AssemblerX86_64Test, Pavgb) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::pavgb, "pavgb %{reg2}, %{reg1}"), "pavgb");
+}
+
+TEST_F(AssemblerX86_64Test, Pavgw) {
+  DriverStr(RepeatFF(&x86_64::X86_64Assembler::pavgw, "pavgw %{reg2}, %{reg1}"), "pavgw");
+}
+
 TEST_F(AssemblerX86_64Test, PCmpeqb) {
   DriverStr(RepeatFF(&x86_64::X86_64Assembler::pcmpeqb, "pcmpeqb %{reg2}, %{reg1}"), "pcmpeqb");
 }
index 4228503..4502626 100644 (file)
@@ -763,5 +763,138 @@ ClassData* Collections::CreateClassData(
   return class_data;
 }
 
+static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
+  return 0;
+}
+
+static uint32_t HeaderSize(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
+  // Size is in elements, so there is only one header.
+  return 1;
+}
+
+// The description of each dex file section type.
+struct FileSectionDescriptor {
+ public:
+  std::string name;
+  uint16_t type;
+  // A function that when applied to a collection object, gives the size of the section.
+  std::function<uint32_t(const dex_ir::Collections&)> size_fn;
+  // A function that when applied to a collection object, gives the offset of the section.
+  std::function<uint32_t(const dex_ir::Collections&)> offset_fn;
+};
+
+static const std::vector<FileSectionDescriptor> kFileSectionDescriptors = {
+  {
+    "Header",
+    DexFile::kDexTypeHeaderItem,
+    &HeaderSize,
+    &HeaderOffset,
+  }, {
+    "StringId",
+    DexFile::kDexTypeStringIdItem,
+    &dex_ir::Collections::StringIdsSize,
+    &dex_ir::Collections::StringIdsOffset
+  }, {
+    "TypeId",
+    DexFile::kDexTypeTypeIdItem,
+    &dex_ir::Collections::TypeIdsSize,
+    &dex_ir::Collections::TypeIdsOffset
+  }, {
+    "ProtoId",
+    DexFile::kDexTypeProtoIdItem,
+    &dex_ir::Collections::ProtoIdsSize,
+    &dex_ir::Collections::ProtoIdsOffset
+  }, {
+    "FieldId",
+    DexFile::kDexTypeFieldIdItem,
+    &dex_ir::Collections::FieldIdsSize,
+    &dex_ir::Collections::FieldIdsOffset
+  }, {
+    "MethodId",
+    DexFile::kDexTypeMethodIdItem,
+    &dex_ir::Collections::MethodIdsSize,
+    &dex_ir::Collections::MethodIdsOffset
+  }, {
+    "ClassDef",
+    DexFile::kDexTypeClassDefItem,
+    &dex_ir::Collections::ClassDefsSize,
+    &dex_ir::Collections::ClassDefsOffset
+  }, {
+    "StringData",
+    DexFile::kDexTypeStringDataItem,
+    &dex_ir::Collections::StringDatasSize,
+    &dex_ir::Collections::StringDatasOffset
+  }, {
+    "TypeList",
+    DexFile::kDexTypeTypeList,
+    &dex_ir::Collections::TypeListsSize,
+    &dex_ir::Collections::TypeListsOffset
+  }, {
+    "EncArr",
+    DexFile::kDexTypeEncodedArrayItem,
+    &dex_ir::Collections::EncodedArrayItemsSize,
+    &dex_ir::Collections::EncodedArrayItemsOffset
+  }, {
+    "Annotation",
+    DexFile::kDexTypeAnnotationItem,
+    &dex_ir::Collections::AnnotationItemsSize,
+    &dex_ir::Collections::AnnotationItemsOffset
+  }, {
+    "AnnoSet",
+    DexFile::kDexTypeAnnotationSetItem,
+    &dex_ir::Collections::AnnotationSetItemsSize,
+    &dex_ir::Collections::AnnotationSetItemsOffset
+  }, {
+    "AnnoSetRL",
+    DexFile::kDexTypeAnnotationSetRefList,
+    &dex_ir::Collections::AnnotationSetRefListsSize,
+    &dex_ir::Collections::AnnotationSetRefListsOffset
+  }, {
+    "AnnoDir",
+    DexFile::kDexTypeAnnotationsDirectoryItem,
+    &dex_ir::Collections::AnnotationsDirectoryItemsSize,
+    &dex_ir::Collections::AnnotationsDirectoryItemsOffset
+  }, {
+    "DebugInfo",
+    DexFile::kDexTypeDebugInfoItem,
+    &dex_ir::Collections::DebugInfoItemsSize,
+    &dex_ir::Collections::DebugInfoItemsOffset
+  }, {
+    "CodeItem",
+    DexFile::kDexTypeCodeItem,
+    &dex_ir::Collections::CodeItemsSize,
+    &dex_ir::Collections::CodeItemsOffset
+  }, {
+    "ClassData",
+    DexFile::kDexTypeClassDataItem,
+    &dex_ir::Collections::ClassDatasSize,
+    &dex_ir::Collections::ClassDatasOffset
+  }
+};
+
+std::vector<dex_ir::DexFileSection> GetSortedDexFileSections(dex_ir::Header* header,
+                                                             dex_ir::SortDirection direction) {
+  const dex_ir::Collections& collections = header->GetCollections();
+  std::vector<dex_ir::DexFileSection> sorted_sections;
+  // Build the table that will map from offset to color
+  for (const FileSectionDescriptor& s : kFileSectionDescriptors) {
+    sorted_sections.push_back(dex_ir::DexFileSection(s.name,
+                                                     s.type,
+                                                     s.size_fn(collections),
+                                                     s.offset_fn(collections)));
+  }
+  // Sort by offset.
+  std::sort(sorted_sections.begin(),
+            sorted_sections.end(),
+            [=](dex_ir::DexFileSection& a, dex_ir::DexFileSection& b) {
+              if (direction == SortDirection::kSortDescending) {
+                return a.offset > b.offset;
+              } else {
+                return a.offset < b.offset;
+              }
+            });
+  return sorted_sections;
+}
+
 }  // namespace dex_ir
 }  // namespace art
index 78ddde8..cad0395 100644 (file)
@@ -1104,6 +1104,28 @@ class MapItem : public Item {
   DISALLOW_COPY_AND_ASSIGN(MapItem);
 };
 
+// Interface for building a vector of file sections for use by other clients.
+struct DexFileSection {
+ public:
+  DexFileSection(const std::string& name, uint16_t type, uint32_t size, uint32_t offset)
+      : name(name), type(type), size(size), offset(offset) { }
+  std::string name;
+  // The type (DexFile::MapItemType).
+  uint16_t type;
+  // The size (in elements, not bytes).
+  uint32_t size;
+  // The byte offset from the start of the file.
+  uint32_t offset;
+};
+
+enum class SortDirection {
+  kSortAscending,
+  kSortDescending
+};
+
+std::vector<DexFileSection> GetSortedDexFileSections(dex_ir::Header* header,
+                                                     SortDirection direction);
+
 }  // namespace dex_ir
 }  // namespace art
 
index 452f51b..829e9fe 100644 (file)
@@ -41,142 +41,13 @@ static std::string MultidexName(const std::string& prefix,
   return prefix + ((dex_file_index > 0) ? std::to_string(dex_file_index + 1) : "") + suffix;
 }
 
-struct FileSection {
- public:
-  std::string name_;
-  uint16_t type_;
-  std::function<uint32_t(const dex_ir::Collections&)> size_fn_;
-  std::function<uint32_t(const dex_ir::Collections&)> offset_fn_;
-};
-
-static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
-  return 0;
-}
-
-static uint32_t HeaderSize(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
-  // Size is in elements, so there is only one header.
-  return 1;
-}
-
-static const std::vector<FileSection> kFileSections = {
-  {
-    "Header",
-    DexFile::kDexTypeHeaderItem,
-    &HeaderSize,
-    &HeaderOffset,
-  }, {
-    "StringId",
-    DexFile::kDexTypeStringIdItem,
-    &dex_ir::Collections::StringIdsSize,
-    &dex_ir::Collections::StringIdsOffset
-  }, {
-    "TypeId",
-    DexFile::kDexTypeTypeIdItem,
-    &dex_ir::Collections::TypeIdsSize,
-    &dex_ir::Collections::TypeIdsOffset
-  }, {
-    "ProtoId",
-    DexFile::kDexTypeProtoIdItem,
-    &dex_ir::Collections::ProtoIdsSize,
-    &dex_ir::Collections::ProtoIdsOffset
-  }, {
-    "FieldId",
-    DexFile::kDexTypeFieldIdItem,
-    &dex_ir::Collections::FieldIdsSize,
-    &dex_ir::Collections::FieldIdsOffset
-  }, {
-    "MethodId",
-    DexFile::kDexTypeMethodIdItem,
-    &dex_ir::Collections::MethodIdsSize,
-    &dex_ir::Collections::MethodIdsOffset
-  }, {
-    "ClassDef",
-    DexFile::kDexTypeClassDefItem,
-    &dex_ir::Collections::ClassDefsSize,
-    &dex_ir::Collections::ClassDefsOffset
-  }, {
-    "StringData",
-    DexFile::kDexTypeStringDataItem,
-    &dex_ir::Collections::StringDatasSize,
-    &dex_ir::Collections::StringDatasOffset
-  }, {
-    "TypeList",
-    DexFile::kDexTypeTypeList,
-    &dex_ir::Collections::TypeListsSize,
-    &dex_ir::Collections::TypeListsOffset
-  }, {
-    "EncArr",
-    DexFile::kDexTypeEncodedArrayItem,
-    &dex_ir::Collections::EncodedArrayItemsSize,
-    &dex_ir::Collections::EncodedArrayItemsOffset
-  }, {
-    "Annotation",
-    DexFile::kDexTypeAnnotationItem,
-    &dex_ir::Collections::AnnotationItemsSize,
-    &dex_ir::Collections::AnnotationItemsOffset
-  }, {
-    "AnnoSet",
-    DexFile::kDexTypeAnnotationSetItem,
-    &dex_ir::Collections::AnnotationSetItemsSize,
-    &dex_ir::Collections::AnnotationSetItemsOffset
-  }, {
-    "AnnoSetRL",
-    DexFile::kDexTypeAnnotationSetRefList,
-    &dex_ir::Collections::AnnotationSetRefListsSize,
-    &dex_ir::Collections::AnnotationSetRefListsOffset
-  }, {
-    "AnnoDir",
-    DexFile::kDexTypeAnnotationsDirectoryItem,
-    &dex_ir::Collections::AnnotationsDirectoryItemsSize,
-    &dex_ir::Collections::AnnotationsDirectoryItemsOffset
-  }, {
-    "DebugInfo",
-    DexFile::kDexTypeDebugInfoItem,
-    &dex_ir::Collections::DebugInfoItemsSize,
-    &dex_ir::Collections::DebugInfoItemsOffset
-  }, {
-    "CodeItem",
-    DexFile::kDexTypeCodeItem,
-    &dex_ir::Collections::CodeItemsSize,
-    &dex_ir::Collections::CodeItemsOffset
-  }, {
-    "ClassData",
-    DexFile::kDexTypeClassDataItem,
-    &dex_ir::Collections::ClassDatasSize,
-    &dex_ir::Collections::ClassDatasOffset
-  }
-};
-
-static constexpr bool kSortAscending = false;
-static constexpr bool kSortDescending = true;
-
-static std::vector<const FileSection*> GetSortedSections(
-    const dex_ir::Collections& collections,
-    bool sort_descending) {
-  std::vector<const FileSection*> sorted_sections;
-  // Build the table that will map from offset to color
-  for (const FileSection& s : kFileSections) {
-    sorted_sections.push_back(&s);
-  }
-  // Sort by offset.
-  std::sort(sorted_sections.begin(),
-            sorted_sections.end(),
-            [&](const FileSection* a, const FileSection* b) {
-              if (sort_descending) {
-                return a->offset_fn_(collections) > b->offset_fn_(collections);
-              } else {
-                return a->offset_fn_(collections) < b->offset_fn_(collections);
-              }
-            });
-  return sorted_sections;
-}
-
 class Dumper {
  public:
   // Colors are based on the type of the section in MapList.
-  explicit Dumper(const dex_ir::Collections& collections)
-      : collections_(collections), out_file_(nullptr),
-        sorted_sections_(GetSortedSections(collections, kSortDescending)) { }
+  explicit Dumper(dex_ir::Header* header)
+      : out_file_(nullptr),
+        sorted_sections_(
+            dex_ir::GetSortedDexFileSections(header, dex_ir::SortDirection::kSortDescending)) { }
 
   bool OpenAndPrintHeader(size_t dex_index) {
     // Open the file and emit the gnuplot prologue.
@@ -191,12 +62,13 @@ class Dumper {
     fprintf(out_file_, "set ylabel \"ClassDef index\"\n");
     fprintf(out_file_, "set xtics rotate out (");
     bool printed_one = false;
-    for (const FileSection& s : kFileSections) {
-      if (s.size_fn_(collections_) > 0) {
+
+    for (const dex_ir::DexFileSection& s : sorted_sections_) {
+      if (s.size > 0) {
         if (printed_one) {
           fprintf(out_file_, ", ");
         }
-        fprintf(out_file_, "\"%s\" %d", s.name_.c_str(), s.offset_fn_(collections_) / kPageSize);
+        fprintf(out_file_, "\"%s\" %d", s.name.c_str(), s.offset / kPageSize);
         printed_one = true;
       }
     }
@@ -209,9 +81,9 @@ class Dumper {
   int GetColor(uint32_t offset) const {
     // The dread linear search to find the right section for the reference.
     uint16_t section = 0;
-    for (const FileSection* file_section : sorted_sections_) {
-      if (file_section->offset_fn_(collections_) < offset) {
-        section = file_section->type_;
+    for (const dex_ir::DexFileSection& file_section : sorted_sections_) {
+      if (file_section.offset < offset) {
+        section = file_section.type;
         break;
       }
     }
@@ -362,9 +234,8 @@ class Dumper {
     { DexFile::kDexTypeAnnotationsDirectoryItem, 16 }
   };
 
-  const dex_ir::Collections& collections_;
   FILE* out_file_;
-  std::vector<const FileSection*> sorted_sections_;
+  std::vector<dex_ir::DexFileSection> sorted_sections_;
 
   DISALLOW_COPY_AND_ASSIGN(Dumper);
 };
@@ -377,7 +248,7 @@ void VisualizeDexLayout(dex_ir::Header* header,
                         const DexFile* dex_file,
                         size_t dex_file_index,
                         ProfileCompilationInfo* profile_info) {
-  std::unique_ptr<Dumper> dumper(new Dumper(header->GetCollections()));
+  std::unique_ptr<Dumper> dumper(new Dumper(header));
   if (!dumper->OpenAndPrintHeader(dex_file_index)) {
     fprintf(stderr, "Could not open output file.\n");
     return;
@@ -433,13 +304,12 @@ void VisualizeDexLayout(dex_ir::Header* header,
 }
 
 static uint32_t FindNextByteAfterSection(dex_ir::Header* header,
-                                         const dex_ir::Collections& collections,
-                                         std::vector<const FileSection*>& sorted_sections,
+                                         const std::vector<dex_ir::DexFileSection>& sorted_sections,
                                          size_t section_index) {
   for (size_t i = section_index + 1; i < sorted_sections.size(); ++i) {
-    const FileSection* section = sorted_sections[i];
-    if (section->size_fn_(collections) != 0) {
-      return section->offset_fn_(collections);
+    const dex_ir::DexFileSection& section = sorted_sections.at(i);
+    if (section.size != 0) {
+      return section.offset;
     }
   }
   return header->FileSize();
@@ -454,19 +324,22 @@ void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index) {
           MultidexName("classes", dex_file_index, ".dex").c_str(),
           header->FileSize());
   fprintf(stdout, "section      offset    items    bytes    pages pct\n");
-  const dex_ir::Collections& collections = header->GetCollections();
-  std::vector<const FileSection*> sorted_sections(GetSortedSections(collections, kSortAscending));
+  std::vector<dex_ir::DexFileSection> sorted_sections =
+      GetSortedDexFileSections(header, dex_ir::SortDirection::kSortAscending);
   for (size_t i = 0; i < sorted_sections.size(); ++i) {
-    const FileSection* file_section = sorted_sections[i];
-    const char* name = file_section->name_.c_str();
-    uint32_t offset = file_section->offset_fn_(collections);
-    uint32_t items = file_section->size_fn_(collections);
+    const dex_ir::DexFileSection& file_section = sorted_sections[i];
     uint32_t bytes = 0;
-    if (items > 0) {
-      bytes = FindNextByteAfterSection(header, collections, sorted_sections, i) - offset;
+    if (file_section.size > 0) {
+      bytes = FindNextByteAfterSection(header, sorted_sections, i) - file_section.offset;
     }
-    fprintf(stdout, "%-10s %8d %8d %8d %8d %%%02d\n", name, offset, items, bytes,
-            (bytes + kPageSize - 1) / kPageSize, 100 * bytes / header->FileSize());
+    fprintf(stdout,
+            "%-10s %8d %8d %8d %8d %%%02d\n",
+            file_section.name.c_str(),
+            file_section.offset,
+            file_section.size,
+            bytes,
+            RoundUp(bytes, kPageSize) / kPageSize,
+            100 * bytes / header->FileSize());
   }
   fprintf(stdout, "\n");
 }
index 4ef48ff..4f70404 100644 (file)
@@ -41,7 +41,7 @@ static const char kDexFileLayoutInputDex[] =
     "AAAAdQEAAAAQAAABAAAAjAEAAA==";
 
 static const char kDexFileLayoutInputProfile[] =
-    "cHJvADAwNAABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA==";
+    "cHJvADAwNQABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA==";
 
 // Dex file with catch handler unreferenced by try blocks.
 // Constructed by building a dex file with try/catch blocks and hex editing.
index 77ed3c6..f5c3ad2 100644 (file)
@@ -1101,6 +1101,22 @@ DISASSEMBLER_ENTRY(cmp,
           opcode1 = opcode_tmp.c_str();
         }
         break;
+      case 0xE0:
+      case 0xE3:
+        if (prefix[2] == 0x66) {
+          src_reg_file = dst_reg_file = SSE;
+          prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
+        } else {
+          src_reg_file = dst_reg_file = MMX;
+        }
+        switch (*instr) {
+          case 0xE0: opcode1 = "pavgb"; break;
+          case 0xE3: opcode1 = "pavgw"; break;
+        }
+        prefix[2] = 0;
+        has_modrm = true;
+        load = true;
+        break;
       case 0xEB:
         if (prefix[2] == 0x66) {
           src_reg_file = dst_reg_file = SSE;
index 5b5d2ef..26622f0 100644 (file)
@@ -2379,9 +2379,8 @@ ENTRY \name
     ret
 .Lnot_marked_rb_\name:
     // Check if the top two bits are one, if this is the case it is a forwarding address.
-    mvn wIP0, wIP0
-    cmp wzr, wIP0, lsr #30
-    beq .Lret_forwarding_address\name
+    tst   wIP0, wIP0, lsl #1
+    bmi   .Lret_forwarding_address\name
 .Lslow_rb_\name:
     /*
      * Allocate 44 stack slots * 8 = 352 bytes:
@@ -2452,10 +2451,9 @@ ENTRY \name
     DECREASE_FRAME 352
     ret
 .Lret_forwarding_address\name:
-    mvn wIP0, wIP0
     // Shift left by the forwarding address shift. This clears out the state bits since they are
     // in the top 2 bits of the lock word.
-    lsl \wreg, wIP0, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT
+    lsl   \wreg, wIP0, #LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT
     ret
 END \name
 .endm
index 13dbc3f..24ea275 100644 (file)
@@ -37,8 +37,9 @@
 namespace art {
 
 const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' };
-// Last profile version: fix the order of dex files in the profile.
-const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '4', '\0' };
+// Last profile version: fix profman merges. Update profile version to force
+// regeneration of possibly faulty profiles.
+const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '5', '\0' };
 
 static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX;
 
index d81c13d..6ca951f 100644 (file)
@@ -17,6 +17,8 @@
 #include "dalvik_system_VMRuntime.h"
 
 #ifdef ART_TARGET_ANDROID
+#include <sys/time.h>
+#include <sys/resource.h>
 extern "C" void android_set_application_target_sdk_version(uint32_t version);
 #endif
 #include <limits.h>
@@ -625,6 +627,23 @@ static jboolean VMRuntime_didPruneDalvikCache(JNIEnv* env ATTRIBUTE_UNUSED,
   return Runtime::Current()->GetPrunedDalvikCache() ? JNI_TRUE : JNI_FALSE;
 }
 
+static void VMRuntime_setSystemDaemonThreadPriority(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                    jclass klass ATTRIBUTE_UNUSED) {
+#ifdef ART_TARGET_ANDROID
+  Thread* self = Thread::Current();
+  DCHECK(self != nullptr);
+  pid_t tid = self->GetTid();
+  // We use a priority lower than the default for the system daemon threads (eg HeapTaskDaemon) to
+  // avoid jank due to CPU contentions between GC and other UI-related threads. b/36631902.
+  // We may use a native priority that doesn't have a corresponding java.lang.Thread-level priority.
+  static constexpr int kSystemDaemonNiceValue = 4;  // priority 124
+  if (setpriority(PRIO_PROCESS, tid, kSystemDaemonNiceValue) != 0) {
+    PLOG(INFO) << *self << " setpriority(PRIO_PROCESS, " << tid << ", "
+               << kSystemDaemonNiceValue << ") failed";
+  }
+#endif
+}
+
 static JNINativeMethod gMethods[] = {
   FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
   NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -662,6 +681,7 @@ static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMRuntime, isBootClassPathOnDisk, "(Ljava/lang/String;)Z"),
   NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, didPruneDalvikCache, "()Z"),
+  NATIVE_METHOD(VMRuntime, setSystemDaemonThreadPriority, "()V"),
 };
 
 void register_dalvik_system_VMRuntime(JNIEnv* env) {
index 907ab02..e94c4e6 100644 (file)
@@ -56,6 +56,8 @@
 #include "mirror/object_reference.h"
 #include "mirror/object-inl.h"
 #include "mirror/reference.h"
+#include "primitive.h"
+#include "reflection.h"
 #include "runtime.h"
 #include "runtime_callbacks.h"
 #include "ScopedLocalRef.h"
@@ -79,9 +81,9 @@ static std::unique_ptr<const art::DexFile> MakeSingleDexFile(art::Thread* self,
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
   // Make the mmap
   std::string error_msg;
+  art::ArraySlice<const unsigned char> final_data(final_dex_data, final_len);
   std::unique_ptr<art::MemMap> map(Redefiner::MoveDataToMemMap(orig_location,
-                                                               final_len,
-                                                               final_dex_data,
+                                                               final_data,
                                                                &error_msg));
   if (map.get() == nullptr) {
     LOG(WARNING) << "Unable to allocate mmap for redefined dex file! Error was: " << error_msg;
@@ -231,14 +233,22 @@ struct ClassCallback : public art::ClassLoadCallback {
       }
 
       // Allocate the byte array to store the dex file bytes in.
-      art::Handle<art::mirror::ByteArray> arr(hs.NewHandle(
-          art::mirror::ByteArray::AllocateAndFill(
-              self,
-              reinterpret_cast<const signed char*>(post_no_redefine_dex_data),
-              post_no_redefine_len)));
+      art::MutableHandle<art::mirror::Object> arr(hs.NewHandle<art::mirror::Object>(nullptr));
+      if (post_no_redefine_dex_data == dex_file_copy->Begin() && name != "java/lang/Long") {
+        // we didn't have any non-retransformable agents. We can just cache a pointer to the
+        // initial_dex_file. It will be kept live by the class_loader.
+        jlong dex_ptr = reinterpret_cast<uintptr_t>(&initial_dex_file);
+        art::JValue val;
+        val.SetJ(dex_ptr);
+        arr.Assign(art::BoxPrimitive(art::Primitive::kPrimLong, val));
+      } else {
+        arr.Assign(art::mirror::ByteArray::AllocateAndFill(
+            self,
+            reinterpret_cast<const signed char*>(post_no_redefine_dex_data),
+            post_no_redefine_len));
+      }
       if (arr.IsNull()) {
-        LOG(WARNING) << "Unable to allocate byte array for initial dex-file bytes. Aborting "
-                     << "transformation";
+        LOG(WARNING) << "Unable to allocate memory for initial dex-file. Aborting transformation";
         self->AssertPendingOOMException();
         return;
       }
index de8d8fe..153692b 100644 (file)
 
 #include "ti_class_definition.h"
 
+#include "base/array_slice.h"
 #include "dex_file.h"
+#include "fixed_up_dex_file.h"
 #include "handle_scope-inl.h"
 #include "handle.h"
 #include "mirror/class-inl.h"
 #include "mirror/object-inl.h"
+#include "reflection.h"
 #include "thread.h"
 
 namespace openjdkjvmti {
@@ -43,15 +46,130 @@ namespace openjdkjvmti {
 bool ArtClassDefinition::IsModified() const {
   // RedefineClasses calls always are 'modified' since they need to change the original_dex_file of
   // the class.
-  if (redefined) {
+  if (redefined_) {
     return true;
   }
   // Check if the dex file we want to set is the same as the current one.
   // Unfortunately we need to do this check even if no modifications have been done since it could
   // be that agents were removed in the mean-time so we still have a different dex file. The dex
   // checksum means this is likely to be fairly fast.
-  return static_cast<jint>(original_dex_file.size()) != dex_len ||
-      memcmp(&original_dex_file.At(0), dex_data.get(), dex_len) != 0;
+  return static_cast<jint>(original_dex_file_.size()) != dex_len_ ||
+      memcmp(&original_dex_file_.At(0), dex_data_.get(), dex_len_) != 0;
+}
+
+jvmtiError ArtClassDefinition::InitCommon(ArtJvmTiEnv* env, jclass klass) {
+  JNIEnv* jni_env = GetJniEnv(env);
+  if (jni_env == nullptr) {
+    return ERR(INTERNAL);
+  }
+  art::ScopedObjectAccess soa(jni_env);
+  art::ObjPtr<art::mirror::Class> m_klass(soa.Decode<art::mirror::Class>(klass));
+  if (m_klass.IsNull()) {
+    return ERR(INVALID_CLASS);
+  }
+  klass_ = klass;
+  loader_ = soa.AddLocalReference<jobject>(m_klass->GetClassLoader());
+  std::string descriptor_store;
+  std::string descriptor(m_klass->GetDescriptor(&descriptor_store));
+  name_ = descriptor.substr(1, descriptor.size() - 2);
+  // Android doesn't really have protection domains.
+  protection_domain_ = nullptr;
+  return OK;
+}
+
+// Gets the data surrounding the given class.
+static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
+                                                art::Handle<art::mirror::Class> klass,
+                                                /*out*/jint* dex_data_len,
+                                                /*out*/unsigned char** dex_data)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  art::StackHandleScope<3> hs(art::Thread::Current());
+  art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
+  const art::DexFile* dex_file = nullptr;
+  if (!ext.IsNull()) {
+    art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile()));
+    if (!orig_dex.IsNull()) {
+      if (orig_dex->IsArrayInstance()) {
+        DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
+        art::Handle<art::mirror::ByteArray> orig_dex_bytes(
+            hs.NewHandle(art::down_cast<art::mirror::ByteArray*>(orig_dex->AsArray())));
+        *dex_data_len = static_cast<jint>(orig_dex_bytes->GetLength());
+        return CopyDataIntoJvmtiBuffer(
+            env,
+            reinterpret_cast<const unsigned char*>(orig_dex_bytes->GetData()),
+            *dex_data_len,
+            /*out*/dex_data);
+      } else if (orig_dex->IsDexCache()) {
+        dex_file = orig_dex->AsDexCache()->GetDexFile();
+      } else {
+        DCHECK_EQ(orig_dex->GetClass()->GetPrimitiveType(), art::Primitive::kPrimLong);
+        art::ObjPtr<art::mirror::Class> prim_long_class(
+            art::Runtime::Current()->GetClassLinker()->GetClassRoot(
+                art::ClassLinker::kPrimitiveLong));
+        art::JValue val;
+        if (!art::UnboxPrimitiveForResult(orig_dex.Get(), prim_long_class, &val)) {
+          // This should never happen.
+          return ERR(INTERNAL);
+        }
+        dex_file = reinterpret_cast<const art::DexFile*>(static_cast<uintptr_t>(val.GetJ()));
+      }
+    }
+  }
+  if (dex_file == nullptr) {
+    dex_file = &klass->GetDexFile();
+  }
+  std::unique_ptr<FixedUpDexFile> fixed_dex_file(FixedUpDexFile::Create(*dex_file));
+  *dex_data_len = static_cast<jint>(fixed_dex_file->Size());
+  return CopyDataIntoJvmtiBuffer(env,
+                                 fixed_dex_file->Begin(),
+                                 fixed_dex_file->Size(),
+                                 /*out*/dex_data);
+}
+
+jvmtiError ArtClassDefinition::Init(ArtJvmTiEnv* env, jclass klass) {
+  jvmtiError res = InitCommon(env, klass);
+  if (res != OK) {
+    return res;
+  }
+  unsigned char* new_data = nullptr;
+  art::Thread* self = art::Thread::Current();
+  art::ScopedObjectAccess soa(self);
+  art::StackHandleScope<1> hs(self);
+  art::Handle<art::mirror::Class> m_klass(hs.NewHandle(self->DecodeJObject(klass)->AsClass()));
+  res = GetDexDataForRetransformation(env, m_klass, &dex_len_, &new_data);
+  if (res != OK) {
+    return res;
+  }
+  dex_data_ = MakeJvmtiUniquePtr(env, new_data);
+  if (m_klass->GetExtData() == nullptr || m_klass->GetExtData()->GetOriginalDexFile() == nullptr) {
+    // We have never redefined class this yet. Keep track of what the (de-quickened) dex file looks
+    // like so we can tell if anything has changed. Really we would like to just always do the
+    // 'else' block but the fact that we de-quickened stuff screws us over.
+    unsigned char* original_data_memory = nullptr;
+    res = CopyDataIntoJvmtiBuffer(env, dex_data_.get(), dex_len_, &original_data_memory);
+    original_dex_file_memory_ = MakeJvmtiUniquePtr(env, original_data_memory);
+    original_dex_file_ = art::ArraySlice<const unsigned char>(original_data_memory, dex_len_);
+  } else {
+    // We know that we have been redefined at least once (there is an original_dex_file set in
+    // the class) so we can just use the current dex file directly.
+    const art::DexFile& dex_file = m_klass->GetDexFile();
+    original_dex_file_ = art::ArraySlice<const unsigned char>(dex_file.Begin(), dex_file.Size());
+  }
+  return res;
+}
+
+jvmtiError ArtClassDefinition::Init(ArtJvmTiEnv* env, const jvmtiClassDefinition& def) {
+  jvmtiError res = InitCommon(env, def.klass);
+  if (res != OK) {
+    return res;
+  }
+  unsigned char* new_data = nullptr;
+  original_dex_file_ = art::ArraySlice<const unsigned char>(def.class_bytes, def.class_byte_count);
+  redefined_ = true;
+  dex_len_ = def.class_byte_count;
+  res = CopyDataIntoJvmtiBuffer(env, def.class_bytes, def.class_byte_count, /*out*/ &new_data);
+  dex_data_ = MakeJvmtiUniquePtr(env, new_data);
+  return res;
 }
 
 }  // namespace openjdkjvmti
index 7a2e922..43d0c3f 100644 (file)
@@ -39,46 +39,39 @@ namespace openjdkjvmti {
 // A struct that stores data needed for redefining/transforming classes. This structure should only
 // even be accessed from a single thread and must not survive past the completion of the
 // redefinition/retransformation function that created it.
-struct ArtClassDefinition {
+class ArtClassDefinition {
  public:
-  jclass klass;
-  jobject loader;
-  std::string name;
-  jobject protection_domain;
-  jint dex_len;
-  JvmtiUniquePtr<unsigned char> dex_data;
-  JvmtiUniquePtr<unsigned char> original_dex_file_memory;
-  art::ArraySlice<const unsigned char> original_dex_file;
-
   ArtClassDefinition()
-      : klass(nullptr),
-        loader(nullptr),
-        name(),
-        protection_domain(nullptr),
-        dex_len(0),
-        dex_data(nullptr),
-        original_dex_file_memory(nullptr),
-        original_dex_file(),
-        redefined(false) {}
+      : klass_(nullptr),
+        loader_(nullptr),
+        name_(),
+        protection_domain_(nullptr),
+        dex_len_(0),
+        dex_data_(nullptr),
+        original_dex_file_memory_(nullptr),
+        original_dex_file_(),
+        redefined_(false) {}
+
+  jvmtiError Init(ArtJvmTiEnv* env, jclass klass);
+  jvmtiError Init(ArtJvmTiEnv* env, const jvmtiClassDefinition& def);
 
   ArtClassDefinition(ArtClassDefinition&& o) = default;
+  ArtClassDefinition& operator=(ArtClassDefinition&& o) = default;
 
   void SetNewDexData(ArtJvmTiEnv* env, jint new_dex_len, unsigned char* new_dex_data) {
+    DCHECK(IsInitialized());
     if (new_dex_data == nullptr) {
       return;
-    } else if (new_dex_data != dex_data.get() || new_dex_len != dex_len) {
-      dex_len = new_dex_len;
-      dex_data = MakeJvmtiUniquePtr(env, new_dex_data);
+    } else if (new_dex_data != dex_data_.get() || new_dex_len != dex_len_) {
+      dex_len_ = new_dex_len;
+      dex_data_ = MakeJvmtiUniquePtr(env, new_dex_data);
     }
   }
 
-  void SetRedefined() {
-    redefined = true;
-  }
-
   art::ArraySlice<const unsigned char> GetNewOriginalDexFile() const {
-    if (redefined) {
-      return original_dex_file;
+    DCHECK(IsInitialized());
+    if (redefined_) {
+      return original_dex_file_;
     } else {
       return art::ArraySlice<const unsigned char>();
     }
@@ -86,8 +79,49 @@ struct ArtClassDefinition {
 
   bool IsModified() const;
 
+  bool IsInitialized() const {
+    return klass_ != nullptr;
+  }
+
+  jclass GetClass() const {
+    DCHECK(IsInitialized());
+    return klass_;
+  }
+
+  jobject GetLoader() const {
+    DCHECK(IsInitialized());
+    return loader_;
+  }
+
+  const std::string& GetName() const {
+    DCHECK(IsInitialized());
+    return name_;
+  }
+
+  jobject GetProtectionDomain() const {
+    DCHECK(IsInitialized());
+    return protection_domain_;
+  }
+
+  art::ArraySlice<const unsigned char> GetDexData() const {
+    DCHECK(IsInitialized());
+    return art::ArraySlice<const unsigned char>(dex_data_.get(), dex_len_);
+  }
+
  private:
-  bool redefined;
+  jvmtiError InitCommon(ArtJvmTiEnv* env, jclass klass);
+
+  jclass klass_;
+  jobject loader_;
+  std::string name_;
+  jobject protection_domain_;
+  jint dex_len_;
+  JvmtiUniquePtr<unsigned char> dex_data_;
+  JvmtiUniquePtr<unsigned char> original_dex_file_memory_;
+  art::ArraySlice<const unsigned char> original_dex_file_;
+  bool redefined_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArtClassDefinition);
 };
 
 }  // namespace openjdkjvmti
index c2495e3..49d9aca 100644 (file)
@@ -882,12 +882,17 @@ class FollowReferencesHelper FINAL {
     void AddRoot(art::mirror::Object* root_obj, const art::RootInfo& info)
         REQUIRES_SHARED(art::Locks::mutator_lock_)
         REQUIRES(!*tag_table_->GetAllowDisallowLock()) {
+      if (stop_reports_) {
+        return;
+      }
+      bool add_to_worklist = ReportRoot(root_obj, info);
       // We use visited_ to mark roots already so we do not need another set.
       if (visited_->find(root_obj) == visited_->end()) {
         visited_->insert(root_obj);
-        worklist_->push_back(root_obj);
+        if (add_to_worklist) {
+          worklist_->push_back(root_obj);
+        }
       }
-      ReportRoot(root_obj, info);
     }
 
     // Remove NO_THREAD_SAFETY_ANALYSIS once ASSERT_CAPABILITY works correctly.
@@ -993,7 +998,7 @@ class FollowReferencesHelper FINAL {
       UNREACHABLE();
     }
 
-    void ReportRoot(art::mirror::Object* root_obj, const art::RootInfo& info)
+    bool ReportRoot(art::mirror::Object* root_obj, const art::RootInfo& info)
         REQUIRES_SHARED(art::Locks::mutator_lock_)
         REQUIRES(!*tag_table_->GetAllowDisallowLock()) {
       jvmtiHeapReferenceInfo ref_info;
@@ -1002,6 +1007,7 @@ class FollowReferencesHelper FINAL {
       if ((result & JVMTI_VISIT_ABORT) != 0) {
         stop_reports_ = true;
       }
+      return (result & JVMTI_VISIT_OBJECTS) != 0;
     }
 
    private:
index 95a1b00..6e0d9a0 100644 (file)
@@ -261,13 +261,12 @@ jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class>
 
 // Moves dex data to an anonymous, read-only mmap'd region.
 std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location,
-                                                         jint data_len,
-                                                         const unsigned char* dex_data,
+                                                         art::ArraySlice<const unsigned char> data,
                                                          std::string* error_msg) {
   std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous(
       StringPrintf("%s-transformed", original_location.c_str()).c_str(),
       nullptr,
-      data_len,
+      data.size(),
       PROT_READ|PROT_WRITE,
       /*low_4gb*/false,
       /*reuse*/false,
@@ -275,7 +274,7 @@ std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& orig
   if (map == nullptr) {
     return map;
   }
-  memcpy(map->Begin(), dex_data, data_len);
+  memcpy(map->Begin(), &data.At(0), data.size());
   // Make the dex files mmap read only. This matches how other DexFiles are mmaped and prevents
   // programs from corrupting it.
   map->Protect(PROT_READ);
@@ -344,13 +343,7 @@ jvmtiError Redefiner::RedefineClasses(ArtJvmTiEnv* env,
     memcpy(class_bytes_copy, definitions[i].class_bytes, definitions[i].class_byte_count);
 
     ArtClassDefinition def;
-    def.dex_len = definitions[i].class_byte_count;
-    def.dex_data = MakeJvmtiUniquePtr(env, class_bytes_copy);
-    // We are definitely modified.
-    def.SetRedefined();
-    def.original_dex_file = art::ArraySlice<const unsigned char>(definitions[i].class_bytes,
-                                                                 definitions[i].class_byte_count);
-    res = Transformer::FillInTransformationData(env, definitions[i].klass, &def);
+    res = def.Init(env, definitions[i]);
     if (res != OK) {
       return res;
     }
@@ -399,25 +392,24 @@ jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env,
 jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition& def) {
   std::string original_dex_location;
   jvmtiError ret = OK;
-  if ((ret = GetClassLocation(env, def.klass, &original_dex_location))) {
+  if ((ret = GetClassLocation(env, def.GetClass(), &original_dex_location))) {
     *error_msg_ = "Unable to get original dex file location!";
     return ret;
   }
   char* generic_ptr_unused = nullptr;
   char* signature_ptr = nullptr;
-  if ((ret = env->GetClassSignature(def.klass, &signature_ptr, &generic_ptr_unused)) != OK) {
+  if ((ret = env->GetClassSignature(def.GetClass(), &signature_ptr, &generic_ptr_unused)) != OK) {
     *error_msg_ = "Unable to get class signature!";
     return ret;
   }
   JvmtiUniquePtr<char> generic_unique_ptr(MakeJvmtiUniquePtr(env, generic_ptr_unused));
   JvmtiUniquePtr<char> signature_unique_ptr(MakeJvmtiUniquePtr(env, signature_ptr));
   std::unique_ptr<art::MemMap> map(MoveDataToMemMap(original_dex_location,
-                                                    def.dex_len,
-                                                    def.dex_data.get(),
+                                                    def.GetDexData(),
                                                     error_msg_));
   std::ostringstream os;
   if (map.get() == nullptr) {
-    os << "Failed to create anonymous mmap for modified dex file of class " << def.name
+    os << "Failed to create anonymous mmap for modified dex file of class " << def.GetName()
        << "in dex file " << original_dex_location << " because: " << *error_msg_;
     *error_msg_ = os.str();
     return ERR(OUT_OF_MEMORY);
@@ -434,13 +426,13 @@ jvmtiError Redefiner::AddRedefinition(ArtJvmTiEnv* env, const ArtClassDefinition
                                                                   /*verify_checksum*/true,
                                                                   error_msg_));
   if (dex_file.get() == nullptr) {
-    os << "Unable to load modified dex file for " << def.name << ": " << *error_msg_;
+    os << "Unable to load modified dex file for " << def.GetName() << ": " << *error_msg_;
     *error_msg_ = os.str();
     return ERR(INVALID_CLASS_FORMAT);
   }
   redefinitions_.push_back(
       Redefiner::ClassRedefinition(this,
-                                   def.klass,
+                                   def.GetClass(),
                                    dex_file.release(),
                                    signature_ptr,
                                    def.GetNewOriginalDexFile()));
index 6c09d46..809a681 100644 (file)
@@ -99,8 +99,7 @@ class Redefiner {
   static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
 
   static std::unique_ptr<art::MemMap> MoveDataToMemMap(const std::string& original_location,
-                                                       jint data_len,
-                                                       const unsigned char* dex_data,
+                                                       art::ArraySlice<const unsigned char> data,
                                                        std::string* error_msg);
 
  private:
index a1883b4..15d8dd0 100644 (file)
 #include "dex_file.h"
 #include "dex_file_types.h"
 #include "events-inl.h"
-#include "fixed_up_dex_file.h"
 #include "gc_root-inl.h"
 #include "globals.h"
 #include "jni_env_ext-inl.h"
+#include "jvalue.h"
 #include "jvmti.h"
 #include "linear_alloc.h"
 #include "mem_map.h"
@@ -70,17 +70,18 @@ jvmtiError Transformer::RetransformClassesDirect(
   for (ArtClassDefinition& def : *definitions) {
     jint new_len = -1;
     unsigned char* new_data = nullptr;
+    art::ArraySlice<const unsigned char> dex_data = def.GetDexData();
     event_handler->DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
         self,
         GetJniEnv(env),
-        def.klass,
-        def.loader,
-        def.name.c_str(),
-        def.protection_domain,
-        def.dex_len,
-        static_cast<const unsigned char*>(def.dex_data.get()),
-        &new_len,
-        &new_data);
+        def.GetClass(),
+        def.GetLoader(),
+        def.GetName().c_str(),
+        def.GetProtectionDomain(),
+        static_cast<jint>(dex_data.size()),
+        &dex_data.At(0),
+        /*out*/&new_len,
+        /*out*/&new_data);
     def.SetNewDexData(env, new_len, new_data);
   }
   return OK;
@@ -118,7 +119,7 @@ jvmtiError Transformer::RetransformClasses(ArtJvmTiEnv* env,
       return ERR(UNMODIFIABLE_CLASS);
     }
     ArtClassDefinition def;
-    res = FillInTransformationData(env, classes[i], &def);
+    res = def.Init(env, classes[i]);
     if (res != OK) {
       return res;
     }
@@ -147,103 +148,4 @@ jvmtiError GetClassLocation(ArtJvmTiEnv* env, jclass klass, /*out*/std::string*
   return OK;
 }
 
-jvmtiError Transformer::GetDexDataForRetransformation(ArtJvmTiEnv* env,
-                                                      art::Handle<art::mirror::Class> klass,
-                                                      /*out*/jint* dex_data_len,
-                                                      /*out*/unsigned char** dex_data) {
-  art::StackHandleScope<3> hs(art::Thread::Current());
-  art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->GetExtData()));
-  const art::DexFile* dex_file = nullptr;
-  if (!ext.IsNull()) {
-    art::Handle<art::mirror::Object> orig_dex(hs.NewHandle(ext->GetOriginalDexFile()));
-    if (!orig_dex.IsNull()) {
-      if (orig_dex->IsArrayInstance()) {
-        DCHECK(orig_dex->GetClass()->GetComponentType()->IsPrimitiveByte());
-        art::Handle<art::mirror::ByteArray> orig_dex_bytes(
-            hs.NewHandle(art::down_cast<art::mirror::ByteArray*>(orig_dex->AsArray())));
-        *dex_data_len = static_cast<jint>(orig_dex_bytes->GetLength());
-        return CopyDataIntoJvmtiBuffer(
-            env,
-            reinterpret_cast<const unsigned char*>(orig_dex_bytes->GetData()),
-            *dex_data_len,
-            /*out*/dex_data);
-      } else {
-        DCHECK(orig_dex->IsDexCache());
-        dex_file = orig_dex->AsDexCache()->GetDexFile();
-        *dex_data_len = static_cast<jint>(dex_file->Size());
-      }
-    }
-  }
-  if (dex_file == nullptr) {
-    dex_file = &klass->GetDexFile();
-    *dex_data_len = static_cast<jint>(dex_file->Size());
-  }
-  std::unique_ptr<FixedUpDexFile> fixed_dex_file(FixedUpDexFile::Create(*dex_file));
-  return CopyDataIntoJvmtiBuffer(env,
-                                 fixed_dex_file->Begin(),
-                                 fixed_dex_file->Size(),
-                                 /*out*/dex_data);
-}
-
-// TODO Move this function somewhere more appropriate.
-// Gets the data surrounding the given class.
-// TODO Make this less magical.
-jvmtiError Transformer::FillInTransformationData(ArtJvmTiEnv* env,
-                                                 jclass klass,
-                                                 ArtClassDefinition* def) {
-  JNIEnv* jni_env = GetJniEnv(env);
-  if (jni_env == nullptr) {
-    // TODO Different error might be better?
-    return ERR(INTERNAL);
-  }
-  art::ScopedObjectAccess soa(jni_env);
-  art::StackHandleScope<3> hs(art::Thread::Current());
-  art::Handle<art::mirror::Class> hs_klass(hs.NewHandle(soa.Decode<art::mirror::Class>(klass)));
-  if (hs_klass.IsNull()) {
-    return ERR(INVALID_CLASS);
-  }
-  def->klass = klass;
-  def->loader = soa.AddLocalReference<jobject>(hs_klass->GetClassLoader());
-  std::string descriptor_store;
-  std::string descriptor(hs_klass->GetDescriptor(&descriptor_store));
-  def->name = descriptor.substr(1, descriptor.size() - 2);
-  // TODO is this always null?
-  def->protection_domain = nullptr;
-  if (def->dex_data.get() == nullptr) {
-    unsigned char* new_data;
-    jvmtiError res = GetDexDataForRetransformation(env, hs_klass, &def->dex_len, &new_data);
-    if (res == OK) {
-      def->dex_data = MakeJvmtiUniquePtr(env, new_data);
-      // TODO This whole thing is a bit of a mess.
-      // We need to keep track of what the runtime should think an unmodified dex file is since
-      // we need to be able to tell if anything changes. This might be different then the currently
-      // loaded dex file since we need to un-quicken stuff.
-      if (hs_klass->GetExtData() == nullptr ||
-          hs_klass->GetExtData()->GetOriginalDexFile() == nullptr) {
-        // We have never redefined this yet. Keep track of what the (de-quickened) dex file looks
-        // like so we can tell if anything has changed.
-        // Really we would like to just always do the 'else' block but the fact that we de-quickened
-        // stuff screws us over.
-        unsigned char* original_data_memory = nullptr;
-        res = env->Allocate(def->dex_len, &original_data_memory);
-        if (res != OK) {
-          return res;
-        }
-        memcpy(original_data_memory, new_data, def->dex_len);
-        def->original_dex_file_memory = MakeJvmtiUniquePtr(env, original_data_memory);
-        def->original_dex_file = art::ArraySlice<const unsigned char>(original_data_memory,
-                                                                      def->dex_len);
-      } else {
-        // We know that we have been redefined at least once (there is an original_dex_file set in
-        // the class) so we can just use the current dex file directly.
-        def->original_dex_file = art::ArraySlice<const unsigned char>(
-            hs_klass->GetDexFile().Begin(), hs_klass->GetDexFile().Size());
-      }
-    } else {
-      return res;
-    }
-  }
-  return OK;
-}
-
 }  // namespace openjdkjvmti
index c6a36e8..ba40e04 100644 (file)
@@ -61,18 +61,6 @@ class Transformer {
                                        jint class_count,
                                        const jclass* classes,
                                        /*out*/std::string* error_msg);
-
-  // Gets the data surrounding the given class.
-  static jvmtiError FillInTransformationData(ArtJvmTiEnv* env,
-                                             jclass klass,
-                                             ArtClassDefinition* def);
-
- private:
-  static jvmtiError GetDexDataForRetransformation(ArtJvmTiEnv* env,
-                                                  art::Handle<art::mirror::Class> klass,
-                                                  /*out*/jint* dex_data_length,
-                                                  /*out*/unsigned char** dex_data)
-      REQUIRES_SHARED(art::Locks::mutator_lock_);
 };
 
 }  // namespace openjdkjvmti
index caed369..8d72fe8 100644 (file)
@@ -736,7 +736,7 @@ void ThreadList::SuspendAllInternal(Thread* self,
         // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning.
         if ((errno != EAGAIN) && (errno != EINTR)) {
           if (errno == ETIMEDOUT) {
-            LOG(kIsDebugBuild ? ::android::base::FATAL : ::android::base::ERROR)
+            LOG(::android::base::FATAL)
                 << "Timed out waiting for threads to suspend, waited for "
                 << PrettyDuration(NanoTime() - start_time);
           } else {
diff --git a/test/640-checker-boolean-simd/expected.txt b/test/640-checker-boolean-simd/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
diff --git a/test/640-checker-boolean-simd/info.txt b/test/640-checker-boolean-simd/info.txt
new file mode 100644 (file)
index 0000000..c9c6d5e
--- /dev/null
@@ -0,0 +1 @@
+Functional tests on SIMD vectorization.
diff --git a/test/640-checker-boolean-simd/src/Main.java b/test/640-checker-boolean-simd/src/Main.java
new file mode 100644 (file)
index 0000000..f8239fa
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Functional tests for SIMD vectorization.
+ */
+public class Main {
+
+  static boolean[] a;
+
+  //
+  // Arithmetic operations.
+  //
+
+  /// CHECK-START: void Main.and(boolean) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.and(boolean) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAnd   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void and(boolean x) {
+    for (int i = 0; i < 128; i++)
+      a[i] &= x;  // NOTE: bitwise and, not the common &&
+  }
+
+  /// CHECK-START: void Main.or(boolean) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.or(boolean) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecOr    loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void or(boolean x) {
+    for (int i = 0; i < 128; i++)
+      a[i] |= x;  // NOTE: bitwise or, not the common ||
+  }
+
+  /// CHECK-START: void Main.xor(boolean) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.xor(boolean) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecXor   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void xor(boolean x) {
+    for (int i = 0; i < 128; i++)
+      a[i] ^= x;  // NOTE: bitwise xor
+  }
+
+  /// CHECK-START: void Main.not() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.not() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNot   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void not() {
+    for (int i = 0; i < 128; i++)
+      a[i] = !a[i];
+  }
+
+  //
+  // Test Driver.
+  //
+
+  public static void main(String[] args) {
+    // Set up.
+    a = new boolean[128];
+    for (int i = 0; i < 128; i++) {
+      a[i] = (i & 1) == 0;
+    }
+    // Arithmetic operations.
+    and(true);
+    for (int i = 0; i < 128; i++) {
+      expectEquals((i & 1) == 0, a[i], "and-true");
+    }
+    xor(true);
+    for (int i = 0; i < 128; i++) {
+      expectEquals((i & 1) != 0, a[i], "xor-true");
+    }
+    xor(false);
+    for (int i = 0; i < 128; i++) {
+      expectEquals((i & 1) != 0, a[i], "xor-false");
+    }
+    not();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((i & 1) == 0, a[i], "not");
+    }
+    or(true);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(true, a[i], "or-true");
+    }
+    and(false);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(false, a[i], "and-false");
+    }
+    or(false);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(false, a[i], "or-false");
+    }
+    // Done.
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(boolean expected, boolean result, String action) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result + " for " + action);
+    }
+  }
+}
diff --git a/test/640-checker-byte-simd/expected.txt b/test/640-checker-byte-simd/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
diff --git a/test/640-checker-byte-simd/info.txt b/test/640-checker-byte-simd/info.txt
new file mode 100644 (file)
index 0000000..c9c6d5e
--- /dev/null
@@ -0,0 +1 @@
+Functional tests on SIMD vectorization.
diff --git a/test/640-checker-byte-simd/src/Main.java b/test/640-checker-byte-simd/src/Main.java
new file mode 100644 (file)
index 0000000..0f7452b
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Functional tests for SIMD vectorization.
+ */
+public class Main {
+
+  static byte[] a;
+
+  //
+  // Arithmetic operations.
+  //
+
+  /// CHECK-START: void Main.add(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAdd   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void add(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] += x;
+  }
+
+  /// CHECK-START: void Main.sub(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecSub   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void sub(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] -= x;
+  }
+
+  /// CHECK-START: void Main.mul(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecMul   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void mul(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] *= x;
+  }
+
+  /// CHECK-START: void Main.div(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.div(int) loop_optimization (after)
+  //
+  //  Not supported on any architecture.
+  //
+  static void div(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] /= x;
+  }
+
+  /// CHECK-START: void Main.neg() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNeg   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void neg() {
+    for (int i = 0; i < 128; i++)
+      a[i] = (byte) -a[i];
+  }
+
+  /// CHECK-START: void Main.not() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.not() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNot   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void not() {
+    for (int i = 0; i < 128; i++)
+      a[i] = (byte) ~a[i];
+  }
+
+  /// CHECK-START: void Main.shl4() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecShl   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void shl4() {
+    for (int i = 0; i < 128; i++)
+      a[i] <<= 4;
+  }
+
+  /// CHECK-START: void Main.sar2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void sar2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>= 2;
+  }
+
+  /// CHECK-START: void Main.shr2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void shr2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 2;
+  }
+
+  //
+  // Shift sanity.
+  //
+
+  static void sar31() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>= 31;
+  }
+
+  static void shr31() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 31;
+  }
+
+  static void shr32() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 32;  // 0, since & 31
+  }
+
+  static void shr33() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 33;  // 1, since & 31
+  }
+
+  //
+  // Loop bounds.
+  //
+
+  static void bounds() {
+    for (int i = 1; i < 127; i++)
+      a[i] += 11;
+  }
+
+  //
+  // Test Driver.
+  //
+
+  public static void main(String[] args) {
+    // Set up.
+    a = new byte[128];
+    for (int i = 0; i < 128; i++) {
+      a[i] = (byte) i;
+    }
+    // Arithmetic operations.
+    add(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte)(i + 2), a[i], "add");
+    }
+    sub(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "sub");
+    }
+    mul(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte)(i + i), a[i], "mul");
+    }
+    div(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(((byte)(i + i)) >> 1, a[i], "div");
+      a[i] = (byte) i;  // undo arithmetic wrap-around effects
+    }
+    neg();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(-i, a[i], "neg");
+    }
+    // Loop bounds.
+    bounds();
+    expectEquals(0, a[0], "bounds0");
+    for (int i = 1; i < 127; i++) {
+      expectEquals(11 - i, a[i], "bounds");
+    }
+    expectEquals(-127, a[127], "bounds127");
+    // Shifts.
+    for (int i = 0; i < 128; i++) {
+      a[i] = (byte) 0xff;
+    }
+    shl4();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte) 0xf0, a[i], "shl4");
+    }
+    sar2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte) 0xfc, a[i], "sar2");
+    }
+    shr2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte) 0xff, a[i], "shr2");  // sic!
+    }
+    sar31();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte) 0xff, a[i], "sar31");
+    }
+    shr31();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0x01, a[i], "shr31");
+      a[i] = (byte) 0x12;  // reset
+    }
+    shr32();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte) 0x12, a[i], "shr32");
+    }
+    shr33();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte) 0x09, a[i], "shr33");
+      a[i] = (byte) 0xf0;  // reset
+    }
+    not();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((byte) 0x0f, a[i], "not");
+    }
+    // Done.
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result, String action) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result + " for " + action);
+    }
+  }
+}
diff --git a/test/640-checker-char-simd/expected.txt b/test/640-checker-char-simd/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
diff --git a/test/640-checker-char-simd/info.txt b/test/640-checker-char-simd/info.txt
new file mode 100644 (file)
index 0000000..c9c6d5e
--- /dev/null
@@ -0,0 +1 @@
+Functional tests on SIMD vectorization.
diff --git a/test/640-checker-char-simd/src/Main.java b/test/640-checker-char-simd/src/Main.java
new file mode 100644 (file)
index 0000000..0628b36
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Functional tests for SIMD vectorization.
+ */
+public class Main {
+
+  static char[] a;
+
+  //
+  // Arithmetic operations.
+  //
+
+  /// CHECK-START: void Main.add(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAdd   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void add(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] += x;
+  }
+
+  /// CHECK-START: void Main.sub(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecSub   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void sub(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] -= x;
+  }
+
+  /// CHECK-START: void Main.mul(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecMul   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void mul(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] *= x;
+  }
+
+  /// CHECK-START: void Main.div(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.div(int) loop_optimization (after)
+  //
+  //  Not supported on any architecture.
+  //
+  static void div(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] /= x;
+  }
+
+  /// CHECK-START: void Main.neg() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNeg   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void neg() {
+    for (int i = 0; i < 128; i++)
+      a[i] = (char) -a[i];
+  }
+
+  /// CHECK-START: void Main.not() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.not() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNot   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void not() {
+    for (int i = 0; i < 128; i++)
+      a[i] = (char) ~a[i];
+  }
+
+  /// CHECK-START: void Main.shl4() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecShl   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void shl4() {
+    for (int i = 0; i < 128; i++)
+      a[i] <<= 4;
+  }
+
+  /// CHECK-START: void Main.sar2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void sar2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>= 2;
+  }
+
+  /// CHECK-START: void Main.shr2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void shr2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 2;
+  }
+
+  //
+  // Shift sanity.
+  //
+
+  static void sar31() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>= 31;
+  }
+
+  static void shr31() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 31;
+  }
+
+  static void shr32() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 32;  // 0, since & 31
+  }
+
+  static void shr33() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 33;  // 1, since & 31
+  }
+
+  //
+  // Loop bounds.
+  //
+
+  static void bounds() {
+    for (int i = 1; i < 127; i++)
+      a[i] += 11;
+  }
+
+  //
+  // Test Driver.
+  //
+
+  public static void main(String[] args) {
+    // Set up.
+    a = new char[128];
+    for (int i = 0; i < 128; i++) {
+      a[i] = (char) i;
+    }
+    // Arithmetic operations.
+    add(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + 2, a[i], "add");
+    }
+    sub(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "sub");
+    }
+    mul(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + i, a[i], "mul");
+    }
+    div(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "div");
+    }
+    neg();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((char)-i, a[i], "neg");
+    }
+    // Loop bounds.
+    bounds();
+    expectEquals(0, a[0], "bounds0");
+    for (int i = 1; i < 127; i++) {
+      expectEquals((char)(11 - i), a[i], "bounds");
+    }
+    expectEquals((char)-127, a[127], "bounds127");
+    // Shifts.
+    for (int i = 0; i < 128; i++) {
+      a[i] = (char) 0xffff;
+    }
+    shl4();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((char) 0xfff0, a[i], "shl4");
+    }
+    sar2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((char) 0x3ffc, a[i], "sar2");
+    }
+    shr2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((char) 0x0fff, a[i], "shr2");
+      a[i] = (char) 0xffff;  // reset
+    }
+    sar31();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0, a[i], "sar31");
+      a[i] = (char) 0xffff;  // reset
+    }
+    shr31();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0, a[i], "shr31");
+      a[i] = (char) 0x1200;  // reset
+    }
+    shr32();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((char) 0x1200, a[i], "shr32");
+    }
+    shr33();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((char) 0x0900, a[i], "shr33");
+      a[i] = (char) 0xf1f0;  // reset
+    }
+    not();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((char) 0x0e0f, a[i], "not");
+    }
+    // Done.
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result, String action) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result + " for " + action);
+    }
+  }
+}
diff --git a/test/640-checker-double-simd/expected.txt b/test/640-checker-double-simd/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
diff --git a/test/640-checker-double-simd/info.txt b/test/640-checker-double-simd/info.txt
new file mode 100644 (file)
index 0000000..c9c6d5e
--- /dev/null
@@ -0,0 +1 @@
+Functional tests on SIMD vectorization.
diff --git a/test/640-checker-double-simd/src/Main.java b/test/640-checker-double-simd/src/Main.java
new file mode 100644 (file)
index 0000000..43f65f1
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Functional tests for SIMD vectorization. Note that this class provides a mere
+ * functional test, not a precise numerical verifier.
+ */
+public class Main {
+
+  static double[] a;
+
+  //
+  // Arithmetic operations.
+  //
+
+  /// CHECK-START: void Main.add(double) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.add(double) loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void add(double x) {
+    for (int i = 0; i < 128; i++)
+      a[i] += x;
+  }
+
+  /// CHECK-START: void Main.sub(double) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sub(double) loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void sub(double x) {
+    for (int i = 0; i < 128; i++)
+      a[i] -= x;
+  }
+
+  /// CHECK-START: void Main.mul(double) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.mul(double) loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void mul(double x) {
+    for (int i = 0; i < 128; i++)
+      a[i] *= x;
+  }
+
+  /// CHECK-START: void Main.div(double) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.div(double) loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void div(double x) {
+    for (int i = 0; i < 128; i++)
+      a[i] /= x;
+  }
+
+  /// CHECK-START: void Main.neg() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void neg() {
+    for (int i = 0; i < 128; i++)
+      a[i] = -a[i];
+  }
+
+  /// CHECK-START: void Main.abs() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.abs() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void abs() {
+    for (int i = 0; i < 128; i++)
+      a[i] = Math.abs(a[i]);
+  }
+
+  /// CHECK-START: void Main.conv(long[]) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.conv(long[]) loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void conv(long[] b) {
+    for (int i = 0; i < 128; i++)
+      a[i] = b[i];
+  }
+
+  //
+  // Loop bounds.
+  //
+
+  static void bounds() {
+    for (int i = 1; i < 127; i++)
+      a[i] += 11;
+  }
+
+  //
+  // Test Driver.
+  //
+
+  public static void main(String[] args) {
+    // Set up.
+    a = new double[128];
+    for (int i = 0; i < 128; i++) {
+      a[i] = i;
+    }
+    // Arithmetic operations.
+    add(2.0);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + 2, a[i], "add");
+    }
+    sub(2.0);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "sub");
+    }
+    mul(2.0);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + i, a[i], "mul");
+    }
+    div(2.0);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "div");
+    }
+    neg();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(-i, a[i], "neg");
+    }
+    // Loop bounds.
+    bounds();
+    expectEquals(0, a[0], "bounds0");
+    for (int i = 1; i < 127; i++) {
+      expectEquals(11 - i, a[i], "bounds");
+    }
+    expectEquals(-127, a[127], "bounds127");
+    // Abs.
+    abs();
+    expectEquals(0, a[0], "abs0");
+    for (int i = 1; i <= 11; i++) {
+      expectEquals(11 - i, a[i], "abs_lo");
+    }
+    for (int i = 12; i < 127; i++) {
+      expectEquals(i - 11, a[i], "abs_hi");
+    }
+    expectEquals(127, a[127], "abs127");
+    // Conversion.
+    long[] b = new long[128];
+    for (int i = 0; i < 128; i++) {
+      b[i] = 1000 * i;
+    }
+    conv(b);
+    for (int i = 1; i < 127; i++) {
+      expectEquals(1000.0 * i, a[i], "conv");
+    }
+    // Done.
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(double expected, double result, String action) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result + " for " + action);
+    }
+  }
+}
diff --git a/test/640-checker-float-simd/expected.txt b/test/640-checker-float-simd/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
diff --git a/test/640-checker-float-simd/info.txt b/test/640-checker-float-simd/info.txt
new file mode 100644 (file)
index 0000000..c9c6d5e
--- /dev/null
@@ -0,0 +1 @@
+Functional tests on SIMD vectorization.
diff --git a/test/640-checker-float-simd/src/Main.java b/test/640-checker-float-simd/src/Main.java
new file mode 100644 (file)
index 0000000..80c3112
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Functional tests for SIMD vectorization. Note that this class provides a mere
+ * functional test, not a precise numerical verifier.
+ */
+public class Main {
+
+  static float[] a;
+
+  //
+  // Arithmetic operations.
+  //
+
+  /// CHECK-START: void Main.add(float) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.add(float) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAdd   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void add(float x) {
+    for (int i = 0; i < 128; i++)
+      a[i] += x;
+  }
+
+  /// CHECK-START: void Main.sub(float) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sub(float) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecSub   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void sub(float x) {
+    for (int i = 0; i < 128; i++)
+      a[i] -= x;
+  }
+
+  /// CHECK-START: void Main.mul(float) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.mul(float) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecMul   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void mul(float x) {
+    for (int i = 0; i < 128; i++)
+      a[i] *= x;
+  }
+
+  /// CHECK-START: void Main.div(float) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.div(float) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecDiv   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void div(float x) {
+    for (int i = 0; i < 128; i++)
+      a[i] /= x;
+  }
+
+  /// CHECK-START: void Main.neg() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNeg   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void neg() {
+    for (int i = 0; i < 128; i++)
+      a[i] = -a[i];
+  }
+
+  /// CHECK-START: void Main.abs() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.abs() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void abs() {
+    for (int i = 0; i < 128; i++)
+      a[i] = Math.abs(a[i]);
+  }
+
+  /// CHECK-START: void Main.conv(int[]) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.conv(int[]) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecCnv   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void conv(int[] b) {
+    for (int i = 0; i < 128; i++)
+      a[i] = b[i];
+  }
+
+  //
+  // Loop bounds.
+  //
+
+  static void bounds() {
+    for (int i = 1; i < 127; i++)
+      a[i] += 11;
+  }
+
+  //
+  // Test Driver.
+  //
+
+  public static void main(String[] args) {
+    // Set up.
+    a = new float[128];
+    for (int i = 0; i < 128; i++) {
+      a[i] = i;
+    }
+    // Arithmetic operations.
+    add(2.0f);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + 2, a[i], "add");
+    }
+    sub(2.0f);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "sub");
+    }
+    mul(2.0f);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + i, a[i], "mul");
+    }
+    div(2.0f);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "div");
+    }
+    neg();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(-i, a[i], "neg");
+    }
+    // Loop bounds.
+    bounds();
+    expectEquals(0, a[0], "bounds0");
+    for (int i = 1; i < 127; i++) {
+      expectEquals(11 - i, a[i], "bounds");
+    }
+    expectEquals(-127, a[127], "bounds127");
+    // Abs.
+    abs();
+    expectEquals(0, a[0], "abs0");
+    for (int i = 1; i <= 11; i++) {
+      expectEquals(11 - i, a[i], "abs_lo");
+    }
+    for (int i = 12; i < 127; i++) {
+      expectEquals(i - 11, a[i], "abs_hi");
+    }
+    expectEquals(127, a[127], "abs127");
+    // Conversion.
+    int[] b = new int[128];
+    for (int i = 0; i < 128; i++) {
+      b[i] = 1000 * i;
+    }
+    conv(b);
+    for (int i = 1; i < 127; i++) {
+      expectEquals(1000.0f * i, a[i], "conv");
+    }
+    // Done.
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(float expected, float result, String action) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result + " for " + action);
+    }
+  }
+}
diff --git a/test/640-checker-int-simd/expected.txt b/test/640-checker-int-simd/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
diff --git a/test/640-checker-int-simd/info.txt b/test/640-checker-int-simd/info.txt
new file mode 100644 (file)
index 0000000..c9c6d5e
--- /dev/null
@@ -0,0 +1 @@
+Functional tests on SIMD vectorization.
diff --git a/test/640-checker-int-simd/src/Main.java b/test/640-checker-int-simd/src/Main.java
new file mode 100644 (file)
index 0000000..ba1e142
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Functional tests for SIMD vectorization.
+ */
+public class Main {
+
+  static int[] a;
+
+  //
+  // Arithmetic operations.
+  //
+
+  /// CHECK-START: void Main.add(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAdd   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void add(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] += x;
+  }
+
+  /// CHECK-START: void Main.sub(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecSub   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void sub(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] -= x;
+  }
+
+  /// CHECK-START: void Main.mul(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecMul   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void mul(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] *= x;
+  }
+
+  /// CHECK-START: void Main.div(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.div(int) loop_optimization (after)
+  //
+  //  Not supported on any architecture.
+  //
+  static void div(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] /= x;
+  }
+
+  /// CHECK-START: void Main.neg() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNeg   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void neg() {
+    for (int i = 0; i < 128; i++)
+      a[i] = -a[i];
+  }
+
+  /// CHECK-START: void Main.not() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.not() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNot   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void not() {
+    for (int i = 0; i < 128; i++)
+      a[i] = ~a[i];
+  }
+
+  /// CHECK-START: void Main.shl4() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecShl   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void shl4() {
+    for (int i = 0; i < 128; i++)
+      a[i] <<= 4;
+  }
+
+  /// CHECK-START: void Main.sar2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void sar2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>= 2;
+  }
+
+  /// CHECK-START: void Main.shr2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void shr2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 2;
+  }
+
+  //
+  // Shift sanity.
+  //
+
+  static void shr32() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 32;  // 0, since & 31
+  }
+
+  static void shr33() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 33;  // 1, since & 31
+  }
+
+  //
+  // Loop bounds.
+  //
+
+  static void bounds() {
+    for (int i = 1; i < 127; i++)
+      a[i] += 11;
+  }
+
+  //
+  // Test Driver.
+  //
+
+  public static void main(String[] args) {
+    // Set up.
+    a = new int[128];
+    for (int i = 0; i < 128; i++) {
+      a[i] = i;
+    }
+    // Arithmetic operations.
+    add(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + 2, a[i], "add");
+    }
+    sub(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "sub");
+    }
+    mul(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + i, a[i], "mul");
+    }
+    div(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "div");
+    }
+    neg();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(-i, a[i], "neg");
+    }
+    // Loop bounds.
+    bounds();
+    expectEquals(0, a[0], "bounds0");
+    for (int i = 1; i < 127; i++) {
+      expectEquals(11 - i, a[i], "bounds");
+    }
+    expectEquals(-127, a[127], "bounds127");
+    // Shifts.
+    for (int i = 0; i < 128; i++) {
+      a[i] = 0xffffffff;
+    }
+    shl4();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0xfffffff0, a[i], "shl4");
+    }
+    sar2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0xfffffffc, a[i], "sar2");
+    }
+    shr2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0x3fffffff, a[i], "shr2");
+    }
+    shr32();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0x3fffffff, a[i], "shr32");
+    }
+    shr33();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0x1fffffff, a[i], "shr33");
+    }
+    not();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0xe0000000, a[i], "not");
+    }
+    // Done.
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result, String action) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result + " for " + action);
+    }
+  }
+}
diff --git a/test/640-checker-long-simd/expected.txt b/test/640-checker-long-simd/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
diff --git a/test/640-checker-long-simd/info.txt b/test/640-checker-long-simd/info.txt
new file mode 100644 (file)
index 0000000..c9c6d5e
--- /dev/null
@@ -0,0 +1 @@
+Functional tests on SIMD vectorization.
diff --git a/test/640-checker-long-simd/src/Main.java b/test/640-checker-long-simd/src/Main.java
new file mode 100644 (file)
index 0000000..90a2e76
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Functional tests for SIMD vectorization.
+ */
+public class Main {
+
+  static long[] a;
+
+  //
+  // Arithmetic operations.
+  //
+
+  /// CHECK-START: void Main.add(long) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.add(long) loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void add(long x) {
+    for (int i = 0; i < 128; i++)
+      a[i] += x;
+  }
+
+  /// CHECK-START: void Main.sub(long) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sub(long) loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void sub(long x) {
+    for (int i = 0; i < 128; i++)
+      a[i] -= x;
+  }
+
+  /// CHECK-START: void Main.mul(long) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.mul(long) loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void mul(long x) {
+    for (int i = 0; i < 128; i++)
+      a[i] *= x;
+  }
+
+  /// CHECK-START: void Main.div(long) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.div(long) loop_optimization (after)
+  //
+  //  Not supported on any architecture.
+  //
+  static void div(long x) {
+    for (int i = 0; i < 128; i++)
+      a[i] /= x;
+  }
+
+  /// CHECK-START: void Main.neg() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void neg() {
+    for (int i = 0; i < 128; i++)
+      a[i] = -a[i];
+  }
+
+  /// CHECK-START: void Main.not() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.not() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void not() {
+    for (int i = 0; i < 128; i++)
+      a[i] = ~a[i];
+  }
+
+  /// CHECK-START: void Main.shl4() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void shl4() {
+    for (int i = 0; i < 128; i++)
+      a[i] <<= 4;
+  }
+
+  /// CHECK-START: void Main.sar2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void sar2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>= 2;
+  }
+
+  /// CHECK-START: void Main.shr2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void shr2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 2;
+  }
+
+  //
+  // Shift sanity.
+  //
+
+  static void shr64() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 64;  // 0, since & 63
+  }
+
+  static void shr65() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 65;  // 1, since & 63
+  }
+
+  //
+  // Loop bounds.
+  //
+
+  static void bounds() {
+    for (int i = 1; i < 127; i++)
+      a[i] += 11;
+  }
+
+  //
+  // Test Driver.
+  //
+
+  public static void main(String[] args) {
+    // Set up.
+    a = new long[128];
+    for (int i = 0; i < 128; i++) {
+      a[i] = i;
+    }
+    // Arithmetic operations.
+    add(2L);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + 2, a[i], "add");
+    }
+    sub(2L);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "sub");
+    }
+    mul(2L);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + i, a[i], "mul");
+    }
+    div(2L);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "div");
+    }
+    neg();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(-i, a[i], "neg");
+    }
+    // Loop bounds.
+    bounds();
+    expectEquals(0, a[0], "bounds0");
+    for (int i = 1; i < 127; i++) {
+      expectEquals(11 - i, a[i], "bounds");
+    }
+    expectEquals(-127, a[127], "bounds127");
+    // Shifts.
+    for (int i = 0; i < 128; i++) {
+      a[i] = 0xffffffffffffffffL;
+    }
+    shl4();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0xfffffffffffffff0L, a[i], "shl4");
+    }
+    sar2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0xfffffffffffffffcL, a[i], "sar2");
+    }
+    shr2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0x3fffffffffffffffL, a[i], "shr2");
+    }
+    shr64();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0x3fffffffffffffffL, a[i], "shr64");
+    }
+    shr65();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0x1fffffffffffffffL, a[i], "shr65");
+    }
+    not();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0xe000000000000000L, a[i], "not");
+    }
+    // Done.
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(long expected, long result, String action) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result + " for " + action);
+    }
+  }
+}
diff --git a/test/640-checker-short-simd/expected.txt b/test/640-checker-short-simd/expected.txt
new file mode 100644 (file)
index 0000000..b0aad4d
--- /dev/null
@@ -0,0 +1 @@
+passed
diff --git a/test/640-checker-short-simd/info.txt b/test/640-checker-short-simd/info.txt
new file mode 100644 (file)
index 0000000..c9c6d5e
--- /dev/null
@@ -0,0 +1 @@
+Functional tests on SIMD vectorization.
diff --git a/test/640-checker-short-simd/src/Main.java b/test/640-checker-short-simd/src/Main.java
new file mode 100644 (file)
index 0000000..241f8e6
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * Functional tests for SIMD vectorization.
+ */
+public class Main {
+
+  static short[] a;
+
+  //
+  // Arithmetic operations.
+  //
+
+  /// CHECK-START: void Main.add(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.add(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecAdd   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void add(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] += x;
+  }
+
+  /// CHECK-START: void Main.sub(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sub(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecSub   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void sub(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] -= x;
+  }
+
+  /// CHECK-START: void Main.mul(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.mul(int) loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecMul   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void mul(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] *= x;
+  }
+
+  /// CHECK-START: void Main.div(int) loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START: void Main.div(int) loop_optimization (after)
+  //
+  //  Not supported on any architecture.
+  //
+  static void div(int x) {
+    for (int i = 0; i < 128; i++)
+      a[i] /= x;
+  }
+
+  /// CHECK-START: void Main.neg() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.neg() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNeg   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void neg() {
+    for (int i = 0; i < 128; i++)
+      a[i] = (short) -a[i];
+  }
+
+  /// CHECK-START: void Main.not() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.not() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecNot   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void not() {
+    for (int i = 0; i < 128; i++)
+      a[i] = (short) ~a[i];
+  }
+
+  /// CHECK-START: void Main.shl4() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shl4() loop_optimization (after)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: VecLoad  loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecShl   loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: VecStore loop:<<Loop>>      outer_loop:none
+  static void shl4() {
+    for (int i = 0; i < 128; i++)
+      a[i] <<= 4;
+  }
+
+  /// CHECK-START: void Main.sar2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.sar2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void sar2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>= 2;
+  }
+
+  /// CHECK-START: void Main.shr2() loop_optimization (before)
+  /// CHECK-DAG: Phi      loop:<<Loop:B\d+>> outer_loop:none
+  /// CHECK-DAG: ArrayGet loop:<<Loop>>      outer_loop:none
+  /// CHECK-DAG: ArraySet loop:<<Loop>>      outer_loop:none
+  //
+  /// CHECK-START-ARM64: void Main.shr2() loop_optimization (after)
+  //
+  // TODO: fill in when supported
+  static void shr2() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 2;
+  }
+
+  //
+  // Shift sanity.
+  //
+
+  static void sar31() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>= 31;
+  }
+
+  static void shr31() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 31;
+  }
+
+  static void shr32() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 32;  // 0, since & 31
+  }
+
+
+  static void shr33() {
+    for (int i = 0; i < 128; i++)
+      a[i] >>>= 33;  // 1, since & 31
+  }
+
+  //
+  // Loop bounds.
+  //
+
+  static void add() {
+    for (int i = 1; i < 127; i++)
+      a[i] += 11;
+  }
+
+  //
+  // Test Driver.
+  //
+
+  public static void main(String[] args) {
+    // Set up.
+    a = new short[128];
+    for (int i = 0; i < 128; i++) {
+      a[i] = (short) i;
+    }
+    // Arithmetic operations.
+    add(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + 2, a[i], "add");
+    }
+    sub(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "sub");
+    }
+    mul(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i + i, a[i], "mul");
+    }
+    div(2);
+    for (int i = 0; i < 128; i++) {
+      expectEquals(i, a[i], "div");
+    }
+    neg();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(-i, a[i], "neg");
+    }
+    // Loop bounds.
+    add();
+    expectEquals(0, a[0], "bounds0");
+    for (int i = 1; i < 127; i++) {
+      expectEquals(11 - i, a[i], "bounds");
+    }
+    expectEquals(-127, a[127], "bounds127");
+    // Shifts.
+    for (int i = 0; i < 128; i++) {
+      a[i] = (short) 0xffff;
+    }
+    shl4();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((short) 0xfff0, a[i], "shl4");
+    }
+    sar2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((short) 0xfffc, a[i], "sar2");
+    }
+    shr2();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((short) 0xffff, a[i], "shr2");  // sic!
+    }
+    sar31();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((short) 0xffff, a[i], "sar31");
+    }
+    shr31();
+    for (int i = 0; i < 128; i++) {
+      expectEquals(0x0001, a[i], "shr31");
+      a[i] = (short) 0x1200;  // reset
+    }
+    shr32();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((short) 0x1200, a[i], "shr32");
+    }
+    shr33();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((short) 0x0900, a[i], "shr33");
+      a[i] = (short) 0xf0f1;  // reset
+    }
+    not();
+    for (int i = 0; i < 128; i++) {
+      expectEquals((short) 0x0f0e, a[i], "not");
+    }
+    // Done.
+    System.out.println("passed");
+  }
+
+  private static void expectEquals(int expected, int result, String action) {
+    if (expected != result) {
+      throw new Error("Expected: " + expected + ", found: " + result + " for " + action);
+    }
+  }
+}
index cbd7686..20b227a 100644 (file)
 #include <jni.h>
 #include <stdio.h>
 #include <string.h>
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jvmti.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test901HelloTi {
@@ -148,14 +149,14 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setVerboseFlag(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jint iflag, jboolean val) {
   jvmtiVerboseFlag flag = static_cast<jvmtiVerboseFlag>(iflag);
   jvmtiError result = jvmti_env->SetVerboseFlag(flag, val);
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT jboolean JNICALL Java_Main_checkLivePhase(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jvmtiPhase current_phase;
   jvmtiError phase_result = jvmti_env->GetPhase(&current_phase);
-  if (JvmtiErrorToException(env, phase_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, phase_result)) {
     return JNI_FALSE;
   }
   return (current_phase == JVMTI_PHASE_LIVE) ? JNI_TRUE : JNI_FALSE;
index b85ed48..701b0c3 100644 (file)
 #include <stdio.h>
 #include <vector>
 
+#include "android-base/logging.h"
 #include "jni.h"
-#include "ScopedLocalRef.h"
-#include "ScopedPrimitiveArray.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
 
-#include "art_method-inl.h"
-#include "base/logging.h"
 #include "jvmti.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
-#include "utils.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test903HelloTagging {
 
-extern "C" JNIEXPORT void JNICALL Java_Main_setTag(JNIEnv* env ATTRIBUTE_UNUSED,
-                                                   jclass,
-                                                   jobject obj,
-                                                   jlong tag) {
+extern "C" JNIEXPORT void JNICALL Java_Main_setTag(JNIEnv* env, jclass, jobject obj, jlong tag) {
   jvmtiError ret = jvmti_env->SetTag(obj, tag);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error setting tag: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
-extern "C" JNIEXPORT jlong JNICALL Java_Main_getTag(JNIEnv* env ATTRIBUTE_UNUSED,
-                                                    jclass,
-                                                    jobject obj) {
+extern "C" JNIEXPORT jlong JNICALL Java_Main_getTag(JNIEnv* env, jclass, jobject obj) {
   jlong tag = 0;
   jvmtiError ret = jvmti_env->GetTag(obj, &tag);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error getting tag: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
+    return 0;
   }
   return tag;
 }
@@ -86,11 +73,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTaggedObjects(JNIEnv* env
                                                  &result_count,
                                                  result_object_array_ptr,
                                                  result_tag_array_ptr);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Failure running GetLoadedClasses: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
 
@@ -197,4 +180,3 @@ extern "C" JNIEXPORT jlongArray JNICALL Java_Main_testTagsInDifferentEnvs(
 
 }  // namespace Test903HelloTagging
 }  // namespace art
-
index cc6f681..c829496 100644 (file)
 #include <stdio.h>
 #include <vector>
 
-#include "base/logging.h"
+#include "android-base/logging.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ScopedUtfChars.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
-#include "utils.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test904ObjectAllocation {
@@ -57,21 +58,16 @@ static void JNICALL ObjectAllocated(jvmtiEnv* ti_env ATTRIBUTE_UNUSED,
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectAllocCallback(
-    JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
+    JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) {
   jvmtiEventCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
   callbacks.VMObjectAlloc = enable ? ObjectAllocated : nullptr;
 
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error setting callbacks: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_enableAllocationTracking(JNIEnv* env ATTRIBUTE_UNUSED,
+extern "C" JNIEXPORT void JNICALL Java_Main_enableAllocationTracking(JNIEnv* env,
                                                                      jclass,
                                                                      jthread thread,
                                                                      jboolean enable) {
@@ -79,14 +75,8 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableAllocationTracking(JNIEnv* env
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_VM_OBJECT_ALLOC,
       thread);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error enabling/disabling allocation tracking: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 }  // namespace Test904ObjectAllocation
 }  // namespace art
-
index c489309..59b429c 100644 (file)
 #include <stdio.h>
 #include <vector>
 
-#include "base/logging.h"
+#include "android-base/logging.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ScopedUtfChars.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
-#include "utils.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test905ObjectFree {
@@ -46,52 +47,39 @@ static void JNICALL ObjectFree2(jvmtiEnv* ti_env, jlong tag) {
   collected_tags2.push_back(tag);
 }
 
-static void setupObjectFreeCallback(jvmtiEnv* env, jvmtiEventObjectFree callback) {
+static void setupObjectFreeCallback(JNIEnv* env, jvmtiEnv* jenv, jvmtiEventObjectFree callback) {
   jvmtiEventCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
   callbacks.ObjectFree = callback;
-  jvmtiError ret = env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    env->GetErrorName(ret, &err);
-    printf("Error setting callbacks: %s\n", err);
-    env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  jvmtiError ret = jenv->SetEventCallbacks(&callbacks, sizeof(callbacks));
+  JvmtiErrorToException(env, jenv, ret);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectFreeCallback(
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
-  setupObjectFreeCallback(jvmti_env, ObjectFree1);
+  setupObjectFreeCallback(env, jvmti_env, ObjectFree1);
   JavaVM* jvm = nullptr;
   env->GetJavaVM(&jvm);
   CHECK_EQ(jvm->GetEnv(reinterpret_cast<void**>(&jvmti_env2), JVMTI_VERSION_1_2), 0);
   SetAllCapabilities(jvmti_env2);
-  setupObjectFreeCallback(jvmti_env2, ObjectFree2);
+  setupObjectFreeCallback(env, jvmti_env2, ObjectFree2);
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_enableFreeTracking(JNIEnv* env ATTRIBUTE_UNUSED,
+extern "C" JNIEXPORT void JNICALL Java_Main_enableFreeTracking(JNIEnv* env,
                                                                jclass klass ATTRIBUTE_UNUSED,
                                                                jboolean enable) {
   jvmtiError ret = jvmti_env->SetEventNotificationMode(
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_OBJECT_FREE,
       nullptr);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Error enabling/disabling object-free callbacks: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
+    return;
   }
   ret = jvmti_env2->SetEventNotificationMode(
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_OBJECT_FREE,
       nullptr);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env2->GetErrorName(ret, &err);
-    printf("Error enabling/disabling object-free callbacks: %s\n", err);
-    jvmti_env2->Deallocate(reinterpret_cast<unsigned char*>(err));
-  }
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 extern "C" JNIEXPORT jlongArray JNICALL Java_Main_getCollectedTags(JNIEnv* env,
@@ -114,7 +102,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setTag2(JNIEnv* env,
                                                     jobject obj,
                                                     jlong tag) {
   jvmtiError ret = jvmti_env2->SetTag(obj, tag);
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 }  // namespace Test905ObjectFree
index f2532de..bb30074 100644 (file)
 #include <stdio.h>
 #include <vector>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "base/logging.h"
+
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedPrimitiveArray.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
-#include "utf.h"
+#include "scoped_primitive_array.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+#include "ti_utf.h"
 
 namespace art {
 namespace Test906IterateHeap {
@@ -52,7 +56,7 @@ static jint JNICALL HeapIterationCallback(jlong class_tag,
   return config->Handle(class_tag, size, tag_ptr, length);
 }
 
-static bool Run(jint heap_filter, jclass klass_filter, IterationConfig* config) {
+static bool Run(JNIEnv* env, jint heap_filter, jclass klass_filter, IterationConfig* config) {
   jvmtiHeapCallbacks callbacks;
   memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
   callbacks.heap_iteration_callback = HeapIterationCallback;
@@ -61,17 +65,13 @@ static bool Run(jint heap_filter, jclass klass_filter, IterationConfig* config)
                                                  klass_filter,
                                                  &callbacks,
                                                  config);
-  if (ret != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(ret, &err);
-    printf("Failure running IterateThroughHeap: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return false;
   }
   return true;
 }
 
-extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapCount(JNIEnv* env ATTRIBUTE_UNUSED,
+extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapCount(JNIEnv* env,
                                                                     jclass klass ATTRIBUTE_UNUSED,
                                                                     jint heap_filter,
                                                                     jclass klass_filter,
@@ -99,7 +99,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapCount(JNIEnv* env
   };
 
   CountIterationConfig config(0, stop_after);
-  Run(heap_filter, klass_filter, &config);
+  Run(env, heap_filter, klass_filter, &config);
 
   if (config.counter > config.stop_after) {
     printf("Error: more objects visited than signaled.");
@@ -135,7 +135,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapData(JNIEnv* env,
   };
 
   DataIterationConfig config;
-  if (!Run(heap_filter, klass_filter, &config)) {
+  if (!Run(env, heap_filter, klass_filter, &config)) {
     return -1;
   }
 
@@ -154,7 +154,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapData(JNIEnv* env,
   return static_cast<jint>(config.class_tags_.size());
 }
 
-extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env ATTRIBUTE_UNUSED,
+extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env,
                                                                   jclass klass ATTRIBUTE_UNUSED,
                                                                   jint heap_filter,
                                                                   jclass klass_filter) {
@@ -175,7 +175,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env AT
   };
 
   AddIterationConfig config;
-  Run(heap_filter, klass_filter, &config);
+  Run(env, heap_filter, klass_filter, &config);
 }
 
 extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapString(
@@ -199,10 +199,10 @@ extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapString(
                                             void* user_data) {
       FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
       if (*tag_ptr == p->tag_to_find) {
-        size_t utf_byte_count = CountUtf8Bytes(value, value_length);
+        size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
         std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
         memset(mod_utf.get(), 0, utf_byte_count + 1);
-        ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
+        ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
         if (!p->data.empty()) {
           p->data += "\n";
         }
@@ -228,7 +228,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapString(
 
   FindStringCallbacks fsc(tag);
   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fsc);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(fsc.data.c_str());
@@ -316,7 +316,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapPrimitiveArray(
 
   FindArrayCallbacks fac(tag);
   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &fac);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(fac.data.c_str());
@@ -403,7 +403,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_Main_iterateThroughHeapPrimitiveFields
 
   FindFieldCallbacks ffc(tag);
   jvmtiError ret = jvmti_env->IterateThroughHeap(0, nullptr, &callbacks, &ffc);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(ffc.data.c_str());
index 48ce2e2..5ec56c4 100644 (file)
 #include <stdio.h>
 #include <vector>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
+
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ScopedUtfChars.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test907GetLoadedClasses {
index 45148f8..f186895 100644 (file)
 #include <stdio.h>
 #include <string.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
+
 #include "jni.h"
 #include "jvmti.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test908GcStartFinish {
@@ -45,7 +48,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setupGcCallback(
   callbacks.GarbageCollectionStart = GarbageCollectionStart;
 
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_enableGcTracking(JNIEnv* env,
@@ -55,14 +58,14 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableGcTracking(JNIEnv* env,
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_GARBAGE_COLLECTION_START,
       nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
   ret = jvmti_env->SetEventNotificationMode(
       enable ? JVMTI_ENABLE : JVMTI_DISABLE,
       JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
       nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 }
index 67c7567..0150e09 100644 (file)
@@ -19,7 +19,9 @@
 #include <jni.h>
 #include <stdio.h>
 #include <string.h>
-#include "base/macros.h"
+
+#include "android-base/macros.h"
+
 #include "jvmti.h"
 
 namespace art {
index fdc4cdb..ded4f09 100644 (file)
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
+
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test910Methods {
@@ -35,11 +38,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getMethodName(
   char* sig;
   char* gen;
   jvmtiError result = jvmti_env->GetMethodName(id, &name, &sig, &gen);
-  if (result != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(result, &err);
-    printf("Failure running GetMethodName: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -67,11 +66,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getMethodName(
 
   // Also run GetMethodName with all parameter pointers null to check for segfaults.
   jvmtiError result2 = jvmti_env->GetMethodName(id, nullptr, nullptr, nullptr);
-  if (result2 != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(result2, &err);
-    printf("Failure running GetMethodName(null, null, null): %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, result2)) {
     return nullptr;
   }
 
@@ -84,11 +79,7 @@ extern "C" JNIEXPORT jclass JNICALL Java_Main_getMethodDeclaringClass(
 
   jclass declaring_class;
   jvmtiError result = jvmti_env->GetMethodDeclaringClass(id, &declaring_class);
-  if (result != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(result, &err);
-    printf("Failure running GetMethodDeclaringClass: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -101,11 +92,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getMethodModifiers(
 
   jint modifiers;
   jvmtiError result = jvmti_env->GetMethodModifiers(id, &modifiers);
-  if (result != JVMTI_ERROR_NONE) {
-    char* err;
-    jvmti_env->GetErrorName(result, &err);
-    printf("Failure running GetMethodModifiers: %s\n", err);
-    jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return 0;
   }
 
@@ -118,7 +105,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getMaxLocals(
 
   jint max_locals;
   jvmtiError result = jvmti_env->GetMaxLocals(id, &max_locals);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
 
@@ -131,7 +118,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getArgumentsSize(
 
   jint arguments;
   jvmtiError result = jvmti_env->GetArgumentsSize(id, &arguments);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
 
@@ -145,7 +132,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_getMethodLocationStart(
   jlong start;
   jlong end;
   jvmtiError result = jvmti_env->GetMethodLocation(id, &start, &end);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
 
@@ -159,7 +146,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_getMethodLocationEnd(
   jlong start;
   jlong end;
   jvmtiError result = jvmti_env->GetMethodLocation(id, &start, &end);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
 
@@ -172,7 +159,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodNative(
 
   jboolean is_native;
   jvmtiError result = jvmti_env->IsMethodNative(id, &is_native);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return JNI_FALSE;
   }
 
@@ -185,7 +172,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodObsolete(
 
   jboolean is_obsolete;
   jvmtiError result = jvmti_env->IsMethodObsolete(id, &is_obsolete);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return JNI_FALSE;
   }
 
@@ -198,7 +185,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isMethodSynthetic(
 
   jboolean is_synthetic;
   jvmtiError result = jvmti_env->IsMethodSynthetic(id, &is_synthetic);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return JNI_FALSE;
   }
 
index 5a3a311..a499e90 100644 (file)
 #include <memory>
 #include <stdio.h>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
-#include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
 
 namespace art {
 namespace Test911GetStackTrace {
@@ -68,7 +71,7 @@ static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env,
     char* gen;
     {
       jvmtiError result2 = jvmti_env->GetMethodName(frames[method_index].method, &name, &sig, &gen);
-      if (JvmtiErrorToException(env, result2)) {
+      if (JvmtiErrorToException(env, jvmti_env, result2)) {
         return nullptr;
       }
     }
@@ -83,10 +86,7 @@ static jobjectArray TranslateJvmtiFrameInfoArray(JNIEnv* env,
         // Accept absent info and native method errors.
         if (line_result != JVMTI_ERROR_ABSENT_INFORMATION &&
             line_result != JVMTI_ERROR_NATIVE_METHOD) {
-          char* err;
-          jvmti_env->GetErrorName(line_result, &err);
-          printf("Failure running GetLineNumberTable: %s\n", err);
-          jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
+          JvmtiErrorToException(env, jvmti_env, line_result);
           return nullptr;
         }
         line_number_table = nullptr;
@@ -139,7 +139,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_PrintThread_getStackTrace(
   jint count;
   {
     jvmtiError result = jvmti_env->GetStackTrace(thread, start, max, frames.get(), &count);
-    if (JvmtiErrorToException(env, result)) {
+    if (JvmtiErrorToException(env, jvmti_env, result)) {
       return nullptr;
     }
   }
@@ -153,7 +153,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_AllTraces_getAllStackTraces(
   jvmtiStackInfo* stack_infos;
   {
     jvmtiError result = jvmti_env->GetAllStackTraces(max, &stack_infos, &thread_count);
-    if (JvmtiErrorToException(env, result)) {
+    if (JvmtiErrorToException(env, jvmti_env, result)) {
       return nullptr;
     }
   }
@@ -189,7 +189,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_ThreadListTraces_getThreadListSta
                                                             threads.get(),
                                                             max,
                                                             &stack_infos);
-    if (JvmtiErrorToException(env, result)) {
+    if (JvmtiErrorToException(env, jvmti_env, result)) {
       return nullptr;
     }
   }
@@ -215,7 +215,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Frames_getFrameCount(
     JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread) {
   jint count;
   jvmtiError result = jvmti_env->GetFrameCount(thread, &count);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
   return count;
@@ -227,7 +227,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Frames_getFrameLocation(
   jlocation location;
 
   jvmtiError result = jvmti_env->GetFrameLocation(thread, depth, &method, &location);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -237,12 +237,12 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Frames_getFrameLocation(
       {
         jclass decl_class;
         jvmtiError class_result = jvmti_env->GetMethodDeclaringClass(method, &decl_class);
-        if (JvmtiErrorToException(env, class_result)) {
+        if (JvmtiErrorToException(env, jvmti_env, class_result)) {
           return nullptr;
         }
         jint modifiers;
         jvmtiError mod_result = jvmti_env->GetMethodModifiers(method, &modifiers);
-        if (JvmtiErrorToException(env, mod_result)) {
+        if (JvmtiErrorToException(env, jvmti_env, mod_result)) {
           return nullptr;
         }
         constexpr jint kStatic = 0x8;
index 5bd34f6..2636367 100644 (file)
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
+
 #include "class_linker.h"
 #include "jni.h"
 #include "mirror/class_loader.h"
 #include "jvmti.h"
 #include "runtime.h"
-#include "ScopedLocalRef.h"
-#include "ScopedUtfChars.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test912Classes {
@@ -233,7 +236,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassLoaderClasses(
   jint count = 0;
   jclass* classes = nullptr;
   jvmtiError result = jvmti_env->GetClassLoaderClasses(jclassloader, &count, &classes);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -251,7 +254,7 @@ extern "C" JNIEXPORT jintArray JNICALL Java_Main_getClassVersion(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) {
   jint major, minor;
   jvmtiError result = jvmti_env->GetClassVersionNumbers(klass, &minor, &major);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -270,7 +273,7 @@ static std::string GetClassName(jvmtiEnv* jenv, JNIEnv* jni_env, jclass klass) {
   jvmtiError result = jenv->GetClassSignature(klass, &name, nullptr);
   if (result != JVMTI_ERROR_NONE) {
     if (jni_env != nullptr) {
-      JvmtiErrorToException(jni_env, result);
+      JvmtiErrorToException(jni_env, jenv, result);
     } else {
       printf("Failed to get class signature.\n");
     }
@@ -291,13 +294,13 @@ static void EnableEvents(JNIEnv* env,
     jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                                          JVMTI_EVENT_CLASS_LOAD,
                                                          nullptr);
-    if (JvmtiErrorToException(env, ret)) {
+    if (JvmtiErrorToException(env, jvmti_env, ret)) {
       return;
     }
     ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                               JVMTI_EVENT_CLASS_PREPARE,
                                               nullptr);
-    JvmtiErrorToException(env, ret);
+    JvmtiErrorToException(env, jvmti_env, ret);
     return;
   }
 
@@ -306,20 +309,20 @@ static void EnableEvents(JNIEnv* env,
   callbacks.ClassLoad = class_load;
   callbacks.ClassPrepare = class_prepare;
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_CLASS_LOAD,
                                             nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_CLASS_PREPARE,
                                             nullptr);
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 class ClassLoadPreparePrinter {
@@ -364,7 +367,7 @@ class ClassLoadPreparePrinter {
     jvmtiError result = jenv->GetThreadInfo(thread, &info);
     if (result != JVMTI_ERROR_NONE) {
       if (jni_env != nullptr) {
-        JvmtiErrorToException(jni_env, result);
+        JvmtiErrorToException(jni_env, jenv, result);
       } else {
         printf("Failed to get thread name.\n");
       }
index fc2761e..2a183ee 100644 (file)
@@ -79,6 +79,37 @@ root@root --(thread)--> 3000@0 [size=132, length=-1]
 5@1002 --(field@9)--> 1@1000 [size=16, length=-1]
 6@1000 --(class)--> 1000@0 [size=123, length=-1]
 ---
+root@root --(thread)--> 3000@0 [size=132, length=-1]
+---
+3@1001 --(class)--> 1001@0 [size=123, length=-1]
+---
+root@root --(thread)--> 3000@0 [size=132, length=-1]
+---
+3@1001 --(class)--> 1001@0 [size=123, length=-1]
+---
+root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=13,location= 32])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=3,method=doFollowReferencesTest,vreg=1,location= 28])--> 3000@0 [size=132, length=-1]
+root@root --(thread)--> 3000@0 [size=132, length=-1]
+0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
+---
+1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
+3@1001 --(class)--> 1001@0 [size=123, length=-1]
+3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+---
+root@root --(jni-global)--> 1@1000 [size=16, length=-1]
+root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=13,location= 10])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 10])--> 1@1000 [size=16, length=-1]
+root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 19])--> 1@1000 [size=16, length=-1]
+root@root --(thread)--> 1@1000 [size=16, length=-1]
+root@root --(thread)--> 3000@0 [size=132, length=-1]
+---
+1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
+3@1001 --(class)--> 1001@0 [size=123, length=-1]
+3@1001 --(field@4)--> 4@1000 [size=16, length=-1]
+3@1001 --(field@5)--> 5@1002 [size=32, length=-1]
+---
 [1@0 (32, 'HelloWorld'), 2@0 (16, '')]
 2
 3
index 66fc7be..6a47ca1 100644 (file)
 #include <iostream>
 #include <vector>
 
+#include "android-base/macros.h"
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
-#include "base/logging.h"
-#include "base/macros.h"
 #include "jit/jit.h"
 #include "jni.h"
 #include "native_stack_dump.h"
 #include "thread-inl.h"
 #include "thread_list.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test913Heaps {
@@ -550,7 +552,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_followReferencesString(
 
   FindStringCallbacks fsc;
   jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &fsc);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
 
@@ -648,7 +650,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_Main_followReferencesPrimitiveArray(
 
   FindArrayCallbacks fac;
   jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &fac);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(fac.data.c_str());
@@ -738,7 +740,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_Main_followReferencesPrimitiveFields(
 
   FindFieldCallbacks ffc;
   jvmtiError ret = jvmti_env->FollowReferences(0, nullptr, initial_object, &callbacks, &ffc);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return nullptr;
   }
   return env->NewStringUTF(ffc.data.c_str());
index 66f6883..10778ff 100644 (file)
@@ -28,6 +28,16 @@ public class Main {
     Runtime.getRuntime().gc();
     Runtime.getRuntime().gc();
 
+    new TestConfig(null, 0, 1, -1).doFollowReferencesTest();
+
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
+    new TestConfig(null, 0, Integer.MAX_VALUE, 1).doFollowReferencesTest();
+
+    Runtime.getRuntime().gc();
+    Runtime.getRuntime().gc();
+
     doStringTest();
 
     Runtime.getRuntime().gc();
@@ -202,6 +212,8 @@ public class Main {
   private static class TestConfig {
     private Class<?> klass = null;
     private int heapFilter = 0;
+    private int stopAfter = Integer.MAX_VALUE;
+    private int followSet = -1;
 
     public TestConfig() {
     }
@@ -209,6 +221,12 @@ public class Main {
       this.klass = klass;
       this.heapFilter = heapFilter;
     }
+    public TestConfig(Class<?> klass, int heapFilter, int stopAfter, int followSet) {
+      this.klass = klass;
+      this.heapFilter = heapFilter;
+      this.stopAfter = stopAfter;
+      this.followSet = followSet;
+    }
 
     public void doFollowReferencesTest() throws Exception {
       // Force GCs to clean up dirt.
@@ -241,8 +259,8 @@ public class Main {
       tmpStorage.add(a);
       v.add("0@0", "1@1000");  // tmpStorage[0] --(array-element)--> a.
 
-      doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, null, v, null);
-      doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, null, v, "3@1001");
+      doFollowReferencesTestImpl(null, stopAfter, followSet, null, v, null);
+      doFollowReferencesTestImpl(a.foo2, stopAfter, followSet, null, v, "3@1001");
 
       tmpStorage.clear();
     }
@@ -252,8 +270,8 @@ public class Main {
       tagClasses(v);
       A a = createTree(v);
 
-      doFollowReferencesTestImpl(null, Integer.MAX_VALUE, -1, a, v, null);
-      doFollowReferencesTestImpl(a.foo2, Integer.MAX_VALUE, -1, a, v, "3@1001");
+      doFollowReferencesTestImpl(null, stopAfter, followSet, a, v, null);
+      doFollowReferencesTestImpl(a.foo2, stopAfter, followSet, a, v, "3@1001");
     }
 
     private void doFollowReferencesTestImpl(A root, int stopAfter, int followSet,
index c659126..726c5cf 100644 (file)
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test918Fields {
index ad1431e..5263e75 100644 (file)
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "test_env.h"
 
 namespace art {
 namespace Test920Objects {
index 3fd274e..896e4c3 100644 (file)
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test922Properties {
@@ -32,7 +34,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getSystemProperties(
   jint count;
   char** properties;
   jvmtiError result = jvmti_env->GetSystemProperties(&count, &properties);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -61,7 +63,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_Main_getSystemProperty(
 
   char* value = nullptr;
   jvmtiError result = jvmti_env->GetSystemProperty(string.c_str(), &value);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -84,7 +86,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setSystemProperty(
   }
 
   jvmtiError result = jvmti_env->SetSystemProperty(key_string.c_str(), value_string.c_str());
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return;
   }
 }
index 131fc6a..6369a74 100644 (file)
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test923Monitors {
@@ -40,7 +41,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_createRawMonitor(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jrawMonitorID id;
   jvmtiError result = jvmti_env->CreateRawMonitor("dummy", &id);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return 0;
   }
   return MonitorToLong(id);
@@ -49,37 +50,37 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_createRawMonitor(
 extern "C" JNIEXPORT void JNICALL Java_Main_destroyRawMonitor(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->DestroyRawMonitor(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorEnter(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->RawMonitorEnter(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorExit(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->RawMonitorExit(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorWait(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l, jlong millis) {
   jvmtiError result = jvmti_env->RawMonitorWait(LongToMonitor(l), millis);
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotify(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->RawMonitorNotify(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_rawMonitorNotifyAll(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jlong l) {
   jvmtiError result = jvmti_env->RawMonitorNotifyAll(LongToMonitor(l));
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 }  // namespace Test923Monitors
index 14ea5af..a8b37ec 100644 (file)
 
 #include <stdio.h>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "base/macros.h"
-#include "base/logging.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
 
 namespace art {
 namespace Test924Threads {
@@ -36,7 +38,7 @@ extern "C" JNIEXPORT jthread JNICALL Java_Main_getCurrentThread(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jthread thread = nullptr;
   jvmtiError result = jvmti_env->GetCurrentThread(&thread);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
   return thread;
@@ -48,7 +50,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadInfo(
   memset(&info, 0, sizeof(jvmtiThreadInfo));
 
   jvmtiError result = jvmti_env->GetThreadInfo(thread, &info);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -94,7 +96,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getThreadState(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
   jint state;
   jvmtiError result = jvmti_env->GetThreadState(thread, &state);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return 0;
   }
   return state;
@@ -106,7 +108,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getAllThreads(
   jthread* threads;
 
   jvmtiError result = jvmti_env->GetAllThreads(&thread_count, &threads);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -124,7 +126,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_getTLS(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread) {
   void* tls;
   jvmtiError result = jvmti_env->GetThreadLocalStorage(thread, &tls);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return 0;
   }
   return static_cast<jlong>(reinterpret_cast<uintptr_t>(tls));
@@ -134,7 +136,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setTLS(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthread thread, jlong val) {
   const void* tls = reinterpret_cast<void*>(static_cast<uintptr_t>(val));
   jvmtiError result = jvmti_env->SetThreadLocalStorage(thread, tls);
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 static void JNICALL ThreadEvent(jvmtiEnv* jvmti_env,
@@ -172,13 +174,13 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableThreadEvents(
     jvmtiError ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                                          JVMTI_EVENT_THREAD_START,
                                                          nullptr);
-    if (JvmtiErrorToException(env, ret)) {
+    if (JvmtiErrorToException(env, jvmti_env, ret)) {
       return;
     }
     ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
                                               JVMTI_EVENT_THREAD_END,
                                               nullptr);
-    JvmtiErrorToException(env, ret);
+    JvmtiErrorToException(env, jvmti_env, ret);
     return;
   }
 
@@ -187,20 +189,20 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableThreadEvents(
   callbacks.ThreadStart = ThreadStart;
   callbacks.ThreadEnd = ThreadEnd;
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_THREAD_START,
                                             nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_THREAD_END,
                                             nullptr);
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 }  // namespace Test924Threads
index 2feaab0..d555553 100644 (file)
 
 #include <stdio.h>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "base/macros.h"
-#include "base/logging.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
 
 namespace art {
 namespace Test925ThreadGroups {
@@ -38,7 +40,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTopThreadGroups(
   jthreadGroup* groups;
   jint group_count;
   jvmtiError result = jvmti_env->GetTopThreadGroups(&group_count, &groups);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -56,7 +58,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupInfo(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) {
   jvmtiThreadGroupInfo info;
   jvmtiError result = jvmti_env->GetThreadGroupInfo(group, &info);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
@@ -96,7 +98,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupChildren(
                                                         &threads,
                                                         &threadgroup_count,
                                                         &groups);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
index 7b1d5c3..55d3921 100644 (file)
 
 #include <inttypes.h>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
+
 #include "jni.h"
 #include "jvmti.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
 
 namespace art {
 namespace Test926Timers {
@@ -32,7 +35,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getAvailableProcessors(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jint count;
   jvmtiError result = jvmti_env->GetAvailableProcessors(&count);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
   return count;
@@ -42,7 +45,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_getTime(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jlong time;
   jvmtiError result = jvmti_env->GetTime(&time);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return -1;
   }
   return time;
@@ -52,7 +55,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTimerInfo(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   jvmtiTimerInfo info;
   jvmtiError result = jvmti_env->GetTimerInfo(&info);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return nullptr;
   }
 
index b5c0efd..26a6707 100644 (file)
 #include "jni.h"
 #include "jvmti.h"
 
-#include "base/logging.h"
-#include "base/macros.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test927JNITable {
@@ -42,14 +43,14 @@ extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest(
     JNIEnv* env, jclass klass) {
   // Get the current table, as the delegate.
   jvmtiError getorig_result = jvmti_env->GetJNIFunctionTable(&gOriginalEnv);
-  if (JvmtiErrorToException(env, getorig_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, getorig_result)) {
     return;
   }
 
   // Get the current table, as the override we'll install.
   JNINativeInterface* env_override;
   jvmtiError getoverride_result = jvmti_env->GetJNIFunctionTable(&env_override);
-  if (JvmtiErrorToException(env, getoverride_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, getoverride_result)) {
     return;
   }
 
@@ -58,7 +59,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest(
 
   // Install the override.
   jvmtiError setoverride_result = jvmti_env->SetJNIFunctionTable(env_override);
-  if (JvmtiErrorToException(env, setoverride_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, setoverride_result)) {
     return;
   }
 
@@ -68,7 +69,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest(
 
   // Install the "original." There is no real reset.
   jvmtiError setoverride2_result = jvmti_env->SetJNIFunctionTable(gOriginalEnv);
-  if (JvmtiErrorToException(env, setoverride2_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, setoverride2_result)) {
     return;
   }
 
index ad7a053..5516105 100644 (file)
 
 #include <inttypes.h>
 
+#include "android-base/logging.h"
+#include "android-base/macros.h"
 #include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test929Search {
@@ -36,7 +37,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_addToBootClassLoader(
     return;
   }
   jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str());
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader(
@@ -46,7 +47,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader(
     return;
   }
   jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str());
-  JvmtiErrorToException(env, result);
+  JvmtiErrorToException(env, jvmti_env, result);
 }
 
 }  // namespace Test929Search
index 2e6bd46..f9af8cf 100644 (file)
  */
 
 #include <inttypes.h>
+#include <pthread.h>
 #include <sched.h>
 
-#include "barrier.h"
-#include "base/logging.h"
-#include "base/macros.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "runtime.h"
-#include "ScopedLocalRef.h"
-#include "thread-inl.h"
-#include "well_known_classes.h"
+#include "scoped_local_ref.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test930AgentThread {
@@ -36,13 +34,12 @@ namespace Test930AgentThread {
 struct AgentData {
   AgentData() : main_thread(nullptr),
                 jvmti_env(nullptr),
-                b(2),
                 priority(0) {
   }
 
   jthread main_thread;
   jvmtiEnv* jvmti_env;
-  Barrier b;
+  pthread_barrier_t b;
   jint priority;
 };
 
@@ -53,14 +50,21 @@ static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) {
   // This thread is not the main thread.
   jthread this_thread;
   jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread);
-  CHECK(!JvmtiErrorToException(env, this_thread_result));
+  CheckJvmtiError(jenv, this_thread_result);
   CHECK(!env->IsSameObject(this_thread, data->main_thread));
 
   // The thread is a daemon.
   jvmtiThreadInfo info;
   jvmtiError info_result = jenv->GetThreadInfo(this_thread, &info);
-  CHECK(!JvmtiErrorToException(env, info_result));
+  CheckJvmtiError(jenv, info_result);
   CHECK(info.is_daemon);
+  CheckJvmtiError(jenv, jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name)));
+  if (info.thread_group != nullptr) {
+    env->DeleteLocalRef(info.thread_group);
+  }
+  if (info.context_class_loader != nullptr) {
+    env->DeleteLocalRef(info.context_class_loader);
+  }
 
   // The thread has the requested priority.
   // TODO: Our thread priorities do not work on the host.
@@ -70,7 +74,7 @@ static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) {
   jint thread_count;
   jthread* threads;
   jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads);
-  CHECK(!JvmtiErrorToException(env, threads_result));
+  CheckJvmtiError(jenv, threads_result);
   bool found = false;
   for (jint i = 0; i != thread_count; ++i) {
     if (env->IsSameObject(threads[i], this_thread)) {
@@ -81,29 +85,53 @@ static void AgentMain(jvmtiEnv* jenv, JNIEnv* env, void* arg) {
   CHECK(found);
 
   // Done, let the main thread progress.
-  data->b.Pass(Thread::Current());
+  int wait_result = pthread_barrier_wait(&data->b);
+  CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   // Create a Thread object.
-  ScopedLocalRef<jobject> thread_name(env,
-                                      env->NewStringUTF("Agent Thread"));
+  ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF("Agent Thread"));
   if (thread_name.get() == nullptr) {
     return;
   }
 
-  ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
+  ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread"));
+  if (thread_klass.get() == nullptr) {
+    return;
+  }
+  ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get()));
   if (thread.get() == nullptr) {
     return;
   }
 
+  // Get a ThreadGroup from the current thread. We need a non-null one as we're gonna call a
+  // runtime-only constructor (so we can set priority and daemon state).
+  jvmtiThreadInfo cur_thread_info;
+  jvmtiError info_result = jvmti_env->GetThreadInfo(nullptr, &cur_thread_info);
+  if (JvmtiErrorToException(env, jvmti_env, info_result)) {
+    return;
+  }
+  CheckJvmtiError(jvmti_env,
+                  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(cur_thread_info.name)));
+  ScopedLocalRef<jobject> thread_group(env, cur_thread_info.thread_group);
+  if (cur_thread_info.context_class_loader != nullptr) {
+    env->DeleteLocalRef(cur_thread_info.context_class_loader);
+  }
+
+  jmethodID initID = env->GetMethodID(thread_klass.get(),
+                                      "<init>",
+                                      "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+  if (initID == nullptr) {
+    return;
+  }
   env->CallNonvirtualVoidMethod(thread.get(),
-                                WellKnownClasses::java_lang_Thread,
-                                WellKnownClasses::java_lang_Thread_init,
-                                Runtime::Current()->GetMainThreadGroup(),
+                                thread_klass.get(),
+                                initID,
+                                thread_group.get(),
                                 thread_name.get(),
-                                kMinThreadPriority,
+                                0,
                                 JNI_FALSE);
   if (env->ExceptionCheck()) {
     return;
@@ -111,7 +139,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread(
 
   jthread main_thread;
   jvmtiError main_thread_result = jvmti_env->GetCurrentThread(&main_thread);
-  if (JvmtiErrorToException(env, main_thread_result)) {
+  if (JvmtiErrorToException(env, jvmti_env, main_thread_result)) {
     return;
   }
 
@@ -119,21 +147,23 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread(
   data.main_thread = env->NewGlobalRef(main_thread);
   data.jvmti_env = jvmti_env;
   data.priority = JVMTI_THREAD_MIN_PRIORITY;
+  CHECK_EQ(0, pthread_barrier_init(&data.b, nullptr, 2));
 
   jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority);
-  if (JvmtiErrorToException(env, result)) {
+  if (JvmtiErrorToException(env, jvmti_env, result)) {
     return;
   }
 
-  data.b.Wait(Thread::Current());
+  int wait_result = pthread_barrier_wait(&data.b);
+  CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0);
 
   // Scheduling may mean that the agent thread is put to sleep. Wait until it's dead in an effort
   // to not unload the plugin and crash.
   for (;;) {
-    NanoSleep(1000 * 1000);
+    sleep(1);
     jint thread_state;
     jvmtiError state_result = jvmti_env->GetThreadState(thread.get(), &thread_state);
-    if (JvmtiErrorToException(env, state_result)) {
+    if (JvmtiErrorToException(env, jvmti_env, state_result)) {
       return;
     }
     if (thread_state == 0 ||                                    // Was never alive.
@@ -143,9 +173,11 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread(
   }
   // Yield and sleep a bit more, to give the plugin time to tear down the native thread structure.
   sched_yield();
-  NanoSleep(100 * 1000 * 1000);
+  sleep(1);
 
   env->DeleteGlobalRef(data.main_thread);
+
+  pthread_barrier_destroy(&data.b);
 }
 
 }  // namespace Test930AgentThread
index 7043350..2b74c40 100644 (file)
 #include <signal.h>
 #include <sys/types.h>
 
-#include "base/logging.h"
-#include "base/macros.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test933MiscEvents {
@@ -42,14 +43,14 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testSigQuit(
   memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
   callbacks.DataDumpRequest = DumpRequestCallback;
   jvmtiError ret = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 
   ret = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
                                             JVMTI_EVENT_DATA_DUMP_REQUEST,
                                             nullptr);
-  if (JvmtiErrorToException(env, ret)) {
+  if (JvmtiErrorToException(env, jvmti_env, ret)) {
     return;
   }
 
@@ -65,7 +66,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_testSigQuit(
   }
 
   ret = jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_DATA_DUMP_REQUEST, nullptr);
-  JvmtiErrorToException(env, ret);
+  JvmtiErrorToException(env, jvmti_env, ret);
 }
 
 }  // namespace Test933MiscEvents
index 3b19ca5..b2ef056 100644 (file)
 #include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test936SearchOnload {
index 7cb3c08..698e023 100644 (file)
  * limitations under the License.
  */
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
 #include "mirror/class-inl.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "test_env.h"
 
 namespace art {
 namespace Test944TransformClassloaders {
 
-
 extern "C" JNIEXPORT jlong JNICALL Java_Main_getDexFilePointer(JNIEnv* env, jclass, jclass klass) {
   if (Runtime::Current() == nullptr) {
     env->ThrowNew(env->FindClass("java/lang/Exception"),
index 442836b..ee653a4 100644 (file)
 #include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test945ObsoleteNative {
index daae087..1faf1a1 100644 (file)
 #include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test980RedefineObjects {
@@ -39,9 +41,11 @@ extern "C" JNIEXPORT void JNICALL Java_art_test_TestWatcher_NotifyConstructed(
     JNIEnv* env, jclass TestWatcherClass ATTRIBUTE_UNUSED, jobject constructed) {
   char* sig = nullptr;
   char* generic_sig = nullptr;
-  if (JvmtiErrorToException(env, jvmti_env->GetClassSignature(env->GetObjectClass(constructed),
-                                                              &sig,
-                                                              &generic_sig))) {
+  if (JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->GetClassSignature(env->GetObjectClass(constructed),
+                                                         &sig,
+                                                         &generic_sig))) {
     // Exception.
     return;
   }
index cd3f007..1e063cf 100644 (file)
 
 import java.lang.reflect.Field;
 import java.util.Base64;
+import java.nio.ByteBuffer;
 
 import dalvik.system.ClassExt;
+import dalvik.system.InMemoryDexClassLoader;
 
 public class Main {
 
@@ -67,6 +69,53 @@ public class Main {
     "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
     "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA");
 
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform3 {
+   *   public void sayHi() {
+   *    System.out.println("hello3");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES_3_INITIAL = Base64.getDecoder().decode(
+    "ZGV4CjAzNQC2W2fBsAeLNAwWYlG8FVigzfsV7nBWITzQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
+    "AABqAQAAeAEAAI8BAACjAQAAtwEAAMsBAADcAQAA3wEAAOMBAAD3AQAA/wEAAAQCAAANAgAAAQAA" +
+    "AAIAAAADAAAABAAAAAUAAAAHAAAABwAAAAUAAAAAAAAACAAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAB8CAAAA" +
+    "AAAAAQABAAEAAAAUAgAABAAAAHAQAwAAAA4AAwABAAIAAAAZAgAACQAAAGIAAAAbAQoAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAMTFRyYW5zZm9ybTM7ABVMamF2YS9pby9QcmludFN0cmVhbTsA" +
+    "EkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3Rl" +
+    "bTsAD1RyYW5zZm9ybTMuamF2YQABVgACVkwAEmVtaXR0ZXI6IGphY2stNC4zMAAGaGVsbG8zAANv" +
+    "dXQAB3ByaW50bG4ABXNheUhpAAIABw4ABAAHDocAAAABAQCAgASgAgEBuAIAAAANAAAAAAAAAAEA" +
+    "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
+    "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
+    "AyAAAAIAAAAUAgAAACAAAAEAAAAfAgAAABAAAAEAAAAwAgAA");
+
+  /**
+   * base64 encoded class/dex file for
+   * class Transform3 {
+   *   public void sayHi() {
+   *    System.out.println("Goodbye3");
+   *   }
+   * }
+   */
+  private static final byte[] DEX_BYTES_3_FINAL = Base64.getDecoder().decode(
+    "ZGV4CjAzNQBAXE5GthgMydaFBuinf+ZBfXcBYIw2UlXQAgAAcAAAAHhWNBIAAAAAAAAAADACAAAO" +
+    "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACwAQAAIAEAAGIB" +
+    "AABqAQAAdAEAAIIBAACZAQAArQEAAMEBAADVAQAA5gEAAOkBAADtAQAAAQIAAAYCAAAPAgAAAgAA" +
+    "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+    "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAACECAAAA" +
+    "AAAAAQABAAEAAAAWAgAABAAAAHAQAwAAAA4AAwABAAIAAAAbAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+    "EAAOAAAAAQAAAAMABjxpbml0PgAIR29vZGJ5ZTMADExUcmFuc2Zvcm0zOwAVTGphdmEvaW8vUHJp" +
+    "bnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEv" +
+    "bGFuZy9TeXN0ZW07AA9UcmFuc2Zvcm0zLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTQuMzAA" +
+    "A291dAAHcHJpbnRsbgAFc2F5SGkAAgAHDgAEAAcOhwAAAAEBAICABKACAQG4AgANAAAAAAAAAAEA" +
+    "AAAAAAAAAQAAAA4AAABwAAAAAgAAAAYAAACoAAAAAwAAAAIAAADAAAAABAAAAAEAAADYAAAABQAA" +
+    "AAQAAADgAAAABgAAAAEAAAAAAQAAASAAAAIAAAAgAQAAARAAAAEAAABcAQAAAiAAAA4AAABiAQAA" +
+    "AyAAAAIAAAAWAgAAACAAAAEAAAAhAgAAABAAAAEAAAAwAgAA");
+
   public static void main(String[] args) {
     try {
       doTest();
@@ -125,6 +174,20 @@ public class Main {
     enableCommonRetransformation(false);
     doCommonClassRedefinition(Transform.class, new byte[0], DEX_BYTES_1);
     assertSame((new byte[0]).getClass(), getOriginalDexFile(t1.getClass()).getClass());
+
+    // Check we don't have anything if we don't have any originalDexFile if the onload
+    // transformation doesn't do anything.
+    enableCommonRetransformation(true);
+    Class<?> transform3Class = new InMemoryDexClassLoader(
+        ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Main.class.getClassLoader()).loadClass("Transform3");
+    assertSame(null, getOriginalDexFile(transform3Class));
+
+    // Check that we end up with a java.lang.Long pointer if we do an 'on-load' redefinition.
+    addCommonTransformationResult("Transform3", new byte[0], DEX_BYTES_3_FINAL);
+    enableCommonRetransformation(true);
+    Class<?> transform3ClassTransformed = new InMemoryDexClassLoader(
+        ByteBuffer.wrap(DEX_BYTES_3_INITIAL), Main.class.getClassLoader()).loadClass("Transform3");
+    assertSame(Long.class, getOriginalDexFile(transform3ClassTransformed).getClass());
   }
 
   // Transforms the class
index e8b7e13..3ef3c7c 100644 (file)
@@ -37,8 +37,9 @@
 #include "thread-inl.h"
 #include "thread_list.h"
 
-#include "ti-agent/common_helper.h"
-#include "ti-agent/common_load.h"
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 namespace art {
 namespace Test983SourceTransformVerify {
index ec40acf..40f7edd 100644 (file)
@@ -241,26 +241,24 @@ art_cc_test_library {
 }
 
 art_cc_defaults {
-    name: "libtiagent-defaults",
+   name: "libtiagent-base-defaults",
     defaults: ["libartagent-defaults"],
     srcs: [
-        // This is to get the IsInterpreted native method.
-        "common/stack_inspect.cc",
-        "common/runtime_state.cc",
-        "ti-agent/common_load.cc",
-        "ti-agent/common_helper.cc",
-        "901-hello-ti-agent/basics.cc",
+        // These are the ART-independent parts.
+        "ti-agent/agent_startup.cc",
+        "ti-agent/jni_binder.cc",
+        "ti-agent/jvmti_helper.cc",
+        "ti-agent/test_env.cc",
+        // This is the list of non-special OnLoad things and excludes BCI and anything that depends
+        // on ART internals.
         "903-hello-tagging/tagging.cc",
         "904-object-allocation/tracking.cc",
         "905-object-free/tracking_free.cc",
         "906-iterate-heap/iterate_heap.cc",
         "907-get-loaded-classes/get_loaded_classes.cc",
         "908-gc-start-finish/gc_callbacks.cc",
-        "909-attach-agent/attach.cc",
         "910-methods/methods.cc",
         "911-get-stack-trace/stack_trace.cc",
-        "912-classes/classes.cc",
-        "913-heaps/heaps.cc",
         "918-fields/fields.cc",
         "920-objects/objects.cc",
         "922-properties/properties.cc",
@@ -272,16 +270,35 @@ art_cc_defaults {
         "929-search/search.cc",
         "931-agent-thread/agent_thread.cc",
         "933-misc-events/misc_events.cc",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    header_libs: ["libopenjdkjvmti_headers"],
+    include_dirs: ["art/test/ti-agent"],
+}
+
+art_cc_defaults {
+    name: "libtiagent-defaults",
+    defaults: ["libtiagent-base-defaults"],
+    srcs: [
+        // This is to get the IsInterpreted native method.
+        "common/stack_inspect.cc",
+        "common/runtime_state.cc",
+        // This includes the remaining test functions. We should try to refactor things to
+        // make this list smaller.
+        "ti-agent/common_helper.cc",
+        "ti-agent/common_load.cc",
+        "901-hello-ti-agent/basics.cc",
+        "909-attach-agent/attach.cc",
+        "912-classes/classes.cc",
+        "913-heaps/heaps.cc",
         "936-search-onload/search_onload.cc",
         "944-transform-classloaders/classloader.cc",
         "945-obsolete-native/obsolete_native.cc",
         "980-redefine-object/redefine_object.cc",
         "983-source-transform-verify/source_transform.cc",
     ],
-    shared_libs: [
-        "libbase",
-    ],
-    header_libs: ["libopenjdkjvmti_headers"],
 }
 
 art_cc_test_library {
@@ -299,6 +316,12 @@ art_cc_test_library {
     shared_libs: ["libartd"],
 }
 
+art_cc_test_library {
+    name: "libctstiagent",
+    defaults: ["libtiagent-base-defaults"],
+    export_include_dirs: ["ti-agent"],
+}
+
 cc_defaults {
     name: "libarttest-defaults",
     defaults: [
index a7b28de..7891d4c 100644 (file)
         "variant": "interp-ac"
     },
     {
-        "tests": ["638-checker-inline-caches",
-                  "644-checker-deopt"],
-        "description": ["Disabled temporarily until a fix arrives."],
-        "bug": "http://b/36371709"
-    },
-    {
         "tests": ["629-vdex-speed",
-                  "634-vdex-duplicate",
-                  "983-source-transform-verify"],
+                  "634-vdex-duplicate"],
         "description": ["Profile driven dexlayout does not work with vdex or dex verifier."],
         "variant": "speed-profile"
     }
diff --git a/test/ti-agent/agent_startup.cc b/test/ti-agent/agent_startup.cc
new file mode 100644 (file)
index 0000000..b55db7b
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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 "agent_startup.h"
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+
+namespace art {
+
+static constexpr const char* kMainClass = "Main";
+
+static StartCallback gCallback = nullptr;
+
+// TODO: Check this. This may not work on device. The classloader containing the app's classes
+//       may not have been created at this point (i.e., if it's not the system classloader).
+static void JNICALL VMInitCallback(jvmtiEnv* jvmti_env,
+                                   JNIEnv* jni_env,
+                                   jthread thread ATTRIBUTE_UNUSED) {
+  // Bind kMainClass native methods.
+  BindFunctions(jvmti_env, jni_env, kMainClass);
+
+  if (gCallback != nullptr) {
+    gCallback(jvmti_env, jni_env);
+    gCallback = nullptr;
+  }
+
+  // And delete the jvmtiEnv.
+  jvmti_env->DisposeEnvironment();
+}
+
+// Install a phase callback that will bind JNI functions on VMInit.
+void BindOnLoad(JavaVM* vm, StartCallback callback) {
+  // Use a new jvmtiEnv. Otherwise we might collide with table changes.
+  jvmtiEnv* install_env;
+  if (vm->GetEnv(reinterpret_cast<void**>(&install_env), JVMTI_VERSION_1_0) != 0) {
+    LOG(FATAL) << "Could not get jvmtiEnv";
+  }
+  SetAllCapabilities(install_env);
+
+  {
+    jvmtiEventCallbacks callbacks;
+    memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+    callbacks.VMInit = VMInitCallback;
+
+    CheckJvmtiError(install_env, install_env->SetEventCallbacks(&callbacks, sizeof(callbacks)));
+  }
+
+  CheckJvmtiError(install_env, install_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                                                     JVMTI_EVENT_VM_INIT,
+                                                                     nullptr));
+
+  gCallback = callback;
+}
+
+// Ensure binding of the Main class when the agent is started through OnAttach.
+void BindOnAttach(JavaVM* vm, StartCallback callback) {
+  // Get a JNIEnv. As the thread is attached, we must not destroy it.
+  JNIEnv* env;
+  CHECK_EQ(0, vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6))
+      << "Could not get JNIEnv";
+
+  jvmtiEnv* jvmti_env;
+  CHECK_EQ(0, vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0))
+      << "Could not get jvmtiEnv";
+  SetAllCapabilities(jvmti_env);
+
+  BindFunctions(jvmti_env, env, kMainClass);
+
+  if (callback != nullptr) {
+    callback(jvmti_env, env);
+  }
+
+  if (jvmti_env->DisposeEnvironment() != JVMTI_ERROR_NONE) {
+    LOG(FATAL) << "Could not dispose temporary jvmtiEnv";
+  }
+}
+
+}  // namespace art
diff --git a/test/ti-agent/agent_startup.h b/test/ti-agent/agent_startup.h
new file mode 100644 (file)
index 0000000..4963320
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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_TEST_TI_AGENT_AGENT_STARTUP_H_
+#define ART_TEST_TI_AGENT_AGENT_STARTUP_H_
+
+#include <functional>
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace art {
+
+using StartCallback = void(*)(jvmtiEnv*, JNIEnv*);
+
+// Ensure binding of the Main class when the agent is started through OnLoad.
+void BindOnLoad(JavaVM* vm, StartCallback callback);
+
+// Ensure binding of the Main class when the agent is started through OnAttach.
+void BindOnAttach(JavaVM* vm, StartCallback callback);
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_AGENT_STARTUP_H_
index 6316a9c..ab5dbcc 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "ti-agent/common_helper.h"
+#include "common_helper.h"
 
 #include <dlfcn.h>
 #include <stdio.h>
 #include "jni_internal.h"
 #include "jvmti.h"
 #include "scoped_thread_state_change-inl.h"
-#include "ScopedLocalRef.h"
 #include "stack.h"
-#include "ti-agent/common_load.h"
 #include "utils.h"
 
-namespace art {
-bool RuntimeIsJVM;
-
-bool IsJVM() {
-  return RuntimeIsJVM;
-}
-
-void SetAllCapabilities(jvmtiEnv* env) {
-  jvmtiCapabilities caps;
-  env->GetPotentialCapabilities(&caps);
-  env->AddCapabilities(&caps);
-}
-
-bool JvmtiErrorToException(JNIEnv* env, jvmtiError error) {
-  if (error == JVMTI_ERROR_NONE) {
-    return false;
-  }
-
-  ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
-  if (rt_exception.get() == nullptr) {
-    // CNFE should be pending.
-    return true;
-  }
-
-  char* err;
-  jvmti_env->GetErrorName(error, &err);
-
-  env->ThrowNew(rt_exception.get(), err);
-
-  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err));
-  return true;
-}
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "test_env.h"
 
+namespace art {
 
 template <bool is_redefine>
 static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
@@ -303,7 +274,7 @@ extern "C" JNIEXPORT void Java_Main_enableCommonRetransformation(JNIEnv* env,
                                                        JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
                                                        nullptr);
   if (res != JVMTI_ERROR_NONE) {
-    JvmtiErrorToException(env, res);
+    JvmtiErrorToException(env, jvmti_env, res);
   }
 }
 
@@ -412,140 +383,4 @@ jint OnLoad(JavaVM* vm,
 
 }  // namespace common_transform
 
-static void BindMethod(jvmtiEnv* jenv,
-                       JNIEnv* env,
-                       jclass klass,
-                       jmethodID method) {
-  char* name;
-  char* signature;
-  jvmtiError name_result = jenv->GetMethodName(method, &name, &signature, nullptr);
-  if (name_result != JVMTI_ERROR_NONE) {
-    LOG(FATAL) << "Could not get methods";
-  }
-
-  std::string names[2];
-  if (IsJVM()) {
-    // TODO Get the JNI long name
-    char* klass_name;
-    jvmtiError klass_result = jenv->GetClassSignature(klass, &klass_name, nullptr);
-    if (klass_result == JVMTI_ERROR_NONE) {
-      std::string name_str(name);
-      std::string klass_str(klass_name);
-      names[0] = GetJniShortName(klass_str, name_str);
-      jenv->Deallocate(reinterpret_cast<unsigned char*>(klass_name));
-    } else {
-      LOG(FATAL) << "Could not get class name!";
-    }
-  } else {
-    ScopedObjectAccess soa(Thread::Current());
-    ArtMethod* m = jni::DecodeArtMethod(method);
-    names[0] = m->JniShortName();
-    names[1] = m->JniLongName();
-  }
-  for (const std::string& mangled_name : names) {
-    if (mangled_name == "") {
-      continue;
-    }
-    void* sym = dlsym(RTLD_DEFAULT, mangled_name.c_str());
-    if (sym == nullptr) {
-      continue;
-    }
-
-    JNINativeMethod native_method;
-    native_method.fnPtr = sym;
-    native_method.name = name;
-    native_method.signature = signature;
-
-    env->RegisterNatives(klass, &native_method, 1);
-
-    jenv->Deallocate(reinterpret_cast<unsigned char*>(name));
-    jenv->Deallocate(reinterpret_cast<unsigned char*>(signature));
-    return;
-  }
-
-  LOG(FATAL) << "Could not find " << names[0];
-}
-
-static jclass FindClassWithSystemClassLoader(JNIEnv* env, const char* class_name) {
-  // Find the system classloader.
-  ScopedLocalRef<jclass> cl_klass(env, env->FindClass("java/lang/ClassLoader"));
-  if (cl_klass.get() == nullptr) {
-    return nullptr;
-  }
-  jmethodID getsystemclassloader_method = env->GetStaticMethodID(cl_klass.get(),
-                                                                 "getSystemClassLoader",
-                                                                 "()Ljava/lang/ClassLoader;");
-  if (getsystemclassloader_method == nullptr) {
-    return nullptr;
-  }
-  ScopedLocalRef<jobject> cl(env, env->CallStaticObjectMethod(cl_klass.get(),
-                                                              getsystemclassloader_method));
-  if (cl.get() == nullptr) {
-    return nullptr;
-  }
-
-  // Create a String of the name.
-  std::string descriptor = android::base::StringPrintf("L%s;", class_name);
-  std::string dot_name = DescriptorToDot(descriptor.c_str());
-  ScopedLocalRef<jstring> name_str(env, env->NewStringUTF(dot_name.c_str()));
-
-  // Call Class.forName with it.
-  ScopedLocalRef<jclass> c_klass(env, env->FindClass("java/lang/Class"));
-  if (c_klass.get() == nullptr) {
-    return nullptr;
-  }
-  jmethodID forname_method = env->GetStaticMethodID(
-      c_klass.get(),
-      "forName",
-      "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
-  if (forname_method == nullptr) {
-    return nullptr;
-  }
-
-  return reinterpret_cast<jclass>(env->CallStaticObjectMethod(c_klass.get(),
-                                                              forname_method,
-                                                              name_str.get(),
-                                                              JNI_FALSE,
-                                                              cl.get()));
-}
-
-void BindFunctions(jvmtiEnv* jenv, JNIEnv* env, const char* class_name) {
-  // Use JNI to load the class.
-  ScopedLocalRef<jclass> klass(env, env->FindClass(class_name));
-  if (klass.get() == nullptr) {
-    // We may be called with the wrong classloader. Try explicitly using the system classloader.
-    env->ExceptionClear();
-    klass.reset(FindClassWithSystemClassLoader(env, class_name));
-    if (klass.get() == nullptr) {
-      LOG(FATAL) << "Could not load " << class_name;
-    }
-  }
-  BindFunctionsOnClass(jenv, env, klass.get());
-}
-
-void BindFunctionsOnClass(jvmtiEnv* jenv, JNIEnv* env, jclass klass) {
-  // Use JVMTI to get the methods.
-  jint method_count;
-  jmethodID* methods;
-  jvmtiError methods_result = jenv->GetClassMethods(klass, &method_count, &methods);
-  if (methods_result != JVMTI_ERROR_NONE) {
-    LOG(FATAL) << "Could not get methods";
-  }
-
-  // Check each method.
-  for (jint i = 0; i < method_count; ++i) {
-    jint modifiers;
-    jvmtiError mod_result = jenv->GetMethodModifiers(methods[i], &modifiers);
-    if (mod_result != JVMTI_ERROR_NONE) {
-      LOG(FATAL) << "Could not get methods";
-    }
-    constexpr jint kNative = static_cast<jint>(kAccNative);
-    if ((modifiers & kNative) != 0) {
-      BindMethod(jenv, env, klass, methods[i]);
-    }
-  }
-
-  jenv->Deallocate(reinterpret_cast<unsigned char*>(methods));
-}
-
 }  // namespace art
index f10356d..610019e 100644 (file)
 
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
 
 namespace art {
-namespace common_redefine {
 
+namespace common_redefine {
 jint OnLoad(JavaVM* vm, char* options, void* reserved);
-
 }  // namespace common_redefine
 
 namespace common_retransform {
@@ -36,53 +34,6 @@ namespace common_transform {
 jint OnLoad(JavaVM* vm, char* options, void* reserved);
 }  // namespace common_transform
 
-
-extern bool RuntimeIsJVM;
-
-bool IsJVM();
-
-template <typename T>
-static jobjectArray CreateObjectArray(JNIEnv* env,
-                                      jint length,
-                                      const char* component_type_descriptor,
-                                      T src) {
-  if (length < 0) {
-    return nullptr;
-  }
-
-  ScopedLocalRef<jclass> obj_class(env, env->FindClass(component_type_descriptor));
-  if (obj_class.get() == nullptr) {
-    return nullptr;
-  }
-
-  ScopedLocalRef<jobjectArray> ret(env, env->NewObjectArray(length, obj_class.get(), nullptr));
-  if (ret.get() == nullptr) {
-    return nullptr;
-  }
-
-  for (jint i = 0; i < length; ++i) {
-    jobject element = src(i);
-    env->SetObjectArrayElement(ret.get(), static_cast<jint>(i), element);
-    env->DeleteLocalRef(element);
-    if (env->ExceptionCheck()) {
-      return nullptr;
-    }
-  }
-
-  return ret.release();
-}
-
-void SetAllCapabilities(jvmtiEnv* env);
-
-bool JvmtiErrorToException(JNIEnv* env, jvmtiError error);
-
-// Load the class through JNI. Inspect it, find all native methods. Construct the corresponding
-// mangled name, run dlsym and bind the method.
-//
-// This will abort on failure.
-void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name);
-void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass);
-
 }  // namespace art
 
 #endif  // ART_TEST_TI_AGENT_COMMON_HELPER_H_
index 303e824..9e7b75d 100644 (file)
  * limitations under the License.
  */
 
-#include "common_load.h"
-
 #include <jni.h>
 #include <stdio.h>
 
-#include "art_method-inl.h"
 #include "base/logging.h"
 #include "base/macros.h"
+
+#include "agent_startup.h"
 #include "common_helper.h"
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
 
 #include "901-hello-ti-agent/basics.h"
 #include "909-attach-agent/attach.h"
@@ -31,8 +33,6 @@
 
 namespace art {
 
-jvmtiEnv* jvmti_env;
-
 namespace {
 
 using OnLoad   = jint (*)(JavaVM* vm, char* options, void* reserved);
@@ -44,45 +44,6 @@ struct AgentLib {
   OnAttach attach;
 };
 
-static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env,
-                                   JNIEnv* jni_env,
-                                   jthread thread ATTRIBUTE_UNUSED) {
-  // Bind Main native methods.
-  BindFunctions(jvmti_env, jni_env, "Main");
-}
-
-// Install a phase callback that will bind JNI functions on VMInit.
-bool InstallBindCallback(JavaVM* vm) {
-  // Use a new jvmtiEnv. Otherwise we might collide with table changes.
-  jvmtiEnv* install_env;
-  if (vm->GetEnv(reinterpret_cast<void**>(&install_env), JVMTI_VERSION_1_0) != 0) {
-    return false;
-  }
-  SetAllCapabilities(install_env);
-
-  {
-    jvmtiEventCallbacks callbacks;
-    memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
-    callbacks.VMInit = VMInitCallback;
-
-    jvmtiError install_error = install_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-    if (install_error != JVMTI_ERROR_NONE) {
-      return false;
-    }
-  }
-
-  {
-    jvmtiError enable_error = install_env->SetEventNotificationMode(JVMTI_ENABLE,
-                                                                    JVMTI_EVENT_VM_INIT,
-                                                                    nullptr);
-    if (enable_error != JVMTI_ERROR_NONE) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
 // A trivial OnLoad implementation that only initializes the global jvmti_env.
 static jint MinimalOnLoad(JavaVM* vm,
                           char* options ATTRIBUTE_UNUSED,
@@ -154,28 +115,8 @@ static bool FindAgentNameAndOptions(char* options,
   return true;
 }
 
-static void SetIsJVM(char* options) {
-  RuntimeIsJVM = strncmp(options, "jvm", 3) == 0;
-}
-
-static bool BindFunctionsAttached(JavaVM* vm, const char* class_name) {
-  // Get a JNIEnv. As the thread is attached, we must not destroy it.
-  JNIEnv* env;
-  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != 0) {
-    printf("Unable to get JNI env!\n");
-    return false;
-  }
-
-  jvmtiEnv* jenv;
-  if (vm->GetEnv(reinterpret_cast<void**>(&jenv), JVMTI_VERSION_1_0) != 0) {
-    printf("Unable to get jvmti env!\n");
-    return false;
-  }
-  SetAllCapabilities(jenv);
-
-  BindFunctions(jenv, env, class_name);
-
-  return true;
+static void SetIsJVM(const char* options) {
+  SetJVM(strncmp(options, "jvm", 3) == 0);
 }
 
 }  // namespace
@@ -190,9 +131,7 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void*
 
   SetIsJVM(remaining_options);
 
-  if (!InstallBindCallback(vm)) {
-    return 1;
-  }
+  BindOnLoad(vm, nullptr);
 
   AgentLib* lib = FindAgent(name_option);
   OnLoad fn = nullptr;
@@ -216,7 +155,7 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void
     return -1;
   }
 
-  BindFunctionsAttached(vm, "Main");
+  BindOnAttach(vm, nullptr);
 
   AgentLib* lib = FindAgent(name_option);
   if (lib == nullptr) {
diff --git a/test/ti-agent/jni_binder.cc b/test/ti-agent/jni_binder.cc
new file mode 100644 (file)
index 0000000..b66c2c7
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2017 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 "jni_binder.h"
+
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jvmti_helper.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
+#include "ti_utf.h"
+
+namespace art {
+
+static std::string MangleForJni(const std::string& s) {
+  std::string result;
+  size_t char_count = ti::CountModifiedUtf8Chars(s.c_str(), s.length());
+  const char* cp = &s[0];
+  for (size_t i = 0; i < char_count; ++i) {
+    uint32_t ch = ti::GetUtf16FromUtf8(&cp);
+    if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
+      result.push_back(ch);
+    } else if (ch == '.' || ch == '/') {
+      result += "_";
+    } else if (ch == '_') {
+      result += "_1";
+    } else if (ch == ';') {
+      result += "_2";
+    } else if (ch == '[') {
+      result += "_3";
+    } else {
+      const uint16_t leading = ti::GetLeadingUtf16Char(ch);
+      const uint32_t trailing = ti::GetTrailingUtf16Char(ch);
+
+      android::base::StringAppendF(&result, "_0%04x", leading);
+      if (trailing != 0) {
+        android::base::StringAppendF(&result, "_0%04x", trailing);
+      }
+    }
+  }
+  return result;
+}
+
+static std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
+  // Remove the leading 'L' and trailing ';'...
+  std::string class_name(class_descriptor);
+  CHECK_EQ(class_name[0], 'L') << class_name;
+  CHECK_EQ(class_name[class_name.size() - 1], ';') << class_name;
+  class_name.erase(0, 1);
+  class_name.erase(class_name.size() - 1, 1);
+
+  std::string short_name;
+  short_name += "Java_";
+  short_name += MangleForJni(class_name);
+  short_name += "_";
+  short_name += MangleForJni(method);
+  return short_name;
+}
+
+static void BindMethod(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass, jmethodID method) {
+  std::string name;
+  std::string signature;
+  std::string mangled_names[2];
+  {
+    char* name_cstr;
+    char* sig_cstr;
+    jvmtiError name_result = jvmti_env->GetMethodName(method, &name_cstr, &sig_cstr, nullptr);
+    CheckJvmtiError(jvmti_env, name_result);
+    CHECK(name_cstr != nullptr);
+    CHECK(sig_cstr != nullptr);
+    name = name_cstr;
+    signature = sig_cstr;
+
+    char* klass_name;
+    jvmtiError klass_result = jvmti_env->GetClassSignature(klass, &klass_name, nullptr);
+    CheckJvmtiError(jvmti_env, klass_result);
+
+    mangled_names[0] = GetJniShortName(klass_name, name);
+    // TODO: Long JNI name.
+
+    CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, name_cstr));
+    CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, sig_cstr));
+    CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, klass_name));
+  }
+
+  for (const std::string& mangled_name : mangled_names) {
+    if (mangled_name.empty()) {
+      continue;
+    }
+    void* sym = dlsym(RTLD_DEFAULT, mangled_name.c_str());
+    if (sym == nullptr) {
+      continue;
+    }
+
+    JNINativeMethod native_method;
+    native_method.fnPtr = sym;
+    native_method.name = name.c_str();
+    native_method.signature = signature.c_str();
+
+    env->RegisterNatives(klass, &native_method, 1);
+
+    return;
+  }
+
+  LOG(FATAL) << "Could not find " << mangled_names[0];
+}
+
+static std::string DescriptorToDot(const char* descriptor) {
+  size_t length = strlen(descriptor);
+  if (length > 1) {
+    if (descriptor[0] == 'L' && descriptor[length - 1] == ';') {
+      // Descriptors have the leading 'L' and trailing ';' stripped.
+      std::string result(descriptor + 1, length - 2);
+      std::replace(result.begin(), result.end(), '/', '.');
+      return result;
+    } else {
+      // For arrays the 'L' and ';' remain intact.
+      std::string result(descriptor);
+      std::replace(result.begin(), result.end(), '/', '.');
+      return result;
+    }
+  }
+  // Do nothing for non-class/array descriptors.
+  return descriptor;
+}
+
+static jobject GetSystemClassLoader(JNIEnv* env) {
+  ScopedLocalRef<jclass> cl_klass(env, env->FindClass("java/lang/ClassLoader"));
+  CHECK(cl_klass.get() != nullptr);
+  jmethodID getsystemclassloader_method = env->GetStaticMethodID(cl_klass.get(),
+                                                                 "getSystemClassLoader",
+                                                                 "()Ljava/lang/ClassLoader;");
+  CHECK(getsystemclassloader_method != nullptr);
+  return env->CallStaticObjectMethod(cl_klass.get(), getsystemclassloader_method);
+}
+
+static jclass FindClassWithClassLoader(JNIEnv* env, const char* class_name, jobject class_loader) {
+  // Create a String of the name.
+  std::string descriptor = android::base::StringPrintf("L%s;", class_name);
+  std::string dot_name = DescriptorToDot(descriptor.c_str());
+  ScopedLocalRef<jstring> name_str(env, env->NewStringUTF(dot_name.c_str()));
+
+  // Call Class.forName with it.
+  ScopedLocalRef<jclass> c_klass(env, env->FindClass("java/lang/Class"));
+  CHECK(c_klass.get() != nullptr);
+  jmethodID forname_method = env->GetStaticMethodID(
+      c_klass.get(),
+      "forName",
+      "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+  CHECK(forname_method != nullptr);
+
+  return static_cast<jclass>(env->CallStaticObjectMethod(c_klass.get(),
+                                                         forname_method,
+                                                         name_str.get(),
+                                                         JNI_FALSE,
+                                                         class_loader));
+}
+
+// Find the given classname. First try the implied classloader, then the system classloader,
+// then use JVMTI to find all classloaders.
+static jclass FindClass(jvmtiEnv* jvmti_env,
+                        JNIEnv* env,
+                        const char* class_name,
+                        jobject class_loader) {
+  if (class_loader != nullptr) {
+    return FindClassWithClassLoader(env, class_name, class_loader);
+  }
+
+  jclass from_implied = env->FindClass(class_name);
+  if (from_implied != nullptr) {
+    return from_implied;
+  }
+  env->ExceptionClear();
+
+  ScopedLocalRef<jobject> system_class_loader(env, GetSystemClassLoader(env));
+  CHECK(system_class_loader.get() != nullptr);
+  jclass from_system = FindClassWithClassLoader(env, class_name, system_class_loader.get());
+  if (from_system != nullptr) {
+    return from_system;
+  }
+  env->ExceptionClear();
+
+  // Look at the context classloaders of all threads.
+  jint thread_count;
+  jthread* threads;
+  CheckJvmtiError(jvmti_env, jvmti_env->GetAllThreads(&thread_count, &threads));
+  JvmtiUniquePtr threads_uptr = MakeJvmtiUniquePtr(jvmti_env, threads);
+
+  jclass result = nullptr;
+  for (jint t = 0; t != thread_count; ++t) {
+    // Always loop over all elements, as we need to free the local references.
+    if (result == nullptr) {
+      jvmtiThreadInfo info;
+      CheckJvmtiError(jvmti_env, jvmti_env->GetThreadInfo(threads[t], &info));
+      CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, info.name));
+      if (info.thread_group != nullptr) {
+        env->DeleteLocalRef(info.thread_group);
+      }
+      if (info.context_class_loader != nullptr) {
+        result = FindClassWithClassLoader(env, class_name, info.context_class_loader);
+        env->ExceptionClear();
+        env->DeleteLocalRef(info.context_class_loader);
+      }
+    }
+    env->DeleteLocalRef(threads[t]);
+  }
+
+  if (result != nullptr) {
+    return result;
+  }
+
+  // TODO: Implement scanning *all* classloaders.
+  LOG(FATAL) << "Unimplemented";
+
+  return nullptr;
+}
+
+void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass) {
+  // Use JVMTI to get the methods.
+  jint method_count;
+  jmethodID* methods;
+  jvmtiError methods_result = jvmti_env->GetClassMethods(klass, &method_count, &methods);
+  CheckJvmtiError(jvmti_env, methods_result);
+
+  // Check each method.
+  for (jint i = 0; i < method_count; ++i) {
+    jint modifiers;
+    jvmtiError mod_result = jvmti_env->GetMethodModifiers(methods[i], &modifiers);
+    CheckJvmtiError(jvmti_env, mod_result);
+    constexpr jint kNative = static_cast<jint>(0x0100);
+    if ((modifiers & kNative) != 0) {
+      BindMethod(jvmti_env, env, klass, methods[i]);
+    }
+  }
+
+  CheckJvmtiError(jvmti_env, Deallocate(jvmti_env, methods));
+}
+
+void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name, jobject class_loader) {
+  // Use JNI to load the class.
+  ScopedLocalRef<jclass> klass(env, FindClass(jvmti_env, env, class_name, class_loader));
+  CHECK(klass.get() != nullptr) << class_name;
+  BindFunctionsOnClass(jvmti_env, env, klass.get());
+}
+
+}  // namespace art
diff --git a/test/ti-agent/jni_binder.h b/test/ti-agent/jni_binder.h
new file mode 100644 (file)
index 0000000..6f96257
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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_TEST_TI_AGENT_JNI_BINDER_H_
+#define ART_TEST_TI_AGENT_JNI_BINDER_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace art {
+
+// Load the class through JNI. Inspect it, find all native methods. Construct the corresponding
+// mangled name, run dlsym and bind the method.
+//
+// This will abort on failure.
+void BindFunctions(jvmtiEnv* jvmti_env,
+                   JNIEnv* env,
+                   const char* class_name,
+                   jobject class_loader = nullptr);
+
+void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass);
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_JNI_BINDER_H_
diff --git a/test/ti-agent/jni_helper.h b/test/ti-agent/jni_helper.h
new file mode 100644 (file)
index 0000000..0cbc634
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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_TEST_TI_AGENT_JNI_HELPER_H_
+#define ART_TEST_TI_AGENT_JNI_HELPER_H_
+
+#include "jni.h"
+#include "scoped_local_ref.h"
+
+namespace art {
+
+// Create an object array using a lambda that returns a local ref for each element.
+template <typename T>
+static inline jobjectArray CreateObjectArray(JNIEnv* env,
+                                             jint length,
+                                             const char* component_type_descriptor,
+                                             T src) {
+  if (length < 0) {
+    return nullptr;
+  }
+
+  ScopedLocalRef<jclass> obj_class(env, env->FindClass(component_type_descriptor));
+  if (obj_class.get() == nullptr) {
+    return nullptr;
+  }
+
+  ScopedLocalRef<jobjectArray> ret(env, env->NewObjectArray(length, obj_class.get(), nullptr));
+  if (ret.get() == nullptr) {
+    return nullptr;
+  }
+
+  for (jint i = 0; i < length; ++i) {
+    jobject element = src(i);
+    env->SetObjectArrayElement(ret.get(), static_cast<jint>(i), element);
+    env->DeleteLocalRef(element);
+    if (env->ExceptionCheck()) {
+      return nullptr;
+    }
+  }
+
+  return ret.release();
+}
+
+inline bool JniThrowNullPointerException(JNIEnv* env, const char* msg) {
+  if (env->ExceptionCheck()) {
+    env->ExceptionClear();
+  }
+
+  ScopedLocalRef<jclass> exc_class(env, env->FindClass("java/lang/NullPointerException"));
+  if (exc_class.get() == nullptr) {
+    return -1;
+  }
+
+  return env->ThrowNew(exc_class.get(), msg) == JNI_OK;
+}
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_JNI_HELPER_H_
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
new file mode 100644 (file)
index 0000000..598a30f
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 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 "jvmti_helper.h"
+
+#include <algorithm>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <sstream>
+#include <string.h>
+
+#include "android-base/logging.h"
+#include "scoped_local_ref.h"
+
+namespace art {
+
+void CheckJvmtiError(jvmtiEnv* env, jvmtiError error) {
+  if (error != JVMTI_ERROR_NONE) {
+    char* error_name;
+    jvmtiError name_error = env->GetErrorName(error, &error_name);
+    if (name_error != JVMTI_ERROR_NONE) {
+      LOG(FATAL) << "Unable to get error name for " << error;
+    }
+    LOG(FATAL) << "Unexpected error: " << error_name;
+  }
+}
+
+void SetAllCapabilities(jvmtiEnv* env) {
+  jvmtiCapabilities caps;
+  jvmtiError error1 = env->GetPotentialCapabilities(&caps);
+  CheckJvmtiError(env, error1);
+  jvmtiError error2 = env->AddCapabilities(&caps);
+  CheckJvmtiError(env, error2);
+}
+
+bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error) {
+  if (error == JVMTI_ERROR_NONE) {
+    return false;
+  }
+
+  ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+  if (rt_exception.get() == nullptr) {
+    // CNFE should be pending.
+    return true;
+  }
+
+  char* err;
+  CheckJvmtiError(jvmti_env, jvmti_env->GetErrorName(error, &err));
+
+  env->ThrowNew(rt_exception.get(), err);
+
+  Deallocate(jvmti_env, err);
+  return true;
+}
+
+std::ostream& operator<<(std::ostream& os, const jvmtiError& rhs) {
+  switch (rhs) {
+    case JVMTI_ERROR_NONE:
+      return os << "NONE";
+    case JVMTI_ERROR_INVALID_THREAD:
+      return os << "INVALID_THREAD";
+    case JVMTI_ERROR_INVALID_THREAD_GROUP:
+      return os << "INVALID_THREAD_GROUP";
+    case JVMTI_ERROR_INVALID_PRIORITY:
+      return os << "INVALID_PRIORITY";
+    case JVMTI_ERROR_THREAD_NOT_SUSPENDED:
+      return os << "THREAD_NOT_SUSPENDED";
+    case JVMTI_ERROR_THREAD_SUSPENDED:
+      return os << "THREAD_SUSPENDED";
+    case JVMTI_ERROR_THREAD_NOT_ALIVE:
+      return os << "THREAD_NOT_ALIVE";
+    case JVMTI_ERROR_INVALID_OBJECT:
+      return os << "INVALID_OBJECT";
+    case JVMTI_ERROR_INVALID_CLASS:
+      return os << "INVALID_CLASS";
+    case JVMTI_ERROR_CLASS_NOT_PREPARED:
+      return os << "CLASS_NOT_PREPARED";
+    case JVMTI_ERROR_INVALID_METHODID:
+      return os << "INVALID_METHODID";
+    case JVMTI_ERROR_INVALID_LOCATION:
+      return os << "INVALID_LOCATION";
+    case JVMTI_ERROR_INVALID_FIELDID:
+      return os << "INVALID_FIELDID";
+    case JVMTI_ERROR_NO_MORE_FRAMES:
+      return os << "NO_MORE_FRAMES";
+    case JVMTI_ERROR_OPAQUE_FRAME:
+      return os << "OPAQUE_FRAME";
+    case JVMTI_ERROR_TYPE_MISMATCH:
+      return os << "TYPE_MISMATCH";
+    case JVMTI_ERROR_INVALID_SLOT:
+      return os << "INVALID_SLOT";
+    case JVMTI_ERROR_DUPLICATE:
+      return os << "DUPLICATE";
+    case JVMTI_ERROR_NOT_FOUND:
+      return os << "NOT_FOUND";
+    case JVMTI_ERROR_INVALID_MONITOR:
+      return os << "INVALID_MONITOR";
+    case JVMTI_ERROR_NOT_MONITOR_OWNER:
+      return os << "NOT_MONITOR_OWNER";
+    case JVMTI_ERROR_INTERRUPT:
+      return os << "INTERRUPT";
+    case JVMTI_ERROR_INVALID_CLASS_FORMAT:
+      return os << "INVALID_CLASS_FORMAT";
+    case JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION:
+      return os << "CIRCULAR_CLASS_DEFINITION";
+    case JVMTI_ERROR_FAILS_VERIFICATION:
+      return os << "FAILS_VERIFICATION";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED:
+      return os << "UNSUPPORTED_REDEFINITION_METHOD_ADDED";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED:
+      return os << "UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED";
+    case JVMTI_ERROR_INVALID_TYPESTATE:
+      return os << "INVALID_TYPESTATE";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED:
+      return os << "UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED:
+      return os << "UNSUPPORTED_REDEFINITION_METHOD_DELETED";
+    case JVMTI_ERROR_UNSUPPORTED_VERSION:
+      return os << "UNSUPPORTED_VERSION";
+    case JVMTI_ERROR_NAMES_DONT_MATCH:
+      return os << "NAMES_DONT_MATCH";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED:
+      return os << "UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED";
+    case JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED:
+      return os << "UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED";
+    case JVMTI_ERROR_UNMODIFIABLE_CLASS:
+      return os << "JVMTI_ERROR_UNMODIFIABLE_CLASS";
+    case JVMTI_ERROR_NOT_AVAILABLE:
+      return os << "NOT_AVAILABLE";
+    case JVMTI_ERROR_MUST_POSSESS_CAPABILITY:
+      return os << "MUST_POSSESS_CAPABILITY";
+    case JVMTI_ERROR_NULL_POINTER:
+      return os << "NULL_POINTER";
+    case JVMTI_ERROR_ABSENT_INFORMATION:
+      return os << "ABSENT_INFORMATION";
+    case JVMTI_ERROR_INVALID_EVENT_TYPE:
+      return os << "INVALID_EVENT_TYPE";
+    case JVMTI_ERROR_ILLEGAL_ARGUMENT:
+      return os << "ILLEGAL_ARGUMENT";
+    case JVMTI_ERROR_NATIVE_METHOD:
+      return os << "NATIVE_METHOD";
+    case JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED:
+      return os << "CLASS_LOADER_UNSUPPORTED";
+    case JVMTI_ERROR_OUT_OF_MEMORY:
+      return os << "OUT_OF_MEMORY";
+    case JVMTI_ERROR_ACCESS_DENIED:
+      return os << "ACCESS_DENIED";
+    case JVMTI_ERROR_WRONG_PHASE:
+      return os << "WRONG_PHASE";
+    case JVMTI_ERROR_INTERNAL:
+      return os << "INTERNAL";
+    case JVMTI_ERROR_UNATTACHED_THREAD:
+      return os << "UNATTACHED_THREAD";
+    case JVMTI_ERROR_INVALID_ENVIRONMENT:
+      return os << "INVALID_ENVIRONMENT";
+  }
+  LOG(FATAL) << "Unexpected error type " << static_cast<int>(rhs);
+  __builtin_unreachable();
+}
+
+}  // namespace art
diff --git a/test/ti-agent/jvmti_helper.h b/test/ti-agent/jvmti_helper.h
new file mode 100644 (file)
index 0000000..66d88d0
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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_TEST_TI_AGENT_JVMTI_HELPER_H_
+#define ART_TEST_TI_AGENT_JVMTI_HELPER_H_
+
+#include "jni.h"
+#include "jvmti.h"
+#include <memory>
+#include <ostream>
+
+#include "android-base/logging.h"
+
+namespace art {
+
+// Add all capabilities to the given env.
+void SetAllCapabilities(jvmtiEnv* env);
+
+// Check whether the given error is NONE. If not, print out the corresponding error message
+// and abort.
+void CheckJvmtiError(jvmtiEnv* env, jvmtiError error);
+
+// Convert the given error to a RuntimeException with a message derived from the error. Returns
+// true on error, false if error is JVMTI_ERROR_NONE.
+bool JvmtiErrorToException(JNIEnv* env, jvmtiEnv* jvmti_env, jvmtiError error);
+
+class JvmtiDeleter {
+ public:
+  JvmtiDeleter() : env_(nullptr) {}
+  explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {}
+
+  JvmtiDeleter(JvmtiDeleter&) = default;
+  JvmtiDeleter(JvmtiDeleter&&) = default;
+  JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
+
+  void operator()(unsigned char* ptr) const {
+    CHECK(env_ != nullptr);
+    jvmtiError ret = env_->Deallocate(ptr);
+    CheckJvmtiError(env_, ret);
+  }
+
+ private:
+  mutable jvmtiEnv* env_;
+};
+
+using JvmtiUniquePtr = std::unique_ptr<unsigned char, JvmtiDeleter>;
+
+template <typename T>
+static inline JvmtiUniquePtr MakeJvmtiUniquePtr(jvmtiEnv* env, T* mem) {
+  return JvmtiUniquePtr(reinterpret_cast<unsigned char*>(mem), JvmtiDeleter(env));
+}
+
+template <typename T>
+static inline jvmtiError Deallocate(jvmtiEnv* env, T* mem) {
+  return env->Deallocate(reinterpret_cast<unsigned char*>(mem));
+}
+
+// To print jvmtiError. Does not rely on GetErrorName, so is an approximation.
+std::ostream& operator<<(std::ostream& os, const jvmtiError& rhs);
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_JVMTI_HELPER_H_
diff --git a/test/ti-agent/scoped_local_ref.h b/test/ti-agent/scoped_local_ref.h
new file mode 100644 (file)
index 0000000..daa1583
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 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_TEST_TI_AGENT_SCOPED_LOCAL_REF_H_
+#define ART_TEST_TI_AGENT_SCOPED_LOCAL_REF_H_
+
+#include "jni.h"
+
+#include <stddef.h>
+
+#include "android-base/macros.h"
+
+namespace art {
+
+template<typename T>
+class ScopedLocalRef {
+ public:
+  ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
+  }
+
+  ~ScopedLocalRef() {
+    reset();
+  }
+
+  void reset(T ptr = nullptr) {
+    if (ptr != mLocalRef) {
+      if (mLocalRef != nullptr) {
+        mEnv->DeleteLocalRef(mLocalRef);
+      }
+      mLocalRef = ptr;
+    }
+  }
+
+  T release() __attribute__((warn_unused_result)) {
+    T localRef = mLocalRef;
+    mLocalRef = nullptr;
+    return localRef;
+  }
+
+  T get() const {
+    return mLocalRef;
+  }
+
+ private:
+  JNIEnv* const mEnv;
+  T mLocalRef;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
+};
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_SCOPED_LOCAL_REF_H_
diff --git a/test/ti-agent/scoped_primitive_array.h b/test/ti-agent/scoped_primitive_array.h
new file mode 100644 (file)
index 0000000..1649ed9
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010 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_TEST_TI_AGENT_SCOPED_PRIMITIVE_ARRAY_H_
+#define ART_TEST_TI_AGENT_SCOPED_PRIMITIVE_ARRAY_H_
+
+#include "jni.h"
+
+#include "android-base/macros.h"
+
+#include "jni_helper.h"
+
+namespace art {
+
+#ifdef POINTER_TYPE
+#error POINTER_TYPE is defined.
+#else
+#define POINTER_TYPE(T) T*  /* NOLINT */
+#endif
+
+#ifdef REFERENCE_TYPE
+#error REFERENCE_TYPE is defined.
+#else
+#define REFERENCE_TYPE(T) T&  /* NOLINT */
+#endif
+
+// ScopedBooleanArrayRO, ScopedByteArrayRO, ScopedCharArrayRO, ScopedDoubleArrayRO,
+// ScopedFloatArrayRO, ScopedIntArrayRO, ScopedLongArrayRO, and ScopedShortArrayRO provide
+// convenient read-only access to Java arrays from JNI code. This is cheaper than read-write
+// access and should be used by default.
+#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(PRIMITIVE_TYPE, NAME) \
+    class Scoped ## NAME ## ArrayRO { \
+    public: \
+        explicit Scoped ## NAME ## ArrayRO(JNIEnv* env) \
+        : mEnv(env), mJavaArray(nullptr), mRawArray(nullptr), mSize(0) {} \
+        Scoped ## NAME ## ArrayRO(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
+        : mEnv(env) { \
+            if (javaArray == nullptr) { \
+                mJavaArray = nullptr; \
+                mSize = 0; \
+                mRawArray = nullptr; \
+                JniThrowNullPointerException(env, nullptr); \
+            } else { \
+                reset(javaArray); \
+            } \
+        } \
+        ~Scoped ## NAME ## ArrayRO() { \
+            if (mRawArray != nullptr && mRawArray != mBuffer) { \
+                mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, JNI_ABORT); \
+            } \
+        } \
+        void reset(PRIMITIVE_TYPE ## Array javaArray) { \
+            mJavaArray = javaArray; \
+            mSize = mEnv->GetArrayLength(mJavaArray); \
+            if (mSize <= kBufferSize) { \
+                mEnv->Get ## NAME ## ArrayRegion(mJavaArray, 0, mSize, mBuffer); \
+                mRawArray = mBuffer; \
+            } else { \
+                mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
+            } \
+        } \
+        const PRIMITIVE_TYPE* get() const { return mRawArray; } \
+        PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
+        const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
+        size_t size() const { return mSize; } \
+    private: \
+        static constexpr jsize kBufferSize = 1024; \
+        JNIEnv* const mEnv; \
+        PRIMITIVE_TYPE ## Array mJavaArray; \
+        POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
+        jsize mSize; \
+        PRIMITIVE_TYPE mBuffer[kBufferSize]; \
+        DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRO); \
+    }
+
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jboolean, Boolean);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jbyte, Byte);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jchar, Char);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jdouble, Double);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jfloat, Float);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jint, Int);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jlong, Long);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jshort, Short);
+
+#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO
+
+// ScopedBooleanArrayRW, ScopedByteArrayRW, ScopedCharArrayRW, ScopedDoubleArrayRW,
+// ScopedFloatArrayRW, ScopedIntArrayRW, ScopedLongArrayRW, and ScopedShortArrayRW provide
+// convenient read-write access to Java arrays from JNI code. These are more expensive,
+// since they entail a copy back onto the Java heap, and should only be used when necessary.
+#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(PRIMITIVE_TYPE, NAME) \
+    class Scoped ## NAME ## ArrayRW { \
+    public: \
+        explicit Scoped ## NAME ## ArrayRW(JNIEnv* env) \
+        : mEnv(env), mJavaArray(nullptr), mRawArray(nullptr) {} \
+        Scoped ## NAME ## ArrayRW(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
+        : mEnv(env), mJavaArray(javaArray), mRawArray(nullptr) { \
+            if (mJavaArray == nullptr) { \
+                JniThrowNullPointerException(env, nullptr); \
+            } else { \
+                mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
+            } \
+        } \
+        ~Scoped ## NAME ## ArrayRW() { \
+            if (mRawArray) { \
+                mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, 0); \
+            } \
+        } \
+        void reset(PRIMITIVE_TYPE ## Array javaArray) { \
+            mJavaArray = javaArray; \
+            mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
+        } \
+        const PRIMITIVE_TYPE* get() const { return mRawArray; } \
+        PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
+        const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
+        POINTER_TYPE(PRIMITIVE_TYPE) get() { return mRawArray; }  \
+        REFERENCE_TYPE(PRIMITIVE_TYPE) operator[](size_t n) { return mRawArray[n]; } \
+        size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \
+    private: \
+        JNIEnv* const mEnv; \
+        PRIMITIVE_TYPE ## Array mJavaArray; \
+        POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
+        DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRW); \
+    }
+
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jboolean, Boolean);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jbyte, Byte);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jchar, Char);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jdouble, Double);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jfloat, Float);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jint, Int);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jlong, Long);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jshort, Short);
+
+#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW
+#undef POINTER_TYPE
+#undef REFERENCE_TYPE
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_SCOPED_PRIMITIVE_ARRAY_H_
diff --git a/test/ti-agent/scoped_utf_chars.h b/test/ti-agent/scoped_utf_chars.h
new file mode 100644 (file)
index 0000000..422caaf
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2010 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_TEST_TI_AGENT_SCOPED_UTF_CHARS_H_
+#define ART_TEST_TI_AGENT_SCOPED_UTF_CHARS_H_
+
+#include "jni.h"
+
+#include <string.h>
+
+#include "android-base/macros.h"
+
+#include "jni_helper.h"
+
+namespace art {
+
+class ScopedUtfChars {
+ public:
+  ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
+    if (s == nullptr) {
+      utf_chars_ = nullptr;
+      JniThrowNullPointerException(env, nullptr);
+    } else {
+      utf_chars_ = env->GetStringUTFChars(s, nullptr);
+    }
+  }
+
+  ScopedUtfChars(ScopedUtfChars&& rhs) :
+      env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
+    rhs.env_ = nullptr;
+    rhs.string_ = nullptr;
+    rhs.utf_chars_ = nullptr;
+  }
+
+  ~ScopedUtfChars() {
+    if (utf_chars_) {
+      env_->ReleaseStringUTFChars(string_, utf_chars_);
+    }
+  }
+
+  ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
+    if (this != &rhs) {
+      // Delete the currently owned UTF chars.
+      this->~ScopedUtfChars();
+
+      // Move the rhs ScopedUtfChars and zero it out.
+      env_ = rhs.env_;
+      string_ = rhs.string_;
+      utf_chars_ = rhs.utf_chars_;
+      rhs.env_ = nullptr;
+      rhs.string_ = nullptr;
+      rhs.utf_chars_ = nullptr;
+    }
+    return *this;
+  }
+
+  const char* c_str() const {
+    return utf_chars_;
+  }
+
+  size_t size() const {
+    return strlen(utf_chars_);
+  }
+
+  const char& operator[](size_t n) const {
+    return utf_chars_[n];
+  }
+
+ private:
+  JNIEnv* env_;
+  jstring string_;
+  const char* utf_chars_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
+};
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_SCOPED_UTF_CHARS_H_
diff --git a/test/ti-agent/test_env.cc b/test/ti-agent/test_env.cc
new file mode 100644 (file)
index 0000000..cf47f22
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test_env.h"
+
+namespace art {
+
+jvmtiEnv* jvmti_env = nullptr;
+
+static bool gRuntimeIsJVM = false;
+
+bool IsJVM() {
+  return gRuntimeIsJVM;
+}
+
+void SetJVM(bool b) {
+  gRuntimeIsJVM = b;
+}
+
+}  // namespace art
similarity index 82%
rename from test/ti-agent/common_load.h
rename to test/ti-agent/test_env.h
index e79a006..2eb631c 100644 (file)
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ART_TEST_TI_AGENT_COMMON_LOAD_H_
-#define ART_TEST_TI_AGENT_COMMON_LOAD_H_
+#ifndef ART_TEST_TI_AGENT_TEST_ENV_H_
+#define ART_TEST_TI_AGENT_TEST_ENV_H_
 
 #include "jvmti.h"
 
@@ -23,6 +23,9 @@ namespace art {
 
 extern jvmtiEnv* jvmti_env;
 
+bool IsJVM();
+void SetJVM(bool b);
+
 }  // namespace art
 
-#endif  // ART_TEST_TI_AGENT_COMMON_LOAD_H_
+#endif  // ART_TEST_TI_AGENT_TEST_ENV_H_
diff --git a/test/ti-agent/ti_macros.h b/test/ti-agent/ti_macros.h
new file mode 100644 (file)
index 0000000..d913383
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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_TEST_TI_AGENT_TI_MACROS_H_
+#define ART_TEST_TI_AGENT_TI_MACROS_H_
+
+#include "android-base/macros.h"
+
+#define FINAL final
+#define OVERRIDE override
+#define UNREACHABLE  __builtin_unreachable
+
+#endif  // ART_TEST_TI_AGENT_TI_MACROS_H_
diff --git a/test/ti-agent/ti_utf.h b/test/ti-agent/ti_utf.h
new file mode 100644 (file)
index 0000000..341e106
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2017 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_TEST_TI_AGENT_TI_UTF_H_
+#define ART_TEST_TI_AGENT_TI_UTF_H_
+
+#include <inttypes.h>
+#include <string.h>
+
+#include "android-base/logging.h"
+
+namespace art {
+namespace ti {
+
+inline size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) {
+  DCHECK_LE(byte_count, strlen(utf8));
+  size_t len = 0;
+  const char* end = utf8 + byte_count;
+  for (; utf8 < end; ++utf8) {
+    int ic = *utf8;
+    len++;
+    if (LIKELY((ic & 0x80) == 0)) {
+      // One-byte encoding.
+      continue;
+    }
+    // Two- or three-byte encoding.
+    utf8++;
+    if ((ic & 0x20) == 0) {
+      // Two-byte encoding.
+      continue;
+    }
+    utf8++;
+    if ((ic & 0x10) == 0) {
+      // Three-byte encoding.
+      continue;
+    }
+
+    // Four-byte encoding: needs to be converted into a surrogate
+    // pair.
+    utf8++;
+    len++;
+  }
+  return len;
+}
+
+inline uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) {
+  return static_cast<uint16_t>(maybe_pair >> 16);
+}
+
+inline uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) {
+  return static_cast<uint16_t>(maybe_pair & 0x0000FFFF);
+}
+
+inline uint32_t GetUtf16FromUtf8(const char** utf8_data_in) {
+  const uint8_t one = *(*utf8_data_in)++;
+  if ((one & 0x80) == 0) {
+    // one-byte encoding
+    return one;
+  }
+
+  const uint8_t two = *(*utf8_data_in)++;
+  if ((one & 0x20) == 0) {
+    // two-byte encoding
+    return ((one & 0x1f) << 6) | (two & 0x3f);
+  }
+
+  const uint8_t three = *(*utf8_data_in)++;
+  if ((one & 0x10) == 0) {
+    return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
+  }
+
+  // Four byte encodings need special handling. We'll have
+  // to convert them into a surrogate pair.
+  const uint8_t four = *(*utf8_data_in)++;
+
+  // Since this is a 4 byte UTF-8 sequence, it will lie between
+  // U+10000 and U+1FFFFF.
+  //
+  // TODO: What do we do about values in (U+10FFFF, U+1FFFFF) ? The
+  // spec says they're invalid but nobody appears to check for them.
+  const uint32_t code_point = ((one & 0x0f) << 18) | ((two & 0x3f) << 12)
+      | ((three & 0x3f) << 6) | (four & 0x3f);
+
+  uint32_t surrogate_pair = 0;
+  // Step two: Write out the high (leading) surrogate to the bottom 16 bits
+  // of the of the 32 bit type.
+  surrogate_pair |= ((code_point >> 10) + 0xd7c0) & 0xffff;
+  // Step three : Write out the low (trailing) surrogate to the top 16 bits.
+  surrogate_pair |= ((code_point & 0x03ff) + 0xdc00) << 16;
+
+  return surrogate_pair;
+}
+
+inline void ConvertUtf16ToModifiedUtf8(char* utf8_out,
+                                       size_t byte_count,
+                                       const uint16_t* utf16_in,
+                                       size_t char_count) {
+  if (LIKELY(byte_count == char_count)) {
+    // Common case where all characters are ASCII.
+    const uint16_t *utf16_end = utf16_in + char_count;
+    for (const uint16_t *p = utf16_in; p < utf16_end;) {
+      *utf8_out++ = static_cast<char>(*p++);
+    }
+    return;
+  }
+
+  // String contains non-ASCII characters.
+  while (char_count--) {
+    const uint16_t ch = *utf16_in++;
+    if (ch > 0 && ch <= 0x7f) {
+      *utf8_out++ = ch;
+    } else {
+      // Char_count == 0 here implies we've encountered an unpaired
+      // surrogate and we have no choice but to encode it as 3-byte UTF
+      // sequence. Note that unpaired surrogates can occur as a part of
+      // "normal" operation.
+      if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
+        const uint16_t ch2 = *utf16_in;
+
+        // Check if the other half of the pair is within the expected
+        // range. If it isn't, we will have to emit both "halves" as
+        // separate 3 byte sequences.
+        if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
+          utf16_in++;
+          char_count--;
+          const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
+          *utf8_out++ = (code_point >> 18) | 0xf0;
+          *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
+          *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
+          *utf8_out++ = (code_point & 0x3f) | 0x80;
+          continue;
+        }
+      }
+
+      if (ch > 0x07ff) {
+        // Three byte encoding.
+        *utf8_out++ = (ch >> 12) | 0xe0;
+        *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
+        *utf8_out++ = (ch & 0x3f) | 0x80;
+      } else /*(ch > 0x7f || ch == 0)*/ {
+        // Two byte encoding.
+        *utf8_out++ = (ch >> 6) | 0xc0;
+        *utf8_out++ = (ch & 0x3f) | 0x80;
+      }
+    }
+  }
+}
+
+inline size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
+  size_t result = 0;
+  const uint16_t *end = chars + char_count;
+  while (chars < end) {
+    const uint16_t ch = *chars++;
+    if (LIKELY(ch != 0 && ch < 0x80)) {
+      result++;
+      continue;
+    }
+    if (ch < 0x800) {
+      result += 2;
+      continue;
+    }
+    if (ch >= 0xd800 && ch < 0xdc00) {
+      if (chars < end) {
+        const uint16_t ch2 = *chars;
+        // If we find a properly paired surrogate, we emit it as a 4 byte
+        // UTF sequence. If we find an unpaired leading or trailing surrogate,
+        // we emit it as a 3 byte sequence like would have done earlier.
+        if (ch2 >= 0xdc00 && ch2 < 0xe000) {
+          chars++;
+          result += 4;
+          continue;
+        }
+      }
+    }
+    result += 3;
+  }
+  return result;
+}
+
+}  // namespace ti
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_TI_UTF_H_
index 7eaaaf9..6c2c072 100755 (executable)
@@ -61,6 +61,9 @@ adb shell uptime
 echo -e "${green}Battery info${nc}"
 adb shell dumpsys battery
 
+echo -e "${green}Killing logd, seen leaking on fugu/N${nc}"
+adb shell killall -9 /system/bin/logd
+
 echo -e "${green}Setting adb buffer size to 32MB${nc}"
 adb logcat -G 32M
 adb logcat -g