diff options
-rw-r--r-- | compiler/jni/quick/arm/calling_convention_arm.cc | 16 | ||||
-rw-r--r-- | compiler/jni/quick/arm/calling_convention_arm.h | 4 | ||||
-rw-r--r-- | compiler/jni/quick/arm64/calling_convention_arm64.cc | 17 | ||||
-rw-r--r-- | compiler/utils/arm/assembler_arm.cc | 40 | ||||
-rw-r--r-- | compiler/utils/arm64/assembler_arm64.cc | 37 | ||||
-rw-r--r-- | compiler/utils/arm64/constants_arm64.h | 2 | ||||
-rw-r--r-- | runtime/stack.cc | 468 | ||||
-rw-r--r-- | runtime/stack.h | 45 | ||||
-rw-r--r-- | runtime/stack_map.h | 22 | ||||
-rw-r--r-- | test/454-get-vreg/expected.txt | 0 | ||||
-rw-r--r-- | test/454-get-vreg/get_vreg_jni.cc | 122 | ||||
-rw-r--r-- | test/454-get-vreg/info.txt | 1 | ||||
-rw-r--r-- | test/454-get-vreg/src/Main.java | 53 | ||||
-rw-r--r-- | test/455-set-vreg/expected.txt | 0 | ||||
-rw-r--r-- | test/455-set-vreg/info.txt | 1 | ||||
-rw-r--r-- | test/455-set-vreg/set_vreg_jni.cc | 95 | ||||
-rw-r--r-- | test/455-set-vreg/src/Main.java | 73 | ||||
-rw-r--r-- | test/Android.libarttest.mk | 4 | ||||
-rwxr-xr-x | test/etc/run-test-jar | 2 |
19 files changed, 823 insertions, 179 deletions
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc index 669c3bb..d3690b2 100644 --- a/compiler/jni/quick/arm/calling_convention_arm.cc +++ b/compiler/jni/quick/arm/calling_convention_arm.cc @@ -31,6 +31,10 @@ static const SRegister kHFSArgumentRegisters[] = { S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15 }; +static const SRegister kHFSCalleeSaveRegisters[] = { + S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31 +}; + static const DRegister kHFDArgumentRegisters[] = { D0, D1, D2, D3, D4, D5, D6, D7 }; @@ -226,6 +230,10 @@ ArmJniCallingConvention::ArmJniCallingConvention(bool is_static, bool is_synchro callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R8)); callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R10)); callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R11)); + + for (size_t i = 0; i < arraysize(kHFSCalleeSaveRegisters); ++i) { + callee_save_regs_.push_back(ArmManagedRegister::FromSRegister(kHFSCalleeSaveRegisters[i])); + } } uint32_t ArmJniCallingConvention::CoreSpillMask() const { @@ -235,6 +243,14 @@ uint32_t ArmJniCallingConvention::CoreSpillMask() const { return result; } +uint32_t ArmJniCallingConvention::FpSpillMask() const { + uint32_t result = 0; + for (size_t i = 0; i < arraysize(kHFSCalleeSaveRegisters); ++i) { + result |= (1 << kHFSCalleeSaveRegisters[i]); + } + return result; +} + ManagedRegister ArmJniCallingConvention::ReturnScratchRegister() const { return ArmManagedRegister::FromCoreRegister(R2); } diff --git a/compiler/jni/quick/arm/calling_convention_arm.h b/compiler/jni/quick/arm/calling_convention_arm.h index 604ce1c..dbecb8e 100644 --- a/compiler/jni/quick/arm/calling_convention_arm.h +++ b/compiler/jni/quick/arm/calling_convention_arm.h @@ -63,9 +63,7 @@ class ArmJniCallingConvention FINAL : public JniCallingConvention { } ManagedRegister ReturnScratchRegister() const OVERRIDE; uint32_t CoreSpillMask() const OVERRIDE; - uint32_t FpSpillMask() const OVERRIDE { - return 0; // Floats aren't spilled in JNI down call - } + uint32_t FpSpillMask() const OVERRIDE; bool IsCurrentParamInRegister() OVERRIDE; bool IsCurrentParamOnStack() OVERRIDE; ManagedRegister CurrentParamRegister() OVERRIDE; diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc index b9c8178..05eb80a 100644 --- a/compiler/jni/quick/arm64/calling_convention_arm64.cc +++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc @@ -38,6 +38,10 @@ static const SRegister kSArgumentRegisters[] = { S0, S1, S2, S3, S4, S5, S6, S7 }; +static const DRegister kDCalleeSaveRegisters[] = { + D8, D9, D10, D11, D12, D13, D14, D15 +}; + // Calling convention ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() { return Arm64ManagedRegister::FromXRegister(X20); // saved on entry restored on exit @@ -166,6 +170,10 @@ Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_syn callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X28)); callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X29)); callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X30)); + + for (size_t i = 0; i < arraysize(kDCalleeSaveRegisters); ++i) { + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(kDCalleeSaveRegisters[i])); + } } uint32_t Arm64JniCallingConvention::CoreSpillMask() const { @@ -184,10 +192,11 @@ uint32_t Arm64JniCallingConvention::CoreSpillMask() const { } uint32_t Arm64JniCallingConvention::FpSpillMask() const { - // Compute spill mask to agree with callee saves initialized in the constructor - // Note: All callee-save fp registers will be preserved by aapcs64. And they are not used - // in the jni method. - return 0; + uint32_t result = 0; + for (size_t i = 0; i < arraysize(kDCalleeSaveRegisters); ++i) { + result |= (1 << kDCalleeSaveRegisters[i]); + } + return result; } ManagedRegister Arm64JniCallingConvention::ReturnScratchRegister() const { diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc index a52e6eb..a02191b 100644 --- a/compiler/utils/arm/assembler_arm.cc +++ b/compiler/utils/arm/assembler_arm.cc @@ -385,12 +385,24 @@ void ArmAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, // Push callee saves and link register. RegList push_list = 1 << LR; size_t pushed_values = 1; + int32_t min_s = kNumberOfSRegisters; + int32_t max_s = -1; for (size_t i = 0; i < callee_save_regs.size(); i++) { - Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister(); - push_list |= 1 << reg; - pushed_values++; + if (callee_save_regs.at(i).AsArm().IsCoreRegister()) { + Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister(); + push_list |= 1 << reg; + pushed_values++; + } else { + CHECK(callee_save_regs.at(i).AsArm().IsSRegister()); + min_s = std::min(static_cast<int>(callee_save_regs.at(i).AsArm().AsSRegister()), min_s); + max_s = std::max(static_cast<int>(callee_save_regs.at(i).AsArm().AsSRegister()), max_s); + } } PushList(push_list); + if (max_s != -1) { + pushed_values += 1 + max_s - min_s; + vpushs(static_cast<SRegister>(min_s), 1 + max_s - min_s); + } // Increase frame to required size. CHECK_GT(frame_size, pushed_values * kFramePointerSize); // Must at least have space for Method*. @@ -427,10 +439,22 @@ void ArmAssembler::RemoveFrame(size_t frame_size, // Compute callee saves to pop and PC. RegList pop_list = 1 << PC; size_t pop_values = 1; + int32_t min_s = kNumberOfSRegisters; + int32_t max_s = -1; for (size_t i = 0; i < callee_save_regs.size(); i++) { - Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister(); - pop_list |= 1 << reg; - pop_values++; + if (callee_save_regs.at(i).AsArm().IsCoreRegister()) { + Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister(); + pop_list |= 1 << reg; + pop_values++; + } else { + CHECK(callee_save_regs.at(i).AsArm().IsSRegister()); + min_s = std::min(static_cast<int>(callee_save_regs.at(i).AsArm().AsSRegister()), min_s); + max_s = std::max(static_cast<int>(callee_save_regs.at(i).AsArm().AsSRegister()), max_s); + } + } + + if (max_s != -1) { + pop_values += 1 + max_s - min_s; } // Decrease frame to start of callee saves. @@ -438,6 +462,10 @@ void ArmAssembler::RemoveFrame(size_t frame_size, size_t adjust = frame_size - (pop_values * kFramePointerSize); DecreaseFrameSize(adjust); + if (max_s != -1) { + vpops(static_cast<SRegister>(min_s), 1 + max_s - min_s); + } + // Pop callee saves and PC. PopList(pop_list); } diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc index 21014c8..58c7367 100644 --- a/compiler/utils/arm64/assembler_arm64.cc +++ b/compiler/utils/arm64/assembler_arm64.cc @@ -639,6 +639,7 @@ void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) { } constexpr size_t kFramePointerSize = 8; +constexpr unsigned int kJniRefSpillRegsSize = 11 + 8; void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, const std::vector<ManagedRegister>& callee_save_regs, @@ -648,7 +649,7 @@ void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, // TODO: *create APCS FP - end of FP chain; // *add support for saving a different set of callee regs. - // For now we check that the size of callee regs vector is 11. + // For now we check that the size of callee regs vector is 11 core registers and 8 fp registers. CHECK_EQ(callee_save_regs.size(), kJniRefSpillRegsSize); // Increase frame to required size - must be at least space to push StackReference<Method>. CHECK_GT(frame_size, kJniRefSpillRegsSize * kFramePointerSize); @@ -682,6 +683,23 @@ void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg, reg_offset -= 8; StoreToOffset(X20, SP, reg_offset); + reg_offset -= 8; + StoreDToOffset(D15, SP, reg_offset); + reg_offset -= 8; + StoreDToOffset(D14, SP, reg_offset); + reg_offset -= 8; + StoreDToOffset(D13, SP, reg_offset); + reg_offset -= 8; + StoreDToOffset(D12, SP, reg_offset); + reg_offset -= 8; + StoreDToOffset(D11, SP, reg_offset); + reg_offset -= 8; + StoreDToOffset(D10, SP, reg_offset); + reg_offset -= 8; + StoreDToOffset(D9, SP, reg_offset); + reg_offset -= 8; + StoreDToOffset(D8, SP, reg_offset); + // Move TR(Caller saved) to ETR(Callee saved). The original (ETR)X21 has been saved on stack. // This way we make sure that TR is not trashed by native code. ___ Mov(reg_x(ETR), reg_x(TR)); @@ -753,6 +771,23 @@ void Arm64Assembler::RemoveFrame(size_t frame_size, const std::vector<ManagedReg reg_offset -= 8; LoadFromOffset(X20, SP, reg_offset); + reg_offset -= 8; + LoadDFromOffset(D15, SP, reg_offset); + reg_offset -= 8; + LoadDFromOffset(D14, SP, reg_offset); + reg_offset -= 8; + LoadDFromOffset(D13, SP, reg_offset); + reg_offset -= 8; + LoadDFromOffset(D12, SP, reg_offset); + reg_offset -= 8; + LoadDFromOffset(D11, SP, reg_offset); + reg_offset -= 8; + LoadDFromOffset(D10, SP, reg_offset); + reg_offset -= 8; + LoadDFromOffset(D9, SP, reg_offset); + reg_offset -= 8; + LoadDFromOffset(D8, SP, reg_offset); + // Decrease frame size to start of callee saved regs. DecreaseFrameSize(frame_size); diff --git a/compiler/utils/arm64/constants_arm64.h b/compiler/utils/arm64/constants_arm64.h index ffb54d3..01e8be9 100644 --- a/compiler/utils/arm64/constants_arm64.h +++ b/compiler/utils/arm64/constants_arm64.h @@ -29,8 +29,6 @@ namespace art { namespace arm64 { -constexpr unsigned int kJniRefSpillRegsSize = 11; - constexpr size_t kArm64BaseBufferSize = 4096; } // namespace arm64 diff --git a/runtime/stack.cc b/runtime/stack.cc index b39aebf..d570880 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -134,8 +134,7 @@ mirror::Object* StackVisitor::GetThisObject() const { } else { return cur_shadow_frame_->GetVRegReference(0); } - } else if (m->IsOptimized(GetInstructionSetPointerSize( - Runtime::Current()->GetInstructionSet()))) { + } else if (m->IsOptimized(sizeof(void*))) { // TODO: Implement, currently only used for exceptions when jdwp is enabled. UNIMPLEMENTED(WARNING) << "StackVisitor::GetThisObject is unimplemented with the optimizing compiler"; @@ -163,42 +162,10 @@ bool StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably read registers without a context. DCHECK(m == GetMethod()); - const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); - DCHECK(code_pointer != nullptr); - const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); - QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); - uint32_t vmap_offset; - // TODO: IsInContext stops before spotting floating point registers. - if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) { - bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg); - uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask(); - uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind); - if (!IsAccessibleRegister(reg, is_float)) { - return false; - } - uintptr_t ptr_val = GetRegister(reg, is_float); - bool target64 = Is64BitInstructionSet(kRuntimeISA); - if (target64) { - bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg); - bool wide_hi = (kind == kLongHiVReg) || (kind == kDoubleHiVReg); - int64_t value_long = static_cast<int64_t>(ptr_val); - if (wide_lo) { - ptr_val = static_cast<uintptr_t>(value_long & 0xFFFFFFFF); - } else if (wide_hi) { - ptr_val = static_cast<uintptr_t>(value_long >> 32); - } - } - *val = ptr_val; - return true; + if (m->IsOptimized(sizeof(void*))) { + return GetVRegFromOptimizedCode(m, vreg, kind, val); } else { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile - // its instructions? - uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(), - frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg); - DCHECK(addr != nullptr); - *val = *addr; - return true; + return GetVRegFromQuickCode(m, vreg, kind, val); } } else { DCHECK(cur_shadow_frame_ != nullptr); @@ -207,6 +174,86 @@ bool StackVisitor::GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, } } +bool StackVisitor::GetVRegFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, + uint32_t* val) const { + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); + DCHECK(code_pointer != nullptr); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); + QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); + uint32_t vmap_offset; + // TODO: IsInContext stops before spotting floating point registers. + if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) { + bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg); + uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask(); + uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind); + return GetRegisterIfAccessible(reg, kind, val); + } else { + const DexFile::CodeItem* code_item = m->GetCodeItem(); + DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile + // its instructions? + *val = *GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(), + frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg); + return true; + } +} + +bool StackVisitor::GetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, + uint32_t* val) const { + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); + DCHECK(code_pointer != nullptr); + uint32_t native_pc_offset = m->NativeQuickPcOffset(cur_quick_frame_pc_); + CodeInfo code_info = m->GetOptimizedCodeInfo(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + const DexFile::CodeItem* code_item = m->GetCodeItem(); + DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile + // its instructions? + DCHECK_LT(vreg, code_item->registers_size_); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, + code_item->registers_size_); + DexRegisterMap::LocationKind location_kind = dex_register_map.GetLocationKind(vreg); + switch (location_kind) { + case DexRegisterMap::kInStack: { + const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg); + const uint8_t* addr = reinterpret_cast<const uint8_t*>(cur_quick_frame_) + offset; + *val = *reinterpret_cast<const uint32_t*>(addr); + return true; + } + case DexRegisterMap::kInRegister: + case DexRegisterMap::kInFpuRegister: { + uint32_t reg = dex_register_map.GetMachineRegister(vreg); + return GetRegisterIfAccessible(reg, kind, val); + } + case DexRegisterMap::kConstant: + *val = dex_register_map.GetConstant(vreg); + return true; + case DexRegisterMap::kNone: + return false; + } + UNREACHABLE(); + return false; +} + +bool StackVisitor::GetRegisterIfAccessible(uint32_t reg, VRegKind kind, uint32_t* val) const { + const bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg); + if (!IsAccessibleRegister(reg, is_float)) { + return false; + } + uintptr_t ptr_val = GetRegister(reg, is_float); + const bool target64 = Is64BitInstructionSet(kRuntimeISA); + if (target64) { + const bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg); + const bool wide_hi = (kind == kLongHiVReg) || (kind == kDoubleHiVReg); + int64_t value_long = static_cast<int64_t>(ptr_val); + if (wide_lo) { + ptr_val = static_cast<uintptr_t>(Low32Bits(value_long)); + } else if (wide_hi) { + ptr_val = static_cast<uintptr_t>(High32Bits(value_long)); + } + } + *val = ptr_val; + return true; +} + bool StackVisitor::GetVRegPair(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind_lo, VRegKind kind_hi, uint64_t* val) const { if (kind_lo == kLongLoVReg) { @@ -215,45 +262,15 @@ bool StackVisitor::GetVRegPair(mirror::ArtMethod* m, uint16_t vreg, VRegKind kin DCHECK_EQ(kind_hi, kDoubleHiVReg); } else { LOG(FATAL) << "Expected long or double: kind_lo=" << kind_lo << ", kind_hi=" << kind_hi; + UNREACHABLE(); } if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably read registers without a context. DCHECK(m == GetMethod()); - const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); - DCHECK(code_pointer != nullptr); - const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); - QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); - uint32_t vmap_offset_lo, vmap_offset_hi; - // TODO: IsInContext stops before spotting floating point registers. - if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) && - vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) { - bool is_float = (kind_lo == kDoubleLoVReg); - uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask(); - uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo); - uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi); - if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) { - return false; - } - uintptr_t ptr_val_lo = GetRegister(reg_lo, is_float); - uintptr_t ptr_val_hi = GetRegister(reg_hi, is_float); - bool target64 = Is64BitInstructionSet(kRuntimeISA); - if (target64) { - int64_t value_long_lo = static_cast<int64_t>(ptr_val_lo); - int64_t value_long_hi = static_cast<int64_t>(ptr_val_hi); - ptr_val_lo = static_cast<uintptr_t>(value_long_lo & 0xFFFFFFFF); - ptr_val_hi = static_cast<uintptr_t>(value_long_hi >> 32); - } - *val = (static_cast<uint64_t>(ptr_val_hi) << 32) | static_cast<uint32_t>(ptr_val_lo); - return true; + if (m->IsOptimized(sizeof(void*))) { + return GetVRegPairFromOptimizedCode(m, vreg, kind_lo, kind_hi, val); } else { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile - // its instructions? - uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(), - frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg); - DCHECK(addr != nullptr); - *val = *reinterpret_cast<uint64_t*>(addr); - return true; + return GetVRegPairFromQuickCode(m, vreg, kind_lo, kind_hi, val); } } else { DCHECK(cur_shadow_frame_ != nullptr); @@ -262,61 +279,185 @@ bool StackVisitor::GetVRegPair(mirror::ArtMethod* m, uint16_t vreg, VRegKind kin } } +bool StackVisitor::GetVRegPairFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind_lo, + VRegKind kind_hi, uint64_t* val) const { + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); + DCHECK(code_pointer != nullptr); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); + QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); + uint32_t vmap_offset_lo, vmap_offset_hi; + // TODO: IsInContext stops before spotting floating point registers. + if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) && + vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) { + bool is_float = (kind_lo == kDoubleLoVReg); + uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask(); + uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo); + uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi); + return GetRegisterPairIfAccessible(reg_lo, reg_hi, kind_lo, val); + } else { + const DexFile::CodeItem* code_item = m->GetCodeItem(); + DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile + // its instructions? + uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(), + frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg); + *val = *reinterpret_cast<uint64_t*>(addr); + return true; + } +} + +bool StackVisitor::GetVRegPairFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, + VRegKind kind_lo, VRegKind kind_hi, + uint64_t* val) const { + uint32_t low_32bits; + uint32_t high_32bits; + bool success = GetVRegFromOptimizedCode(m, vreg, kind_lo, &low_32bits); + success &= GetVRegFromOptimizedCode(m, vreg + 1, kind_hi, &high_32bits); + if (success) { + *val = (static_cast<uint64_t>(high_32bits) << 32) | static_cast<uint64_t>(low_32bits); + } + return success; +} + +bool StackVisitor::GetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi, + VRegKind kind_lo, uint64_t* val) const { + const bool is_float = (kind_lo == kDoubleLoVReg); + if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) { + return false; + } + uintptr_t ptr_val_lo = GetRegister(reg_lo, is_float); + uintptr_t ptr_val_hi = GetRegister(reg_hi, is_float); + bool target64 = Is64BitInstructionSet(kRuntimeISA); + if (target64) { + int64_t value_long_lo = static_cast<int64_t>(ptr_val_lo); + int64_t value_long_hi = static_cast<int64_t>(ptr_val_hi); + ptr_val_lo = static_cast<uintptr_t>(Low32Bits(value_long_lo)); + ptr_val_hi = static_cast<uintptr_t>(High32Bits(value_long_hi)); + } + *val = (static_cast<uint64_t>(ptr_val_hi) << 32) | static_cast<uint32_t>(ptr_val_lo); + return true; +} + bool StackVisitor::SetVReg(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value, VRegKind kind) { if (cur_quick_frame_ != nullptr) { - DCHECK(context_ != nullptr); // You can't reliably write registers without a context. - DCHECK(m == GetMethod()); - const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); - DCHECK(code_pointer != nullptr); - const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); - QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); - uint32_t vmap_offset; - // TODO: IsInContext stops before spotting floating point registers. - if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) { - bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg); - uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask(); - const uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind); - if (!IsAccessibleRegister(reg, is_float)) { - return false; - } - bool target64 = Is64BitInstructionSet(kRuntimeISA); - // Deal with 32 or 64-bit wide registers in a way that builds on all targets. - if (target64) { - bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg); - bool wide_hi = (kind == kLongHiVReg) || (kind == kDoubleHiVReg); - if (wide_lo || wide_hi) { - uintptr_t old_reg_val = GetRegister(reg, is_float); - uint64_t new_vreg_portion = static_cast<uint64_t>(new_value); - uint64_t old_reg_val_as_wide = static_cast<uint64_t>(old_reg_val); - uint64_t mask = 0xffffffff; - if (wide_lo) { - mask = mask << 32; - } else { - new_vreg_portion = new_vreg_portion << 32; - } - new_value = static_cast<uintptr_t>((old_reg_val_as_wide & mask) | new_vreg_portion); - } + DCHECK(context_ != nullptr); // You can't reliably write registers without a context. + DCHECK(m == GetMethod()); + if (m->IsOptimized(sizeof(void*))) { + return SetVRegFromOptimizedCode(m, vreg, new_value, kind); + } else { + return SetVRegFromQuickCode(m, vreg, new_value, kind); } - SetRegister(reg, new_value, is_float); - return true; } else { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile - // its instructions? - uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(), - frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg); - DCHECK(addr != nullptr); - *addr = new_value; + cur_shadow_frame_->SetVReg(vreg, new_value); return true; } +} + +bool StackVisitor::SetVRegFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value, + VRegKind kind) { + DCHECK(context_ != nullptr); // You can't reliably write registers without a context. + DCHECK(m == GetMethod()); + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); + DCHECK(code_pointer != nullptr); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); + QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); + uint32_t vmap_offset; + // TODO: IsInContext stops before spotting floating point registers. + if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) { + bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg); + uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask(); + uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind); + return SetRegisterIfAccessible(reg, new_value, kind); } else { - DCHECK(cur_shadow_frame_ != nullptr); - cur_shadow_frame_->SetVReg(vreg, new_value); + const DexFile::CodeItem* code_item = m->GetCodeItem(); + DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile + // its instructions? + uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(), + frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg); + *addr = new_value; return true; } } +bool StackVisitor::SetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value, + VRegKind kind) { + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); + DCHECK(code_pointer != nullptr); + uint32_t native_pc_offset = m->NativeQuickPcOffset(cur_quick_frame_pc_); + CodeInfo code_info = m->GetOptimizedCodeInfo(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + const DexFile::CodeItem* code_item = m->GetCodeItem(); + DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile + // its instructions? + DCHECK_LT(vreg, code_item->registers_size_); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, + code_item->registers_size_); + DexRegisterMap::LocationKind location_kind = dex_register_map.GetLocationKind(vreg); + uint32_t dex_pc = m->ToDexPc(cur_quick_frame_pc_, false); + switch (location_kind) { + case DexRegisterMap::kInStack: { + const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg); + uint8_t* addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + offset; + *reinterpret_cast<uint32_t*>(addr) = new_value; + return true; + } + case DexRegisterMap::kInRegister: + case DexRegisterMap::kInFpuRegister: { + uint32_t reg = dex_register_map.GetMachineRegister(vreg); + return SetRegisterIfAccessible(reg, new_value, kind); + } + case DexRegisterMap::kConstant: + LOG(ERROR) << StringPrintf("Cannot change value of DEX register v%u used as a constant at " + "DEX pc 0x%x (native pc 0x%x) of method %s", + vreg, dex_pc, native_pc_offset, + PrettyMethod(cur_quick_frame_->AsMirrorPtr()).c_str()); + return false; + case DexRegisterMap::kNone: + LOG(ERROR) << StringPrintf("No location for DEX register v%u at DEX pc 0x%x " + "(native pc 0x%x) of method %s", + vreg, dex_pc, native_pc_offset, + PrettyMethod(cur_quick_frame_->AsMirrorPtr()).c_str()); + return false; + default: + LOG(FATAL) << StringPrintf("Unknown location for DEX register v%u at DEX pc 0x%x " + "(native pc 0x%x) of method %s", + vreg, dex_pc, native_pc_offset, + PrettyMethod(cur_quick_frame_->AsMirrorPtr()).c_str()); + UNREACHABLE(); + } +} + +bool StackVisitor::SetRegisterIfAccessible(uint32_t reg, uint32_t new_value, VRegKind kind) { + const bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg); + if (!IsAccessibleRegister(reg, is_float)) { + return false; + } + const bool target64 = Is64BitInstructionSet(kRuntimeISA); + + // Create a new value that can hold both low 32 and high 32 bits, in + // case we are running 64 bits. + uintptr_t full_new_value = new_value; + // Deal with 32 or 64-bit wide registers in a way that builds on all targets. + if (target64) { + bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg); + bool wide_hi = (kind == kLongHiVReg) || (kind == kDoubleHiVReg); + if (wide_lo || wide_hi) { + uintptr_t old_reg_val = GetRegister(reg, is_float); + uint64_t new_vreg_portion = static_cast<uint64_t>(new_value); + uint64_t old_reg_val_as_wide = static_cast<uint64_t>(old_reg_val); + uint64_t mask = 0xffffffff; + if (wide_lo) { + mask = mask << 32; + } else { + new_vreg_portion = new_vreg_portion << 32; + } + full_new_value = static_cast<uintptr_t>((old_reg_val_as_wide & mask) | new_vreg_portion); + } + } + SetRegister(reg, full_new_value, is_float); + return true; +} + bool StackVisitor::SetVRegPair(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value, VRegKind kind_lo, VRegKind kind_hi) { if (kind_lo == kLongLoVReg) { @@ -329,49 +470,10 @@ bool StackVisitor::SetVRegPair(mirror::ArtMethod* m, uint16_t vreg, uint64_t new if (cur_quick_frame_ != nullptr) { DCHECK(context_ != nullptr); // You can't reliably write registers without a context. DCHECK(m == GetMethod()); - const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); - DCHECK(code_pointer != nullptr); - const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); - QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); - uint32_t vmap_offset_lo, vmap_offset_hi; - // TODO: IsInContext stops before spotting floating point registers. - if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) && - vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) { - bool is_float = (kind_lo == kDoubleLoVReg); - uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask(); - uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo); - uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi); - if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) { - return false; - } - uintptr_t new_value_lo = static_cast<uintptr_t>(new_value & 0xFFFFFFFF); - uintptr_t new_value_hi = static_cast<uintptr_t>(new_value >> 32); - bool target64 = Is64BitInstructionSet(kRuntimeISA); - // Deal with 32 or 64-bit wide registers in a way that builds on all targets. - if (target64) { - uintptr_t old_reg_val_lo = GetRegister(reg_lo, is_float); - uintptr_t old_reg_val_hi = GetRegister(reg_hi, is_float); - uint64_t new_vreg_portion_lo = static_cast<uint64_t>(new_value_lo); - uint64_t new_vreg_portion_hi = static_cast<uint64_t>(new_value_hi) << 32; - uint64_t old_reg_val_lo_as_wide = static_cast<uint64_t>(old_reg_val_lo); - uint64_t old_reg_val_hi_as_wide = static_cast<uint64_t>(old_reg_val_hi); - uint64_t mask_lo = static_cast<uint64_t>(0xffffffff) << 32; - uint64_t mask_hi = 0xffffffff; - new_value_lo = static_cast<uintptr_t>((old_reg_val_lo_as_wide & mask_lo) | new_vreg_portion_lo); - new_value_hi = static_cast<uintptr_t>((old_reg_val_hi_as_wide & mask_hi) | new_vreg_portion_hi); - } - SetRegister(reg_lo, new_value_lo, is_float); - SetRegister(reg_hi, new_value_hi, is_float); - return true; + if (m->IsOptimized(sizeof(void*))) { + return SetVRegPairFromOptimizedCode(m, vreg, new_value, kind_lo, kind_hi); } else { - const DexFile::CodeItem* code_item = m->GetCodeItem(); - DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile - // its instructions? - uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(), - frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg); - DCHECK(addr != nullptr); - *reinterpret_cast<uint64_t*>(addr) = new_value; - return true; + return SetVRegPairFromQuickCode(m, vreg, new_value, kind_lo, kind_hi); } } else { DCHECK(cur_shadow_frame_ != nullptr); @@ -380,6 +482,60 @@ bool StackVisitor::SetVRegPair(mirror::ArtMethod* m, uint16_t vreg, uint64_t new } } +bool StackVisitor::SetVRegPairFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value, + VRegKind kind_lo, VRegKind kind_hi) { + const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*)); + DCHECK(code_pointer != nullptr); + const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*))); + QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer); + uint32_t vmap_offset_lo, vmap_offset_hi; + // TODO: IsInContext stops before spotting floating point registers. + if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) && + vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) { + bool is_float = (kind_lo == kDoubleLoVReg); + uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask(); + uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo); + uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi); + return SetRegisterPairIfAccessible(reg_lo, reg_hi, new_value, is_float); + } else { + const DexFile::CodeItem* code_item = m->GetCodeItem(); + DCHECK(code_item != nullptr) << PrettyMethod(m); // Can't be NULL or how would we compile + // its instructions? + uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(), + frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg); + *reinterpret_cast<uint64_t*>(addr) = new_value; + return true; + } +} + +bool StackVisitor::SetVRegPairFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value, + VRegKind kind_lo, VRegKind kind_hi) { + uint32_t low_32bits = Low32Bits(new_value); + uint32_t high_32bits = High32Bits(new_value); + bool success = SetVRegFromOptimizedCode(m, vreg, low_32bits, kind_lo); + success &= SetVRegFromOptimizedCode(m, vreg + 1, high_32bits, kind_hi); + return success; +} + +bool StackVisitor::SetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi, + uint64_t new_value, bool is_float) { + if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) { + return false; + } + uintptr_t new_value_lo = static_cast<uintptr_t>(new_value & 0xFFFFFFFF); + uintptr_t new_value_hi = static_cast<uintptr_t>(new_value >> 32); + bool target64 = Is64BitInstructionSet(kRuntimeISA); + // Deal with 32 or 64-bit wide registers in a way that builds on all targets. + if (target64) { + DCHECK_EQ(reg_lo, reg_hi); + SetRegister(reg_lo, new_value, is_float); + } else { + SetRegister(reg_lo, new_value_lo, is_float); + SetRegister(reg_hi, new_value_hi, is_float); + } + return true; +} + bool StackVisitor::IsAccessibleGPR(uint32_t reg) const { DCHECK(context_ != nullptr); return context_->IsAccessibleGPR(reg); diff --git a/runtime/stack.h b/runtime/stack.h index 5a86ca1..b495f03 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -492,7 +492,8 @@ class StackVisitor { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t val; bool success = GetVReg(m, vreg, kind, &val); - CHECK(success) << "Failed to read vreg " << vreg << " of kind " << kind; + CHECK(success) << "Failed to read v" << vreg << " of kind " << kind << " in method " + << PrettyMethod(m); return val; } @@ -505,7 +506,8 @@ class StackVisitor { uint64_t val; bool success = GetVRegPair(m, vreg, kind_lo, kind_hi, &val); CHECK(success) << "Failed to read vreg pair " << vreg - << " of kind [" << kind_lo << "," << kind_hi << "]"; + << " of kind [" << kind_lo << "," << kind_hi << "] in method " + << PrettyMethod(m); return val; } @@ -673,6 +675,45 @@ class StackVisitor { uintptr_t GetFPR(uint32_t reg) const; void SetFPR(uint32_t reg, uintptr_t value); + bool GetVRegFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, + uint32_t* val) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool GetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind, + uint32_t* val) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool GetRegisterIfAccessible(uint32_t reg, VRegKind kind, uint32_t* val) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool GetVRegPairFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind_lo, + VRegKind kind_hi, uint64_t* val) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool GetVRegPairFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, + VRegKind kind_lo, VRegKind kind_hi, + uint64_t* val) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool GetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi, VRegKind kind_lo, + uint64_t* val) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool SetVRegFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value, + VRegKind kind) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool SetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value, + VRegKind kind) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool SetRegisterIfAccessible(uint32_t reg, uint32_t new_value, VRegKind kind) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + bool SetVRegPairFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value, + VRegKind kind_lo, VRegKind kind_hi) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool SetVRegPairFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value, + VRegKind kind_lo, VRegKind kind_hi) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool SetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi, uint64_t new_value, + bool is_float) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Thread* const thread_; diff --git a/runtime/stack_map.h b/runtime/stack_map.h index fd22361..ec37699 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -104,10 +104,9 @@ class DexRegisterMap { return "in fpu register"; case kConstant: return "as constant"; - default: - LOG(FATAL) << "Invalid location kind " << static_cast<int>(kind); - return nullptr; } + UNREACHABLE(); + return nullptr; } LocationKind GetLocationKind(uint16_t register_index) const { @@ -126,6 +125,23 @@ class DexRegisterMap { kFixedSize + sizeof(LocationKind) + register_index * SingleEntrySize()); } + int32_t GetStackOffsetInBytes(uint16_t register_index) const { + DCHECK(GetLocationKind(register_index) == kInStack); + // We currently encode the offset in bytes. + return GetValue(register_index); + } + + int32_t GetConstant(uint16_t register_index) const { + DCHECK(GetLocationKind(register_index) == kConstant); + return GetValue(register_index); + } + + int32_t GetMachineRegister(uint16_t register_index) const { + DCHECK(GetLocationKind(register_index) == kInRegister + || GetLocationKind(register_index) == kInFpuRegister); + return GetValue(register_index); + } + static size_t SingleEntrySize() { return sizeof(LocationKind) + sizeof(int32_t); } diff --git a/test/454-get-vreg/expected.txt b/test/454-get-vreg/expected.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/454-get-vreg/expected.txt diff --git a/test/454-get-vreg/get_vreg_jni.cc b/test/454-get-vreg/get_vreg_jni.cc new file mode 100644 index 0000000..937d2fe --- /dev/null +++ b/test/454-get-vreg/get_vreg_jni.cc @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "arch/context.h" +#include "jni.h" +#include "mirror/art_method-inl.h" +#include "scoped_thread_state_change.h" +#include "stack.h" +#include "thread.h" + +namespace art { + +namespace { + +class TestVisitor : public StackVisitor { + public: + TestVisitor(Thread* thread, Context* context, mirror::Object* this_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : StackVisitor(thread, context), this_value_(this_value), found_method_index_(0) {} + + bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); + + if (m_name.compare("testSimpleVReg") == 0) { + found_method_index_ = 1; + uint32_t value = 0; + + CHECK(GetVReg(m, 0, kIntVReg, &value)); + CHECK_EQ(value, 42u); + + bool success = GetVReg(m, 1, kIntVReg, &value); + if (m->IsOptimized(sizeof(void*))) CHECK(!success); + + success = GetVReg(m, 2, kIntVReg, &value); + if (m->IsOptimized(sizeof(void*))) CHECK(!success); + + CHECK(GetVReg(m, 3, kReferenceVReg, &value)); + CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_); + + CHECK(GetVReg(m, 4, kIntVReg, &value)); + CHECK_EQ(value, 1u); + + CHECK(GetVReg(m, 5, kFloatVReg, &value)); + uint32_t cast = bit_cast<float, uint32_t>(1.0f); + CHECK_EQ(value, cast); + + CHECK(GetVReg(m, 6, kIntVReg, &value)); + CHECK_EQ(value, 2u); + + CHECK(GetVReg(m, 7, kIntVReg, &value)); + CHECK_EQ(value, true); + + CHECK(GetVReg(m, 8, kIntVReg, &value)); + CHECK_EQ(value, 3u); + + CHECK(GetVReg(m, 9, kIntVReg, &value)); + CHECK_EQ(value, static_cast<uint32_t>('c')); + } else if (m_name.compare("testPairVReg") == 0) { + found_method_index_ = 2; + uint64_t value = 0; + CHECK(GetVRegPair(m, 0, kLongLoVReg, kLongHiVReg, &value)); + CHECK_EQ(value, 42u); + + bool success = GetVRegPair(m, 2, kLongLoVReg, kLongHiVReg, &value); + if (m->IsOptimized(sizeof(void*))) CHECK(!success); + + success = GetVRegPair(m, 4, kLongLoVReg, kLongHiVReg, &value); + if (m->IsOptimized(sizeof(void*))) CHECK(!success); + + uint32_t value32 = 0; + CHECK(GetVReg(m, 6, kReferenceVReg, &value32)); + CHECK_EQ(reinterpret_cast<mirror::Object*>(value32), this_value_); + + CHECK(GetVRegPair(m, 7, kLongLoVReg, kLongHiVReg, &value)); + CHECK_EQ(static_cast<int64_t>(value), std::numeric_limits<int64_t>::min()); + + CHECK(GetVRegPair(m, 9, kLongLoVReg, kLongHiVReg, &value)); + CHECK_EQ(static_cast<int64_t>(value), std::numeric_limits<int64_t>::max()); + + CHECK(GetVRegPair(m, 11, kLongLoVReg, kLongHiVReg, &value)); + CHECK_EQ(value, 0u); + + CHECK(GetVRegPair(m, 13, kDoubleLoVReg, kDoubleHiVReg, &value)); + uint64_t cast = bit_cast<double, uint64_t>(2.0); + CHECK_EQ(value, cast); + } + + return true; + } + + mirror::Object* this_value_; + + // Value returned to Java to ensure the methods testSimpleVReg and testPairVReg + // have been found and tested. + jint found_method_index_; +}; + +extern "C" JNIEXPORT jint JNICALL Java_Main_doNativeCall(JNIEnv*, jobject value) { + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<Context> context(Context::Create()); + TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object*>(value)); + visitor.WalkStack(); + return visitor.found_method_index_; +} + +} // namespace + +} // namespace art diff --git a/test/454-get-vreg/info.txt b/test/454-get-vreg/info.txt new file mode 100644 index 0000000..20df0b5 --- /dev/null +++ b/test/454-get-vreg/info.txt @@ -0,0 +1 @@ +Tests for inspecting DEX registers in a Java method. diff --git a/test/454-get-vreg/src/Main.java b/test/454-get-vreg/src/Main.java new file mode 100644 index 0000000..df07d44 --- /dev/null +++ b/test/454-get-vreg/src/Main.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public Main() { + } + + int testSimpleVReg(int a, float f, short s, boolean z, byte b, char c) { + int e = doCall(); + int g = doNativeCall(); + return e + g; + } + + long testPairVReg(long a, long b, long c, double e) { + long f = doCall(); + long g = doNativeCall(); + return f + g; + } + + native int doNativeCall(); + + int doCall() { + return 42; + } + + static { + System.loadLibrary("arttest"); + } + + public static void main(String[] args) { + Main rm = new Main(); + if (rm.testSimpleVReg(1, 1.0f, (short)2, true, (byte)3, 'c') != 43) { + throw new Error("Expected 43"); + } + + if (rm.testPairVReg(Long.MIN_VALUE, Long.MAX_VALUE, 0, 2.0) != 44) { + throw new Error("Expected 44"); + } + } +} diff --git a/test/455-set-vreg/expected.txt b/test/455-set-vreg/expected.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/455-set-vreg/expected.txt diff --git a/test/455-set-vreg/info.txt b/test/455-set-vreg/info.txt new file mode 100644 index 0000000..e8c57b5 --- /dev/null +++ b/test/455-set-vreg/info.txt @@ -0,0 +1 @@ +Tests for setting DEX registers in a Java method. diff --git a/test/455-set-vreg/set_vreg_jni.cc b/test/455-set-vreg/set_vreg_jni.cc new file mode 100644 index 0000000..24d7832 --- /dev/null +++ b/test/455-set-vreg/set_vreg_jni.cc @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "arch/context.h" +#include "jni.h" +#include "mirror/art_method-inl.h" +#include "scoped_thread_state_change.h" +#include "stack.h" +#include "thread.h" + +namespace art { + +namespace { + +class TestVisitor : public StackVisitor { + public: + TestVisitor(Thread* thread, Context* context, mirror::Object* this_value) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : StackVisitor(thread, context), this_value_(this_value) {} + + bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* m = GetMethod(); + std::string m_name(m->GetName()); + + if (m_name.compare("testIntVReg") == 0) { + uint32_t value = 0; + CHECK(GetVReg(m, 1, kReferenceVReg, &value)); + CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_); + + CHECK(SetVReg(m, 2, 5, kIntVReg)); + CHECK(SetVReg(m, 3, 4, kIntVReg)); + CHECK(SetVReg(m, 4, 3, kIntVReg)); + CHECK(SetVReg(m, 5, 2, kIntVReg)); + CHECK(SetVReg(m, 6, 1, kIntVReg)); + } else if (m_name.compare("testLongVReg") == 0) { + uint32_t value = 0; + CHECK(GetVReg(m, 3, kReferenceVReg, &value)); + CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_); + + CHECK(SetVRegPair(m, 4, std::numeric_limits<int64_t>::max(), kLongLoVReg, kLongHiVReg)); + CHECK(SetVRegPair(m, 6, 4, kLongLoVReg, kLongHiVReg)); + CHECK(SetVRegPair(m, 8, 3, kLongLoVReg, kLongHiVReg)); + CHECK(SetVRegPair(m, 10, 2, kLongLoVReg, kLongHiVReg)); + CHECK(SetVRegPair(m, 12, 1, kLongLoVReg, kLongHiVReg)); + } else if (m_name.compare("testFloatVReg") == 0) { + uint32_t value = 0; + CHECK(GetVReg(m, 1, kReferenceVReg, &value)); + CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_); + + CHECK(SetVReg(m, 2, bit_cast<float, uint32_t>(5.0f), kFloatVReg)); + CHECK(SetVReg(m, 3, bit_cast<float, uint32_t>(4.0f), kFloatVReg)); + CHECK(SetVReg(m, 4, bit_cast<float, uint32_t>(3.0f), kFloatVReg)); + CHECK(SetVReg(m, 5, bit_cast<float, uint32_t>(2.0f), kFloatVReg)); + CHECK(SetVReg(m, 6, bit_cast<float, uint32_t>(1.0f), kFloatVReg)); + } else if (m_name.compare("testDoubleVReg") == 0) { + uint32_t value = 0; + CHECK(GetVReg(m, 3, kReferenceVReg, &value)); + CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_); + + CHECK(SetVRegPair(m, 4, bit_cast<double, uint64_t>(5.0), kDoubleLoVReg, kDoubleHiVReg)); + CHECK(SetVRegPair(m, 6, bit_cast<double, uint64_t>(4.0), kDoubleLoVReg, kDoubleHiVReg)); + CHECK(SetVRegPair(m, 8, bit_cast<double, uint64_t>(3.0), kDoubleLoVReg, kDoubleHiVReg)); + CHECK(SetVRegPair(m, 10, bit_cast<double, uint64_t>(2.0), kDoubleLoVReg, kDoubleHiVReg)); + CHECK(SetVRegPair(m, 12, bit_cast<double, uint64_t>(1.0), kDoubleLoVReg, kDoubleHiVReg)); + } + + return true; + } + + mirror::Object* this_value_; +}; + +extern "C" JNIEXPORT void JNICALL Java_Main_doNativeCallSetVReg(JNIEnv*, jobject value) { + ScopedObjectAccess soa(Thread::Current()); + std::unique_ptr<Context> context(Context::Create()); + TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object*>(value)); + visitor.WalkStack(); +} + +} // namespace + +} // namespace art diff --git a/test/455-set-vreg/src/Main.java b/test/455-set-vreg/src/Main.java new file mode 100644 index 0000000..2172d92 --- /dev/null +++ b/test/455-set-vreg/src/Main.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public Main() { + } + + int testIntVReg(int a, int b, int c, int d, int e) { + doNativeCallSetVReg(); + return a - b - c - d - e; + } + + long testLongVReg(long a, long b, long c, long d, long e) { + doNativeCallSetVReg(); + return a - b - c - d - e; + } + + float testFloatVReg(float a, float b, float c, float d, float e) { + doNativeCallSetVReg(); + return a - b - c - d - e; + } + + double testDoubleVReg(double a, double b, double c, double d, double e) { + doNativeCallSetVReg(); + return a - b - c - d - e; + } + + native void doNativeCallSetVReg(); + + static { + System.loadLibrary("arttest"); + } + + public static void main(String[] args) { + Main rm = new Main(); + int intExpected = 5 - 4 - 3 - 2 - 1; + int intResult = rm.testIntVReg(0, 0, 0, 0, 0); + if (intResult != intExpected) { + throw new Error("Expected " + intExpected + ", got " + intResult); + } + + long longExpected = Long.MAX_VALUE - 4 - 3 - 2 - 1; + long longResult = rm.testLongVReg(0, 0, 0, 0, 0); + if (longResult != longExpected) { + throw new Error("Expected " + longExpected + ", got " + longResult); + } + + float floatExpected = 5.0f - 4.0f - 3.0f - 2.0f - 1.0f; + float floatResult = rm.testFloatVReg(0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + if (floatResult != floatExpected) { + throw new Error("Expected " + floatExpected + ", got " + floatResult); + } + + double doubleExpected = 5.0 - 4.0 - 3.0 - 2.0 - 1.0; + double doubleResult = rm.testDoubleVReg(0.0, 0.0, 0.0, 0.0, 0.0); + if (doubleResult != doubleExpected) { + throw new Error("Expected " + doubleExpected + ", got " + doubleResult); + } + } +} diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index e64df5c..75c5d72 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -27,7 +27,9 @@ LIBARTTEST_COMMON_SRC_FILES := \ 051-thread/thread_test.cc \ 116-nodex2oat/nodex2oat.cc \ 117-nopatchoat/nopatchoat.cc \ - 118-noimage-dex2oat/noimage-dex2oat.cc + 118-noimage-dex2oat/noimage-dex2oat.cc \ + 454-get-vreg/get_vreg_jni.cc \ + 455-set-vreg/set_vreg_jni.cc ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so ifdef TARGET_2ND_ARCH diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index ee40ee8..7a2ad1c 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -361,8 +361,8 @@ if [ "$HOST" = "n" ]; then export ANDROID_DATA=$DEX_LOCATION && \ export DEX_LOCATION=$DEX_LOCATION && \ export ANDROID_ROOT=$ANDROID_ROOT && \ - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \ $mkdir_cmdline && \ + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \ $dex2oat_cmdline && \ $dalvikvm_cmdline" |