diff options
35 files changed, 2287 insertions, 20 deletions
diff --git a/build/Android.common.mk b/build/Android.common.mk index 219f1e273e..d80d039439 100644 --- a/build/Android.common.mk +++ b/build/Android.common.mk @@ -17,7 +17,7 @@ ifndef ANDROID_COMMON_MK ANDROID_COMMON_MK = true -ART_SUPPORTED_ARCH := arm mips x86 x86_64 +ART_SUPPORTED_ARCH := arm arm64 mips x86 x86_64 ifeq (,$(filter $(TARGET_ARCH),$(ART_SUPPORTED_ARCH))) $(warning unsupported TARGET_ARCH=$(TARGET_ARCH)) diff --git a/compiler/Android.mk b/compiler/Android.mk index bcd120b413..4eb9ff58f3 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -66,6 +66,7 @@ LIBART_COMPILER_SRC_FILES := \ driver/compiler_driver.cc \ driver/dex_compilation_unit.cc \ jni/quick/arm/calling_convention_arm.cc \ + jni/quick/arm64/calling_convention_arm64.cc \ jni/quick/mips/calling_convention_mips.cc \ jni/quick/x86/calling_convention_x86.cc \ jni/quick/calling_convention.cc \ diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 49c1283809..6aa85d40de 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -300,6 +300,10 @@ class CommonCompilerTest : public CommonRuntimeTest { // for ARM, do a runtime check to make sure that the features we are passed from // the build match the features we actually determine at runtime. ASSERT_EQ(instruction_set_features, runtime_features); +#elif defined(__aarch64__) + instruction_set = kArm64; + // TODO: arm64 compilation support. + compiler_options_->SetCompilerFilter(CompilerOptions::kInterpretOnly); #elif defined(__mips__) instruction_set = kMips; #elif defined(__i386__) diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index 17c2e94652..344f3ef745 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -86,6 +86,8 @@ uint32_t CompiledCode::AlignCode(uint32_t offset, InstructionSet instruction_set case kArm: case kThumb2: return RoundUp(offset, kArmAlignment); + case kArm64: + return RoundUp(offset, kArm64Alignment); case kMips: return RoundUp(offset, kMipsAlignment); case kX86: // Fall-through. @@ -100,6 +102,7 @@ uint32_t CompiledCode::AlignCode(uint32_t offset, InstructionSet instruction_set size_t CompiledCode::CodeDelta() const { switch (instruction_set_) { case kArm: + case kArm64: case kMips: case kX86: return 0; @@ -117,6 +120,7 @@ const void* CompiledCode::CodePointer(const void* code_pointer, InstructionSet instruction_set) { switch (instruction_set) { case kArm: + case kArm64: case kMips: case kX86: return code_pointer; diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 5a26064414..7890d81236 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -157,9 +157,9 @@ static CompiledMethod* CompileMethod(CompilerDriver& driver, cu.compiler_driver = &driver; cu.class_linker = class_linker; cu.instruction_set = driver.GetInstructionSet(); - cu.target64 = cu.instruction_set == kX86_64; + cu.target64 = (cu.instruction_set == kX86_64) || (cu.instruction_set == kArm64); cu.compiler = compiler; - // TODO: x86_64 is not yet implemented. + // TODO: x86_64 & arm64 are not yet implemented. DCHECK((cu.instruction_set == kThumb2) || (cu.instruction_set == kX86) || (cu.instruction_set == kMips)); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index e601a1b6b6..59754d5a50 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1871,7 +1871,7 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t if ((access_flags & kAccNative) != 0) { // Are we interpreting only and have support for generic JNI down calls? if ((compiler_options_->GetCompilerFilter() == CompilerOptions::kInterpretOnly) && - (instruction_set_ == kX86_64)) { + (instruction_set_ == kX86_64 || instruction_set_ == kArm64)) { // Leaving this empty will trigger the generic JNI version } else { compiled_method = compiler_->JniCompile(*this, access_flags, method_idx, dex_file); diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index a6daa5d00d..f6a324f8e8 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -372,6 +372,11 @@ bool ElfWriterQuick::Write(OatWriter* oat_writer, elf_header.e_flags = EF_ARM_EABI_VER5; break; } + case kArm64: { + elf_header.e_machine = EM_AARCH64; + elf_header.e_flags = 0; + break; + } case kX86: { elf_header.e_machine = EM_386; elf_header.e_flags = 0; diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc new file mode 100644 index 0000000000..c4d0d451c0 --- /dev/null +++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2014 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 "base/logging.h" +#include "calling_convention_arm64.h" +#include "utils/arm64/managed_register_arm64.h" + +namespace art { +namespace arm64 { + +// Calling convention + +ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() { + return Arm64ManagedRegister::FromCoreRegister(IP0); // X16 +} + +ManagedRegister Arm64JniCallingConvention::InterproceduralScratchRegister() { + return Arm64ManagedRegister::FromCoreRegister(IP0); // X16 +} + +static ManagedRegister ReturnRegisterForShorty(const char* shorty) { + if (shorty[0] == 'F') { + return Arm64ManagedRegister::FromSRegister(S0); + } else if (shorty[0] == 'D') { + return Arm64ManagedRegister::FromDRegister(D0); + } else if (shorty[0] == 'J') { + return Arm64ManagedRegister::FromCoreRegister(X0); + } else if (shorty[0] == 'V') { + return Arm64ManagedRegister::NoRegister(); + } else { + return Arm64ManagedRegister::FromWRegister(W0); + } +} + +ManagedRegister Arm64ManagedRuntimeCallingConvention::ReturnRegister() { + return ReturnRegisterForShorty(GetShorty()); +} + +ManagedRegister Arm64JniCallingConvention::ReturnRegister() { + return ReturnRegisterForShorty(GetShorty()); +} + +ManagedRegister Arm64JniCallingConvention::IntReturnRegister() { + return Arm64ManagedRegister::FromWRegister(W0); +} + +// Managed runtime calling convention + +ManagedRegister Arm64ManagedRuntimeCallingConvention::MethodRegister() { + return Arm64ManagedRegister::FromCoreRegister(X0); +} + +bool Arm64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() { + return false; // Everything moved to stack on entry. +} + +bool Arm64ManagedRuntimeCallingConvention::IsCurrentParamOnStack() { + return true; +} + +ManagedRegister Arm64ManagedRuntimeCallingConvention::CurrentParamRegister() { + LOG(FATAL) << "Should not reach here"; + return ManagedRegister::NoRegister(); +} + +FrameOffset Arm64ManagedRuntimeCallingConvention::CurrentParamStackOffset() { + CHECK(IsCurrentParamOnStack()); + FrameOffset result = + FrameOffset(displacement_.Int32Value() + // displacement + kPointerSize + // Method* + (itr_slots_ * kPointerSize)); // offset into in args + return result; +} + +const std::vector<ManagedRegister>& Arm64ManagedRuntimeCallingConvention::EntrySpills() { + // We spill the argument registers on ARM64 to free them up for scratch use, we then assume + // all arguments are on the stack. + if (entry_spills_.size() == 0) { + // TODO Need fp regs spilled too. + // + size_t num_spills = NumArgs(); + + // TODO Floating point need spilling too. + if (num_spills > 0) { + entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X1)); + if (num_spills > 1) { + entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X2)); + if (num_spills > 2) { + entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X3)); + if (num_spills > 3) { + entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X5)); + if (num_spills > 4) { + entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X6)); + if (num_spills > 5) { + entry_spills_.push_back(Arm64ManagedRegister::FromCoreRegister(X7)); + } + } + } + } + } + } + } + + return entry_spills_; +} +// JNI calling convention + +Arm64JniCallingConvention::Arm64JniCallingConvention(bool is_static, bool is_synchronized, + const char* shorty) + : JniCallingConvention(is_static, is_synchronized, shorty) { + // TODO This needs to be converted to 64bit. + // Compute padding to ensure longs and doubles are not split in AAPCS. Ignore the 'this' jobject + // or jclass for static methods and the JNIEnv. We start at the aligned register r2. +// size_t padding = 0; +// for (size_t cur_arg = IsStatic() ? 0 : 1, cur_reg = 2; cur_arg < NumArgs(); cur_arg++) { +// if (IsParamALongOrDouble(cur_arg)) { +// if ((cur_reg & 1) != 0) { +// padding += 4; +// cur_reg++; // additional bump to ensure alignment +// } +// cur_reg++; // additional bump to skip extra long word +// } +// cur_reg++; // bump the iterator for every argument +// } +// padding_ =0; + + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X19)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X20)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X21)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X22)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X23)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X24)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X25)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X26)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X27)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X28)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X29)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromCoreRegister(X30)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(D8)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(D9)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(D10)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(D11)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(D12)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(D13)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(D14)); + callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(D15)); +} + +uint32_t Arm64JniCallingConvention::CoreSpillMask() const { + // Compute spill mask to agree with callee saves initialized in the constructor + uint32_t result = 0; + result = 1 << X19 | 1 << X20 | 1 << X21 | 1 << X22 | 1 << X23 | 1 << X24 | 1 << X25 + | 1 << X26 | 1 << X27 | 1 << X28 | 1<< X29 | 1 << LR; + return result; +} + +ManagedRegister Arm64JniCallingConvention::ReturnScratchRegister() const { + return Arm64ManagedRegister::FromCoreRegister(X9); +} + +size_t Arm64JniCallingConvention::FrameSize() { + // Method*, LR and callee save area size, local reference segment state + size_t frame_data_size = (3 + CalleeSaveRegisters().size()) * kPointerSize; + // References plus 2 words for SIRT header + size_t sirt_size = (ReferenceCount() + 2) * kPointerSize; + // Plus return value spill area size + return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(), kStackAlignment); +} + +size_t Arm64JniCallingConvention::OutArgSize() { + return RoundUp(NumberOfOutgoingStackArgs() * kPointerSize + padding_, + kStackAlignment); +} + +// JniCallingConvention ABI follows AAPCS where longs and doubles must occur +// in even register numbers and stack slots +void Arm64JniCallingConvention::Next() { + JniCallingConvention::Next(); + size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(); + if ((itr_args_ >= 2) && + (arg_pos < NumArgs()) && + IsParamALongOrDouble(arg_pos)) { + // itr_slots_ needs to be an even number, according to AAPCS. + if ((itr_slots_ & 0x1u) != 0) { + itr_slots_++; + } + } +} + +bool Arm64JniCallingConvention::IsCurrentParamInRegister() { + return itr_slots_ < 4; +} + +bool Arm64JniCallingConvention::IsCurrentParamOnStack() { + return !IsCurrentParamInRegister(); +} + +// TODO and floating point? + +static const Register kJniArgumentRegisters[] = { + X0, X1, X2, X3, X4, X5, X6, X7 +}; +ManagedRegister Arm64JniCallingConvention::CurrentParamRegister() { + CHECK_LT(itr_slots_, 4u); + int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(); + // TODO Floating point & 64bit registers. + if ((itr_args_ >= 2) && IsParamALongOrDouble(arg_pos)) { + CHECK_EQ(itr_slots_, 2u); + return Arm64ManagedRegister::FromCoreRegister(X1); + } else { + return + Arm64ManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]); + } +} + +FrameOffset Arm64JniCallingConvention::CurrentParamStackOffset() { + CHECK_GE(itr_slots_, 4u); + size_t offset = displacement_.Int32Value() - OutArgSize() + ((itr_slots_ - 4) * kPointerSize); + CHECK_LT(offset, OutArgSize()); + return FrameOffset(offset); +} + +size_t Arm64JniCallingConvention::NumberOfOutgoingStackArgs() { + size_t static_args = IsStatic() ? 1 : 0; // count jclass + // regular argument parameters and this + size_t param_args = NumArgs() + NumLongOrDoubleArgs(); + // count JNIEnv* less arguments in registers + return static_args + param_args + 1 - 4; +} + +} // namespace arm64 +} // namespace art diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.h b/compiler/jni/quick/arm64/calling_convention_arm64.h new file mode 100644 index 0000000000..b4d050275d --- /dev/null +++ b/compiler/jni/quick/arm64/calling_convention_arm64.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 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_JNI_QUICK_ARM64_CALLING_CONVENTION_ARM64_H_ +#define ART_COMPILER_JNI_QUICK_ARM64_CALLING_CONVENTION_ARM64_H_ + +#include "jni/quick/calling_convention.h" + +namespace art { +namespace arm64 { + +class Arm64ManagedRuntimeCallingConvention : public ManagedRuntimeCallingConvention { + public: + Arm64ManagedRuntimeCallingConvention(bool is_static, bool is_synchronized, const char* shorty) + : ManagedRuntimeCallingConvention(is_static, is_synchronized, shorty) {} + virtual ~Arm64ManagedRuntimeCallingConvention() {} + // Calling convention + virtual ManagedRegister ReturnRegister(); + virtual ManagedRegister InterproceduralScratchRegister(); + // Managed runtime calling convention + virtual ManagedRegister MethodRegister(); + virtual bool IsCurrentParamInRegister(); + virtual bool IsCurrentParamOnStack(); + virtual ManagedRegister CurrentParamRegister(); + virtual FrameOffset CurrentParamStackOffset(); + virtual const std::vector<ManagedRegister>& EntrySpills(); + + private: + std::vector<ManagedRegister> entry_spills_; + + DISALLOW_COPY_AND_ASSIGN(Arm64ManagedRuntimeCallingConvention); +}; + +class Arm64JniCallingConvention : public JniCallingConvention { + public: + explicit Arm64JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty); + virtual ~Arm64JniCallingConvention() {} + // Calling convention + virtual ManagedRegister ReturnRegister(); + virtual ManagedRegister IntReturnRegister(); + virtual ManagedRegister InterproceduralScratchRegister(); + // JNI calling convention + virtual void Next(); // Override default behavior for AAPCS + virtual size_t FrameSize(); + virtual size_t OutArgSize(); + virtual const std::vector<ManagedRegister>& CalleeSaveRegisters() const { + return callee_save_regs_; + } + virtual ManagedRegister ReturnScratchRegister() const; + virtual uint32_t CoreSpillMask() const; + virtual uint32_t FpSpillMask() const { + return 0; // Floats aren't spilled in JNI down call + } + virtual bool IsCurrentParamInRegister(); + virtual bool IsCurrentParamOnStack(); + virtual ManagedRegister CurrentParamRegister(); + virtual FrameOffset CurrentParamStackOffset(); + + protected: + virtual size_t NumberOfOutgoingStackArgs(); + + private: + // TODO: these values aren't unique and can be shared amongst instances + std::vector<ManagedRegister> callee_save_regs_; + + // Padding to ensure longs and doubles are not split in AAPCS + size_t padding_; + + DISALLOW_COPY_AND_ASSIGN(Arm64JniCallingConvention); +}; + +} // namespace arm64 +} // namespace art + +#endif // ART_COMPILER_JNI_QUICK_ARM64_CALLING_CONVENTION_ARM64_H_ diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc index ac962af9e6..5856df4bc1 100644 --- a/compiler/jni/quick/calling_convention.cc +++ b/compiler/jni/quick/calling_convention.cc @@ -18,6 +18,7 @@ #include "base/logging.h" #include "jni/quick/arm/calling_convention_arm.h" +#include "jni/quick/arm64/calling_convention_arm64.h" #include "jni/quick/mips/calling_convention_mips.h" #include "jni/quick/x86/calling_convention_x86.h" #include "utils.h" @@ -37,6 +38,8 @@ ManagedRuntimeCallingConvention* ManagedRuntimeCallingConvention::Create( case kArm: case kThumb2: return new arm::ArmManagedRuntimeCallingConvention(is_static, is_synchronized, shorty); + case kArm64: + return new arm64::Arm64ManagedRuntimeCallingConvention(is_static, is_synchronized, shorty); case kMips: return new mips::MipsManagedRuntimeCallingConvention(is_static, is_synchronized, shorty); case kX86: @@ -91,6 +94,8 @@ JniCallingConvention* JniCallingConvention::Create(bool is_static, bool is_synch case kArm: case kThumb2: return new arm::ArmJniCallingConvention(is_static, is_synchronized, shorty); + case kArm64: + return new arm64::Arm64JniCallingConvention(is_static, is_synchronized, shorty); case kMips: return new mips::MipsJniCallingConvention(is_static, is_synchronized, shorty); case kX86: diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc index 3e13e44397..4dffef9f05 100644 --- a/compiler/trampolines/trampoline_compiler.cc +++ b/compiler/trampolines/trampoline_compiler.cc @@ -18,6 +18,7 @@ #include "jni_internal.h" #include "utils/arm/assembler_arm.h" +#include "utils/arm64/assembler_arm64.h" #include "utils/mips/assembler_mips.h" #include "utils/x86/assembler_x86.h" @@ -53,6 +54,46 @@ static const std::vector<uint8_t>* CreateTrampoline(EntryPointCallingConvention } } // namespace arm +namespace arm64 { +static const std::vector<uint8_t>* CreateTrampoline(EntryPointCallingConvention abi, + ThreadOffset offset) { + UniquePtr<Arm64Assembler> assembler(static_cast<Arm64Assembler*>(Assembler::Create(kArm64))); + + switch (abi) { + case kInterpreterAbi: // Thread* is first argument (X0) in interpreter ABI. + // FIXME IPx used by VIXL - this is unsafe. + __ Call(Arm64ManagedRegister::FromCoreRegister(X0), Offset(offset.Int32Value()), + Arm64ManagedRegister::FromCoreRegister(IP1)); + + break; + case kJniAbi: // Load via Thread* held in JNIEnv* in first argument (X0). + + __ LoadRawPtr(Arm64ManagedRegister::FromCoreRegister(IP1), + Arm64ManagedRegister::FromCoreRegister(X0), + Offset(JNIEnvExt::SelfOffset().Int32Value())); + + // FIXME IPx used by VIXL - this is unsafe. + __ Call(Arm64ManagedRegister::FromCoreRegister(IP1), Offset(offset.Int32Value()), + Arm64ManagedRegister::FromCoreRegister(IP0)); + + break; + case kPortableAbi: // X18 holds Thread*. + case kQuickAbi: // Fall-through. + __ Call(Arm64ManagedRegister::FromCoreRegister(TR), Offset(offset.Int32Value()), + Arm64ManagedRegister::FromCoreRegister(IP0)); + + break; + } + + size_t cs = assembler->CodeSize(); + UniquePtr<std::vector<uint8_t> > entry_stub(new std::vector<uint8_t>(cs)); + MemoryRegion code(&(*entry_stub)[0], entry_stub->size()); + assembler->FinalizeInstructions(code); + + return entry_stub.release(); +} +} // namespace arm64 + namespace mips { static const std::vector<uint8_t>* CreateTrampoline(EntryPointCallingConvention abi, ThreadOffset offset) { @@ -123,6 +164,8 @@ const std::vector<uint8_t>* CreateTrampoline(InstructionSet isa, EntryPointCalli case kArm: case kThumb2: return arm::CreateTrampoline(abi, offset); + case kArm64: + return arm64::CreateTrampoline(abi, offset); case kMips: return mips::CreateTrampoline(abi, offset); case kX86: diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h index 70df252114..2bada3fc9e 100644 --- a/compiler/utils/arm64/assembler_arm64.h +++ b/compiler/utils/arm64/assembler_arm64.h @@ -18,6 +18,7 @@ #define ART_COMPILER_UTILS_ARM64_ASSEMBLER_ARM64_H_ #include <vector> +#include <stdint.h> #include "base/logging.h" #include "constants_arm64.h" diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h index 5df37cc12e..80f17f5eb1 100644 --- a/compiler/utils/arm64/managed_register_arm64.h +++ b/compiler/utils/arm64/managed_register_arm64.h @@ -24,7 +24,7 @@ namespace art { namespace arm64 { -const int kNumberOfCoreRegIds = kNumberOfCoreRegisters; +const int kNumberOfCoreRegIds = 32; const int kNumberOfWRegIds = kNumberOfWRegisters; const int kNumberOfDRegIds = kNumberOfDRegisters; const int kNumberOfSRegIds = kNumberOfSRegisters; diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h index f02c20f208..cd4fc12e33 100644 --- a/compiler/utils/assembler.h +++ b/compiler/utils/assembler.h @@ -38,6 +38,9 @@ class AssemblerFixup; namespace arm { class ArmAssembler; } +namespace arm64 { + class Arm64Assembler; +} namespace mips { class MipsAssembler; } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 908d995451..72effded06 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -837,6 +837,8 @@ static int dex2oat(int argc, char** argv) { StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data(); if (instruction_set_str == "arm") { instruction_set = kThumb2; + } else if (instruction_set_str == "arm64") { + instruction_set = kArm64; } else if (instruction_set_str == "mips") { instruction_set = kMips; } else if (instruction_set_str == "x86") { @@ -1020,8 +1022,8 @@ static int dex2oat(int argc, char** argv) { } if (compiler_filter_string == NULL) { - if (instruction_set == kX86_64) { - // TODO: currently x86-64 is only interpreted. + if (instruction_set == kX86_64 || instruction_set == kArm64) { + // TODO: currently x86-64 and arm64 are only interpreted. compiler_filter_string = "interpret-only"; } else if (image) { compiler_filter_string = "speed"; diff --git a/runtime/Android.mk b/runtime/Android.mk index cca7d03031..1ca8e071e7 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -212,6 +212,16 @@ LIBART_TARGET_SRC_FILES_arm := \ arch/arm/thread_arm.cc \ arch/arm/fault_handler_arm.cc +LIBART_TARGET_SRC_FILES_arm64 := \ + arch/arm64/context_arm64.cc \ + arch/arm64/entrypoints_init_arm64.cc \ + arch/arm64/jni_entrypoints_arm64.S \ + arch/arm64/portable_entrypoints_arm64.S \ + arch/arm64/quick_entrypoints_arm64.S \ + arch/arm64/thread_arm64.cc \ + monitor_pool.cc \ + arch/arm64/fault_handler_arm64.cc + LIBART_TARGET_SRC_FILES_x86 := \ arch/x86/context_x86.cc \ arch/x86/entrypoints_init_x86.cc \ @@ -241,13 +251,9 @@ LIBART_TARGET_SRC_FILES_mips := \ arch/mips/thread_mips.cc \ arch/mips/fault_handler_mips.cc -ifeq ($(TARGET_ARCH),arm64) -$(info TODOArm64: $(LOCAL_PATH)/Android.mk Add Arm64 specific runtime files) -else ifeq ($(TARGET_ARCH),mips64) $(info TODOMips64: $(LOCAL_PATH)/Android.mk Add mips64 specific runtime files) endif # TARGET_ARCH != mips64 -endif # TARGET_ARCH != arm64 ifeq (,$(filter $(TARGET_ARCH),$(ART_SUPPORTED_ARCH))) $(warning unsupported TARGET_ARCH=$(TARGET_ARCH)) diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S new file mode 100644 index 0000000000..634f77791d --- /dev/null +++ b/runtime/arch/arm64/asm_support_arm64.S @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 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_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_ +#define ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_ + +#include "asm_support_arm64.h" + +.cfi_sections .debug_frame + +.macro ENTRY name + .type \name, #function + .global \name + /* Cache alignment for function entry */ + .balign 16 +\name: + .cfi_startproc +.endm + +.macro END name + .cfi_endproc + .size \name, .-\name +.endm + +.macro UNIMPLEMENTED name + ENTRY \name + brk 0 + END \name +.endm + +#endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_S_ diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h new file mode 100644 index 0000000000..44c3e6047f --- /dev/null +++ b/runtime/arch/arm64/asm_support_arm64.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 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_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_ +#define ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_ + +#include "asm_support.h" + +// TODO Thread offsets need to be checked when on Aarch64. + +// Offset of field Runtime::callee_save_methods_[kSaveAll] +#define RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET 320 +// Offset of field Runtime::callee_save_methods_[kRefsOnly] +#define RUNTIME_REFS_ONLY_CALLEE_SAVE_FRAME_OFFSET 328 +// Offset of field Runtime::callee_save_methods_[kRefsAndArgs] +#define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 336 + +// Register holding Thread::Current(). +#define xSELF x18 +// Frame Pointer +#define xFP x29 +// Link Register +#define xLR x30 +// Define the intraprocedural linkage temporary registers. +#define xIP0 x16 +#define xIP1 x17 +// Offset of field Thread::suspend_count_ verified in InitCpu +#define THREAD_FLAGS_OFFSET 0 +// Offset of field Thread::card_table_ verified in InitCpu +#define THREAD_CARD_TABLE_OFFSET 8 +// Offset of field Thread::exception_ verified in InitCpu +#define THREAD_EXCEPTION_OFFSET 16 +// Offset of field Thread::thin_lock_thread_id_ verified in InitCpu +#define THREAD_ID_OFFSET 112 + +#endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_ diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc new file mode 100644 index 0000000000..3d63c36abe --- /dev/null +++ b/runtime/arch/arm64/context_arm64.cc @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2014 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 <stdint.h> + +#include "context_arm64.h" + +#include "mirror/art_method.h" +#include "mirror/object-inl.h" +#include "stack.h" +#include "thread.h" + + +namespace art { +namespace arm64 { + +static const uint64_t gZero = 0; + +void Arm64Context::Reset() { + for (size_t i = 0; i < kNumberOfCoreRegisters; i++) { + gprs_[i] = NULL; + } + for (size_t i = 0; i < kNumberOfDRegisters; i++) { + fprs_[i] = NULL; + } + gprs_[SP] = &sp_; + gprs_[LR] = &pc_; + // Initialize registers with easy to spot debug values. + sp_ = Arm64Context::kBadGprBase + SP; + pc_ = Arm64Context::kBadGprBase + LR; +} + +void Arm64Context::FillCalleeSaves(const StackVisitor& fr) { + mirror::ArtMethod* method = fr.GetMethod(); + uint32_t core_spills = method->GetCoreSpillMask(); + uint32_t fp_core_spills = method->GetFpSpillMask(); + size_t spill_count = __builtin_popcount(core_spills); + size_t fp_spill_count = __builtin_popcount(fp_core_spills); + size_t frame_size = method->GetFrameSizeInBytes(); + + if (spill_count > 0) { + // Lowest number spill is farthest away, walk registers and fill into context. + int j = 1; + for (size_t i = 0; i < kNumberOfCoreRegisters; i++) { + if (((core_spills >> i) & 1) != 0) { + gprs_[i] = fr.CalleeSaveAddress(spill_count - j, frame_size); + j++; + } + } + } + + if (fp_spill_count > 0) { + // Lowest number spill is farthest away, walk registers and fill into context. + int j = 1; + for (size_t i = 0; i < kNumberOfDRegisters; i++) { + if (((fp_core_spills >> i) & 1) != 0) { + fprs_[i] = fr.CalleeSaveAddress(spill_count + fp_spill_count - j, frame_size); + j++; + } + } + } +} + +void Arm64Context::SetGPR(uint32_t reg, uintptr_t value) { + DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); + DCHECK_NE(gprs_[reg], &gZero); // Can't overwrite this static value since they are never reset. + DCHECK(gprs_[reg] != NULL); + *gprs_[reg] = value; +} + +void Arm64Context::SmashCallerSaves() { + // This needs to be 0 because we want a null/zero return value. + gprs_[X0] = const_cast<uint64_t*>(&gZero); + gprs_[X1] = NULL; + gprs_[X2] = NULL; + gprs_[X3] = NULL; + gprs_[X4] = NULL; + gprs_[X5] = NULL; + gprs_[X6] = NULL; + gprs_[X7] = NULL; + gprs_[X8] = NULL; + gprs_[X9] = NULL; + gprs_[X10] = NULL; + gprs_[X11] = NULL; + gprs_[X12] = NULL; + gprs_[X13] = NULL; + gprs_[X14] = NULL; + gprs_[X15] = NULL; + + fprs_[D8] = NULL; + fprs_[D9] = NULL; + fprs_[D10] = NULL; + fprs_[D11] = NULL; + fprs_[D12] = NULL; + fprs_[D13] = NULL; + fprs_[D14] = NULL; + fprs_[D15] = NULL; +} + +extern "C" void art_quick_do_long_jump(uint64_t*, uint64_t*); + +void Arm64Context::DoLongJump() { + uint64_t gprs[32]; + uint64_t fprs[32]; + + for (size_t i = 0; i < kNumberOfCoreRegisters; ++i) { + gprs[i] = gprs_[i] != NULL ? *gprs_[i] : Arm64Context::kBadGprBase + i; + } + for (size_t i = 0; i < kNumberOfDRegisters; ++i) { + fprs[i] = fprs_[i] != NULL ? *fprs_[i] : Arm64Context::kBadGprBase + i; + } + DCHECK_EQ(reinterpret_cast<uintptr_t>(Thread::Current()), gprs[TR]); + art_quick_do_long_jump(gprs, fprs); +} + +} // namespace arm64 +} // namespace art diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h new file mode 100644 index 0000000000..d40e291a69 --- /dev/null +++ b/runtime/arch/arm64/context_arm64.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 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_RUNTIME_ARCH_ARM64_CONTEXT_ARM64_H_ +#define ART_RUNTIME_ARCH_ARM64_CONTEXT_ARM64_H_ + +#include "arch/context.h" +#include "base/logging.h" +#include "registers_arm64.h" + +namespace art { +namespace arm64 { + +class Arm64Context : public Context { + public: + Arm64Context() { + Reset(); + } + + ~Arm64Context() {} + + void Reset(); + + void FillCalleeSaves(const StackVisitor& fr); + + void SetSP(uintptr_t new_sp) { + SetGPR(SP, new_sp); + } + + void SetPC(uintptr_t new_lr) { + SetGPR(LR, new_lr); + } + + virtual uintptr_t* GetGPRAddress(uint32_t reg) { + DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); + return gprs_[reg]; + } + + uintptr_t GetGPR(uint32_t reg) { + DCHECK_LT(reg, static_cast<uint32_t>(kNumberOfCoreRegisters)); + return *gprs_[reg]; + } + + void SetGPR(uint32_t reg, uintptr_t value); + void SmashCallerSaves(); + void DoLongJump(); + + private: + // Pointers to register locations, initialized to NULL or the specific registers below. + uintptr_t* gprs_[kNumberOfCoreRegisters]; + uint64_t * fprs_[kNumberOfDRegisters]; + // Hold values for sp and pc if they are not located within a stack frame. + uintptr_t sp_, pc_; +}; + +} // namespace arm64 +} // namespace art + +#endif // ART_RUNTIME_ARCH_ARM64_CONTEXT_ARM64_H_ diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc new file mode 100644 index 0000000000..2a5c7d1e8e --- /dev/null +++ b/runtime/arch/arm64/entrypoints_init_arm64.cc @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2014 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 "entrypoints/interpreter/interpreter_entrypoints.h" +#include "entrypoints/portable/portable_entrypoints.h" +#include "entrypoints/quick/quick_entrypoints.h" +#include "entrypoints/entrypoint_utils.h" +#include "entrypoints/math_entrypoints.h" + +namespace art { + +// Interpreter entrypoints. +extern "C" void artInterpreterToInterpreterBridge(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); +extern "C" void artInterpreterToCompiledCodeBridge(Thread* self, MethodHelper& mh, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, JValue* result); + +// Portable entrypoints. +extern "C" void art_portable_resolution_trampoline(mirror::ArtMethod*); +extern "C" void art_portable_to_interpreter_bridge(mirror::ArtMethod*); + +// Cast entrypoints. +extern "C" uint32_t artIsAssignableFromCode(const mirror::Class* klass, + const mirror::Class* ref_class); +extern "C" void art_quick_check_cast(void*, void*); + +// DexCache entrypoints. +extern "C" void* art_quick_initialize_static_storage(uint32_t, void*); +extern "C" void* art_quick_initialize_type(uint32_t, void*); +extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t, void*); +extern "C" void* art_quick_resolve_string(void*, uint32_t); + +// Exception entrypoints. +extern "C" void* GetAndClearException(Thread*); + +// Field entrypoints. +extern "C" int art_quick_set32_instance(uint32_t, void*, int32_t); +extern "C" int art_quick_set32_static(uint32_t, int32_t); +extern "C" int art_quick_set64_instance(uint32_t, void*, int64_t); +extern "C" int art_quick_set64_static(uint32_t, int64_t); +extern "C" int art_quick_set_obj_instance(uint32_t, void*, void*); +extern "C" int art_quick_set_obj_static(uint32_t, void*); +extern "C" int32_t art_quick_get32_instance(uint32_t, void*); +extern "C" int32_t art_quick_get32_static(uint32_t); +extern "C" int64_t art_quick_get64_instance(uint32_t, void*); +extern "C" int64_t art_quick_get64_static(uint32_t); +extern "C" void* art_quick_get_obj_instance(uint32_t, void*); +extern "C" void* art_quick_get_obj_static(uint32_t); + +// Array entrypoints. +extern "C" void art_quick_aput_obj_with_null_and_bound_check(void*, uint32_t, void*); +extern "C" void art_quick_aput_obj_with_bound_check(void*, uint32_t, void*); +extern "C" void art_quick_aput_obj(void*, uint32_t, void*); +extern "C" void art_quick_handle_fill_data(void*, void*); + +// Lock entrypoints. +extern "C" void art_quick_lock_object(void*); +extern "C" void art_quick_unlock_object(void*); + +// Math entrypoints. +extern int32_t CmpgDouble(double a, double b); +extern int32_t CmplDouble(double a, double b); +extern int32_t CmpgFloat(float a, float b); +extern int32_t CmplFloat(float a, float b); + +// Single-precision FP arithmetics. +extern "C" float fmodf(float a, float b); // REM_FLOAT[_2ADDR] + +// Double-precision FP arithmetics. +extern "C" double fmod(double a, double b); // REM_DOUBLE[_2ADDR] + +// Long long arithmetics - REM_LONG[_2ADDR] and DIV_LONG[_2ADDR] +extern "C" int64_t art_quick_mul_long(int64_t, int64_t); +extern "C" uint64_t art_quick_shl_long(uint64_t, uint32_t); +extern "C" uint64_t art_quick_shr_long(uint64_t, uint32_t); +extern "C" uint64_t art_quick_ushr_long(uint64_t, uint32_t); + +// Intrinsic entrypoints. +extern "C" int32_t __memcmp16(void*, void*, int32_t); +extern "C" int32_t art_quick_indexof(void*, uint32_t, uint32_t, uint32_t); +extern "C" int32_t art_quick_string_compareto(void*, void*); + +// Invoke entrypoints. +extern "C" void art_quick_imt_conflict_trampoline(mirror::ArtMethod*); +extern "C" void art_quick_resolution_trampoline(mirror::ArtMethod*); +extern "C" void art_quick_to_interpreter_bridge(mirror::ArtMethod*); +extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, void*); +extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); +extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*); +extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*); +extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*); + +// Thread entrypoints. +extern void CheckSuspendFromCode(Thread* thread); +extern "C" void art_quick_test_suspend(); + +// Throw entrypoints. +extern "C" void art_quick_deliver_exception(void*); +extern "C" void art_quick_throw_array_bounds(int32_t index, int32_t limit); +extern "C" void art_quick_throw_div_zero(); +extern "C" void art_quick_throw_no_such_method(int32_t method_idx); +extern "C" void art_quick_throw_null_pointer_exception(); +extern "C" void art_quick_throw_stack_overflow(void*); + +extern void ResetQuickAllocEntryPoints(QuickEntryPoints* qpoints); + +// Generic JNI downcall +extern "C" void art_quick_generic_jni_trampoline(mirror::ArtMethod*); + +void InitEntryPoints(InterpreterEntryPoints* ipoints, JniEntryPoints* jpoints, + PortableEntryPoints* ppoints, QuickEntryPoints* qpoints) { + // Interpreter + ipoints->pInterpreterToInterpreterBridge = artInterpreterToInterpreterBridge; + ipoints->pInterpreterToCompiledCodeBridge = artInterpreterToCompiledCodeBridge; + + // JNI + jpoints->pDlsymLookup = art_jni_dlsym_lookup_stub; + + // Portable + ppoints->pPortableResolutionTrampoline = art_portable_resolution_trampoline; + ppoints->pPortableToInterpreterBridge = art_portable_to_interpreter_bridge; + + // Alloc + ResetQuickAllocEntryPoints(qpoints); + + // Cast + qpoints->pInstanceofNonTrivial = artIsAssignableFromCode; + qpoints->pCheckCast = art_quick_check_cast; + + // DexCache + qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage; + qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access; + qpoints->pInitializeType = art_quick_initialize_type; + qpoints->pResolveString = art_quick_resolve_string; + + // Field + qpoints->pSet32Instance = art_quick_set32_instance; + qpoints->pSet32Static = art_quick_set32_static; + qpoints->pSet64Instance = art_quick_set64_instance; + qpoints->pSet64Static = art_quick_set64_static; + qpoints->pSetObjInstance = art_quick_set_obj_instance; + qpoints->pSetObjStatic = art_quick_set_obj_static; + qpoints->pGet32Instance = art_quick_get32_instance; + qpoints->pGet64Instance = art_quick_get64_instance; + qpoints->pGetObjInstance = art_quick_get_obj_instance; + qpoints->pGet32Static = art_quick_get32_static; + qpoints->pGet64Static = art_quick_get64_static; + qpoints->pGetObjStatic = art_quick_get_obj_static; + + // Array + qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check; + qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check; + qpoints->pAputObject = art_quick_aput_obj; + qpoints->pHandleFillArrayData = art_quick_handle_fill_data; + + // JNI + qpoints->pJniMethodStart = JniMethodStart; + qpoints->pJniMethodStartSynchronized = JniMethodStartSynchronized; + qpoints->pJniMethodEnd = JniMethodEnd; + qpoints->pJniMethodEndSynchronized = JniMethodEndSynchronized; + qpoints->pJniMethodEndWithReference = JniMethodEndWithReference; + qpoints->pJniMethodEndWithReferenceSynchronized = JniMethodEndWithReferenceSynchronized; + qpoints->pQuickGenericJniTrampoline = art_quick_generic_jni_trampoline; + + // Locks + qpoints->pLockObject = art_quick_lock_object; + qpoints->pUnlockObject = art_quick_unlock_object; + + // Math + // TODO NULL entrypoints not needed for ARM64 - generate inline. + qpoints->pCmpgDouble = CmpgDouble; + qpoints->pCmpgFloat = CmpgFloat; + qpoints->pCmplDouble = CmplDouble; + qpoints->pCmplFloat = CmplFloat; + qpoints->pFmod = fmod; + qpoints->pSqrt = sqrt; + qpoints->pL2d = NULL; + qpoints->pFmodf = fmodf; + qpoints->pL2f = NULL; + qpoints->pD2iz = NULL; + qpoints->pF2iz = NULL; + qpoints->pIdivmod = NULL; + qpoints->pD2l = NULL; + qpoints->pF2l = NULL; + qpoints->pLdiv = NULL; + qpoints->pLmod = NULL; + qpoints->pLmul = art_quick_mul_long; + qpoints->pShlLong = art_quick_shl_long; + qpoints->pShrLong = art_quick_shr_long; + qpoints->pUshrLong = art_quick_ushr_long; + + // Intrinsics + qpoints->pIndexOf = art_quick_indexof; + qpoints->pMemcmp16 = __memcmp16; + qpoints->pStringCompareTo = art_quick_string_compareto; + qpoints->pMemcpy = memcpy; + + // Invocation + qpoints->pQuickImtConflictTrampoline = art_quick_imt_conflict_trampoline; + qpoints->pQuickResolutionTrampoline = art_quick_resolution_trampoline; + qpoints->pQuickToInterpreterBridge = art_quick_to_interpreter_bridge; + qpoints->pInvokeDirectTrampolineWithAccessCheck = art_quick_invoke_direct_trampoline_with_access_check; + qpoints->pInvokeInterfaceTrampolineWithAccessCheck = art_quick_invoke_interface_trampoline_with_access_check; + qpoints->pInvokeStaticTrampolineWithAccessCheck = art_quick_invoke_static_trampoline_with_access_check; + qpoints->pInvokeSuperTrampolineWithAccessCheck = art_quick_invoke_super_trampoline_with_access_check; + qpoints->pInvokeVirtualTrampolineWithAccessCheck = art_quick_invoke_virtual_trampoline_with_access_check; + + // Thread + qpoints->pCheckSuspend = CheckSuspendFromCode; + qpoints->pTestSuspend = art_quick_test_suspend; + + // Throws + qpoints->pDeliverException = art_quick_deliver_exception; + qpoints->pThrowArrayBounds = art_quick_throw_array_bounds; + qpoints->pThrowDivZero = art_quick_throw_div_zero; + qpoints->pThrowNoSuchMethod = art_quick_throw_no_such_method; + qpoints->pThrowNullPointer = art_quick_throw_null_pointer_exception; + qpoints->pThrowStackOverflow = art_quick_throw_stack_overflow; +}; + +} // namespace art diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc new file mode 100644 index 0000000000..419e5afb09 --- /dev/null +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 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 "fault_handler.h" +#include <sys/ucontext.h> +#include "base/macros.h" +#include "globals.h" +#include "base/logging.h" +#include "base/hex_dump.h" + + +// +// ARM64 specific fault handler functions. +// + +namespace art { + +void FaultManager::GetMethodAndReturnPC(void* context, uintptr_t& method, uintptr_t& return_pc) { +} + +bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} + +bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} + +bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { + return false; +} +} // namespace art diff --git a/runtime/arch/arm64/jni_entrypoints_arm64.S b/runtime/arch/arm64/jni_entrypoints_arm64.S new file mode 100644 index 0000000000..d2ed692188 --- /dev/null +++ b/runtime/arch/arm64/jni_entrypoints_arm64.S @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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 "asm_support_arm64.S" + + /* + * Jni dlsym lookup stub. + */ + .extern artFindNativeMethod +UNIMPLEMENTED art_jni_dlsym_lookup_stub + + /* + * Entry point of native methods when JNI bug compatibility is enabled. + */ + .extern artWorkAroundAppJniBugs +UNIMPLEMENTED art_work_around_app_jni_bugs + diff --git a/runtime/arch/arm64/portable_entrypoints_arm64.S b/runtime/arch/arm64/portable_entrypoints_arm64.S new file mode 100644 index 0000000000..e136885c7e --- /dev/null +++ b/runtime/arch/arm64/portable_entrypoints_arm64.S @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 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 "asm_support_arm64.S" + + /* + * Portable invocation stub. + */ +UNIMPLEMENTED art_portable_invoke_stub + +UNIMPLEMENTED art_portable_proxy_invoke_handler + +UNIMPLEMENTED art_portable_resolution_trampoline + +UNIMPLEMENTED art_portable_to_interpreter_bridge diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S new file mode 100644 index 0000000000..2d64e7f598 --- /dev/null +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -0,0 +1,1094 @@ +/* + * Copyright (C) 2014 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 "asm_support_arm64.S" + +#include "arch/quick_alloc_entrypoints.S" + + + /* + * Macro that sets up the callee save frame to conform with + * Runtime::CreateCalleeSaveMethod(kSaveAll) + */ +.macro SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + adrp x9, :got:_ZN3art7Runtime9instance_E + ldr x9, [x9, #:got_lo12:_ZN3art7Runtime9instance_E] + + // Our registers aren't intermixed - just spill in order. + ldr x9,[x9] // x9 = & (art::Runtime * art::Runtime.instance_) . + + // x9 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] . + ldr x9, [x9, RUNTIME_SAVE_ALL_CALLEE_SAVE_FRAME_OFFSET ] + + sub sp, sp, #368 + .cfi_adjust_cfa_offset 368 + + // FP args + stp d1, d2, [sp, #8] + stp d2, d3, [sp, #24] + stp d4, d5, [sp, #40] + stp d6, d7, [sp, #56] + + // FP callee-saves + stp d8, d9, [sp, #72] + stp d10, d11, [sp, #88] + stp d12, d13, [sp, #104] + stp d14, d15, [sp, #120] + + stp d16, d17, [sp, #136] + stp d18, d19, [sp, #152] + stp d20, d21, [sp, #168] + stp d22, d23, [sp, #184] + stp d24, d25, [sp, #200] + stp d26, d27, [sp, #216] + stp d28, d29, [sp, #232] + stp d30, d31, [sp, #248] + + + // Callee saved. + stp xSELF, x19, [sp, #264] + stp x20, x21, [sp, #280] + stp x22, x23, [sp, #296] + stp x24, x25, [sp, #312] + stp x26, x27, [sp, #328] + stp x28, xFP, [sp, #344] // Save FP. + str xLR, [sp, #360] + + .cfi_offset x18,72 + .cfi_offset x19,80 + .cfi_offset x20,88 + .cfi_offset x21,96 + .cfi_offset x22,104 + .cfi_offset x23,112 + .cfi_offset x24,120 + .cfi_offset x25,128 + .cfi_offset x26,136 + .cfi_offset x27,144 + .cfi_offset x28,152 + .cfi_offset x29,160 + .cfi_offset x30,168 + + // Loads appropriate callee-save-method + str x9, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs] + +.endm + + /* + * Macro that sets up the callee save frame to conform with + * Runtime::CreateCalleeSaveMethod(kRefsOnly). + */ +.macro SETUP_REF_ONLY_CALLEE_SAVE_FRAME + brk 0 +.endm + +.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME + brk 0 +.endm + +.macro RESTORE_REF_ONLY_CALLEE_SAVE_FRAME_AND_RETURN + brk 0 +.endm + + +.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL + sub sp, sp, #304 + .cfi_adjust_cfa_offset 304 + + stp d0, d1, [sp, #16] + stp d2, d3, [sp, #32] + stp d4, d5, [sp, #48] + stp d6, d7, [sp, #64] + stp d8, d9, [sp, #80] + stp d10, d11, [sp, #96] + stp d12, d13, [sp, #112] + stp d14, d15, [sp, #128] + + stp x1, x2, [sp, #144] + stp x3, x4, [sp, #160] + stp x5, x6, [sp, #176] + stp x7, xSELF, [sp, #192] + stp x19, x20, [sp, #208] + stp x21, x22, [sp, #224] + stp x23, x24, [sp, #240] + stp x25, x26, [sp, #256] + stp x27, x28, [sp, #272] + stp xFP, xLR, [sp, #288] + + .cfi_offset x1,144 + .cfi_offset x2,152 + .cfi_offset x3,160 + .cfi_offset x4,168 + .cfi_offset x5,176 + .cfi_offset x6,184 + .cfi_offset x7,192 + .cfi_offset x18,200 + .cfi_offset x19,208 + .cfi_offset x20,216 + .cfi_offset x21,224 + .cfi_offset x22,232 + .cfi_offset x23,240 + .cfi_offset x24,248 + .cfi_offset x25,256 + .cfi_offset x26,264 + .cfi_offset x27,272 + .cfi_offset x28,280 + .cfi_offset x29,288 + .cfi_offset x30,296 +.endm + + /* + * Macro that sets up the callee save frame to conform with + * Runtime::CreateCalleeSaveMethod(kRefsAndArgs). + * + * TODO This is probably too conservative - saving FP & LR. + */ +.macro SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + adrp x9, :got:_ZN3art7Runtime9instance_E + ldr x9, [x9, #:got_lo12:_ZN3art7Runtime9instance_E] + + // Our registers aren't intermixed - just spill in order. + ldr x9,[x9] // x9 = & (art::Runtime * art::Runtime.instance_) . + + // x9 = (ArtMethod*) Runtime.instance_.callee_save_methods[kRefAndArgs] . + ldr x9, [x9, RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET ] + + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL + + str x9, [sp] // Store ArtMethod* Runtime::callee_save_methods_[kRefsAndArgs] +.endm + +.macro RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + + ldp d0, d1, [sp, #16] + ldp d2, d3, [sp, #32] + ldp d4, d5, [sp, #48] + ldp d6, d7, [sp, #64] + ldp d8, d9, [sp, #80] + ldp d10, d11, [sp, #96] + ldp d12, d13, [sp, #112] + ldp d14, d15, [sp, #128] + + // args. + ldp x1, x2, [sp, #144] + ldp x3, x4, [sp, #160] + ldp x5, x6, [sp, #176] + ldp x7, xSELF, [sp, #192] + ldp x19, x20, [sp, #208] + ldp x21, x22, [sp, #224] + ldp x23, x24, [sp, #240] + ldp x25, x26, [sp, #256] + ldp x27, x28, [sp, #272] + ldp xFP, xLR, [sp, #288] + + add sp, sp, #304 + .cfi_adjust_cfa_offset -304 +.endm + +.macro RETURN_IF_RESULT_IS_ZERO + brk 0 +.endm + +.macro RETURN_IF_RESULT_IS_NON_ZERO + brk 0 +.endm + + /* + * Macro that set calls through to artDeliverPendingExceptionFromCode, where the pending + * exception is Thread::Current()->exception_ + */ +.macro DELIVER_PENDING_EXCEPTION + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME + mov x0, xSELF + mov x1, sp + + // Point of no return. + b artDeliverPendingExceptionFromCode // artDeliverPendingExceptionFromCode(Thread*, SP) + brk 0 // Unreached +.endm + +.macro RETURN_OR_DELIVER_PENDING_EXCEPTION + ldr x9, [xSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field. + cbnz x9, 1f + ret +1: + DELIVER_PENDING_EXCEPTION +.endm + +.macro NO_ARG_RUNTIME_EXCEPTION c_name, cxx_name + .extern \cxx_name +ENTRY \c_name + brk 0 +END \c_name +.endm + +.macro ONE_ARG_RUNTIME_EXCEPTION c_name, cxx_name + .extern \cxx_name +ENTRY \c_name + brk 0 +END \c_name +.endm + +.macro TWO_ARG_RUNTIME_EXCEPTION c_name, cxx_name + .extern \cxx_name +ENTRY \c_name + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + brk 0 +END \c_name +.endm + + /* + * Called by managed code, saves callee saves and then calls artThrowException + * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception. + */ +ONE_ARG_RUNTIME_EXCEPTION art_quick_deliver_exception, artDeliverExceptionFromCode + + /* + * Called by managed code to create and deliver a NullPointerException. + */ +NO_ARG_RUNTIME_EXCEPTION art_quick_throw_null_pointer_exception, artThrowNullPointerExceptionFromCode + + /* + * Called by managed code to create and deliver an ArithmeticException. + */ +NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode + + /* + * Called by managed code to create and deliver an ArrayIndexOutOfBoundsException. Arg1 holds + * index, arg2 holds limit. + */ +TWO_ARG_RUNTIME_EXCEPTION art_quick_throw_array_bounds, artThrowArrayBoundsFromCode + + /* + * Called by managed code to create and deliver a StackOverflowError. + */ +NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode + + /* + * Called by managed code to create and deliver a NoSuchMethodError. + */ +ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode + + /* + * TODO arm64 specifics need to be fleshed out. + * All generated callsites for interface invokes and invocation slow paths will load arguments + * as usual - except instead of loading x0 with the target Method*, x0 will contain + * the method_idx. This wrapper will save x1-x3, load the caller's Method*, align the + * stack and call the appropriate C helper. + * NOTE: "this" is first visible argument of the target, and so can be found in x1. + * + * The helper will attempt to locate the target and return a result in x0 consisting + * of the target Method* in x0 and method->code_ in x1. + * + * If unsuccessful, the helper will return NULL/NULL. There will be a pending exception in the + * thread and we branch to another stub to deliver it. + * + * On success this wrapper will restore arguments and *jump* to the target, leaving the lr + * pointing back to the original caller. + */ +.macro INVOKE_TRAMPOLINE c_name, cxx_name + .extern \cxx_name +ENTRY \c_name + brk 0 +END \c_name +.endm + +INVOKE_TRAMPOLINE art_quick_invoke_interface_trampoline, artInvokeInterfaceTrampoline +INVOKE_TRAMPOLINE art_quick_invoke_interface_trampoline_with_access_check, artInvokeInterfaceTrampolineWithAccessCheck + +INVOKE_TRAMPOLINE art_quick_invoke_static_trampoline_with_access_check, artInvokeStaticTrampolineWithAccessCheck +INVOKE_TRAMPOLINE art_quick_invoke_direct_trampoline_with_access_check, artInvokeDirectTrampolineWithAccessCheck +INVOKE_TRAMPOLINE art_quick_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck +INVOKE_TRAMPOLINE art_quick_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck + +/* + * extern"C" void art_quick_invoke_stub(ArtMethod *method, x0 + * uint32_t *args, x1 + * uint32_t argsize, w2 + * Thread *self, x3 + * JValue *result, x4 + * char *shorty); x5 + * +----------------------+ + * | | + * | C/C++ frame | + * | LR'' | + * | FP'' | <- SP' + * +----------------------+ + * +----------------------+ + * | SP' | + * | X5 | + * | X4 | Saved registers + * | LR' | + * | FP' | <- FP + * +----------------------+ + * | uint32_t out[n-1] | + * | : : | Outs + * | uint32_t out[0] | + * | ArtMethod* NULL | <- SP + * +----------------------+ + * + * Outgoing registers: + * x0 - Method* + * x1-x7 - integer parameters. + * d0-d7 - Floating point parameters. + * xSELF = self + * SP = & of ArtMethod* + * x1 = "this" pointer. + * + */ +ENTRY art_quick_invoke_stub + // Spill registers as per AACPS64 calling convention. + +SAVE_SIZE=5*8 // x4, x5, LR & FP saved. +SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 + + mov x9, sp // Save stack pointer. + + mov x10, xFP // Save frame pointer + .cfi_register x29,x10 + add x11, x2, # SAVE_SIZE_AND_METHOD // calculate size of frame. + + sub x11, sp, x11 // Calculate SP position - saves + ArtMethod* + args + + and x11, x11, # ~0xf // Enforce 16 byte stack alignment. + + sub xFP, x9, #SAVE_SIZE // Calculate new FP. Don't store here until SP moved. + .cfi_def_cfa_register x29 + + mov sp, x11 // set new SP. + + str x9, [xFP, #32] // Save old stack pointer. + + .cfi_offset x9, 32 + + stp x4, x5, [xFP, #16] // Save result and shorty addresses. + + .cfi_offset x4, 16 + .cfi_offset x5, 24 + + stp x10, xLR, [xFP] // Store lr & old fp @ fp + + .cfi_offset x30, 0 + .cfi_offset x10, 8 + + mov xSELF, x3 // Move thread pointer into SELF register. + + // Copy arguments into stack frame. + // Use simple copy routine for now. + // 4 bytes per slot. + // X1 - source address + // W2 - args length + // X10 - destination address. + add x9, sp, #8 // Destination address is bottom of stack + NULL. + + // w2 = argsize parameter. +.LcopyParams: + cmp w2, #0 + beq .LendCopyParams + sub w2, w2, #4 // Need 65536 bytes of range. + ldr w10, [x1, x2] + str w10, [x9, x2] + + b .LcopyParams + +.LendCopyParams: + + // Store NULL into Method* at bottom of frame. + str xzr, [sp] + + // Fill registers x/w1 to x/w7 and s/d0 to s/d7 with parameters. + // Parse the passed shorty to determine which register to load. + // Load addresses for routines that load WXSD registers. + adr x11, .LstoreW2 + adr x12, .LstoreX2 + adr x13, .LstoreS0 + adr x14, .LstoreD0 + + // Initialize routine offsets to 0 for integers and floats. + // x8 for integers, x15 for floating point. + mov x8, #0 + mov x15, #0 + + add x10, x5, #1 // Load shorty address, plus one to skip return value. + ldr w1, [x9],#4 // Load "this" parameter, and increment arg pointer. + + // Loop to fill registers. +.LfillRegisters: + ldrb w17, [x10], #1 // Load next character in signature, and increment. + cbz w17, .LcallFunction // Exit at end of signature. Shorty 0 terminated. + + cmp w17, #'F' // is this a float? + bne .LisDouble + + cmp x15, # 8*12 // Skip this load if all registers full. + beq .LfillRegisters + + add x17, x13, x15 // Calculate subroutine to jump to. + br x17 + +.LisDouble: + cmp w17, #'D' // is this a double? + bne .LisLong + + cmp x15, # 8*12 // Skip this load if all registers full. + beq .LfillRegisters + + + add x17, x14, x15 // Calculate subroutine to jump to. + br x17 + +.LisLong: + cmp w17, #'J' // is this a long? + bne .LisOther + + cmp x8, # 7*12 // Skip this load if all registers full. + beq .LfillRegisters + + add x17, x12, x8 // Calculate subroutine to jump to. + br x17 + + +.LisOther: // Everything else takes one vReg. + cmp x8, # 7*12 // Skip this load if all registers full. + beq .LfillRegisters + add x17, x11, x8 // Calculate subroutine to jump to. + br x17 + +// Macro for loading a parameter into a register. +// counter - the register with offset into these tables +// size - the size of the register - 4 or 8 bytes. +// register - the name of the register to be loaded. +.macro LOADREG counter size register return + ldr \register , [x9], #\size + add \counter, \counter, 12 + b \return +.endm + +// Store ints. +.LstoreW2: + LOADREG x8 4 w2 .LfillRegisters + LOADREG x8 4 w3 .LfillRegisters + LOADREG x8 4 w4 .LfillRegisters + LOADREG x8 4 w5 .LfillRegisters + LOADREG x8 4 w6 .LfillRegisters + LOADREG x8 4 w7 .LfillRegisters + +// Store longs. +.LstoreX2: + LOADREG x8 8 x2 .LfillRegisters + LOADREG x8 8 x3 .LfillRegisters + LOADREG x8 8 x4 .LfillRegisters + LOADREG x8 8 x5 .LfillRegisters + LOADREG x8 8 x6 .LfillRegisters + LOADREG x8 8 x7 .LfillRegisters + +// Store singles. +.LstoreS0: + LOADREG x15 4 s0 .LfillRegisters + LOADREG x15 4 s1 .LfillRegisters + LOADREG x15 4 s2 .LfillRegisters + LOADREG x15 4 s3 .LfillRegisters + LOADREG x15 4 s4 .LfillRegisters + LOADREG x15 4 s5 .LfillRegisters + LOADREG x15 4 s6 .LfillRegisters + LOADREG x15 4 s7 .LfillRegisters + +// Store doubles. +.LstoreD0: + LOADREG x15 8 d0 .LfillRegisters + LOADREG x15 8 d1 .LfillRegisters + LOADREG x15 8 d2 .LfillRegisters + LOADREG x15 8 d3 .LfillRegisters + LOADREG x15 8 d4 .LfillRegisters + LOADREG x15 8 d5 .LfillRegisters + LOADREG x15 8 d6 .LfillRegisters + LOADREG x15 8 d7 .LfillRegisters + + +.LcallFunction: + + // load method-> METHOD_QUICK_CODE_OFFSET + ldr x9, [x0 , #METHOD_QUICK_CODE_OFFSET] + // Branch to method. + blr x9 + + // Restore return value address and shorty address. + ldp x4,x5, [xFP, #16] + .cfi_restore x4 + .cfi_restore x5 + + // Store result (w0/x0/s0/d0) appropriately, depending on resultType. + ldrb w10, [x5] + + // Don't set anything for a void type. + cmp w10, #'V' + beq .Lexit_art_quick_invoke_stub + + cmp w10, #'D' + bne .Lreturn_is_float + str d0, [x4] + b .Lexit_art_quick_invoke_stub + +.Lreturn_is_float: + cmp w10, #'F' + bne .Lreturn_is_int + str s0, [x4] + b .Lexit_art_quick_invoke_stub + + // Just store x0. Doesn't matter if it is 64 or 32 bits. +.Lreturn_is_int: + str x0, [x4] + +.Lexit_art_quick_invoke_stub: + ldr x2, [x29, #32] // Restore stack pointer. + mov sp, x2 + .cfi_restore sp + + ldp x29, x30, [x29] // Restore old frame pointer and link register. + .cfi_restore x29 + .cfi_restore x30 + + ret +END art_quick_invoke_stub + +/* extern"C" + * void art_quick_invoke_static_stub(ArtMethod *method, x0 + * uint32_t *args, x1 + * uint32_t argsize, w2 + * Thread *self, x3 + * JValue *result, x4 + * char *shorty); x5 + */ +ENTRY art_quick_invoke_static_stub + // Spill registers as per AACPS64 calling convention. + +SAVE_SIZE=5*8 // x4, x5, SP, LR & FP saved +SAVE_SIZE_AND_METHOD=SAVE_SIZE+8 + + mov x9, sp // Save stack pointer. + + mov x10, xFP // Save frame pointer + .cfi_register x29,x10 + add x11, x2, # SAVE_SIZE_AND_METHOD // calculate size of frame. + + sub x11, sp, x11 // Calculate SP position - saves + ArtMethod* + args + + and x11, x11, # ~0xf // Enforce 16 byte stack alignment. + + sub xFP, x9, #SAVE_SIZE // Calculate new FP. Don't store here until SP moved. + + mov sp, x11 // set new SP. + + .cfi_def_cfa_register 29 + + str x9, [xFP, #32] // Save old stack pointer. + + .cfi_offset x9, 32 + + stp x4, x5, [xFP, #16] // Save result and shorty addresses. + + .cfi_offset x4, 16 + .cfi_offset x5, 24 + + stp x10, xLR, [x29] // Store lr & old fp @ fp + + .cfi_offset x30, 0 + .cfi_offset x10, 8 + + mov xSELF, x3 // Move thread pointer into SELF register. + + // Copy arguments into stack frame. + // Use simple copy routine for now. + // 4 bytes per slot. + // X1 - source address + // W2 - args length + // X10 - destination address. + add x9, sp, #8 // Destination address is bottom of stack + NULL. + + // w2 = argsize parameter. +.LcopyParams2: + cmp w2, #0 + beq .LendCopyParams2 + sub w2, w2, #4 // Need 65536 bytes of range. + ldr w10, [x1, x2] + str w10, [x9, x2] + + b .LcopyParams2 + +.LendCopyParams2: + + // Store NULL into Method* at bottom of frame. + str xzr, [sp] + + // Fill registers x/w1 to x/w7 and s/d0 to s/d7 with parameters. + // Parse the passed shorty to determine which register to load. + // Load addresses for routines that load WXSD registers. + adr x11, .LstoreW1_2 + adr x12, .LstoreX1_2 + adr x13, .LstoreS0_2 + adr x14, .LstoreD0_2 + + // Initialize routine offsets to 0 for integers and floats. + // x8 for integers, x15 for floating point. + mov x8, #0 + mov x15, #0 + + add x10, x5, #1 // Load shorty address, plus one to skip return value. + + // Loop to fill registers. +.LfillRegisters2: + ldrb w17, [x10], #1 // Load next character in signature, and increment. + cbz w17, .LcallFunction2 // Exit at end of signature. Shorty 0 terminated. + + cmp w17, #'F' // is this a float? + bne .LisDouble2 + + cmp x15, # 8*12 // Skip this load if all registers full. + beq .LfillRegisters2 + + add x17, x13, x15 // Calculate subroutine to jump to. + br x17 + +.LisDouble2: + cmp w17, #'D' // is this a double? + bne .LisLong2 + + cmp x15, # 8*12 // Skip this load if all registers full. + beq .LfillRegisters2 + + + add x17, x14, x15 // Calculate subroutine to jump to. + br x17 + +.LisLong2: + cmp w17, #'J' // is this a long? + bne .LisOther2 + + cmp x8, # 7*12 // Skip this load if all registers full. + beq .LfillRegisters2 + + add x17, x12, x8 // Calculate subroutine to jump to. + br x17 + + +.LisOther2: // Everything else takes one vReg. + cmp x8, # 7*12 // Skip this load if all registers full. + beq .LfillRegisters2 + add x17, x11, x8 // Calculate subroutine to jump to. + br x17 + +// Store ints. +.LstoreW1_2: + LOADREG x8 4 w1 .LfillRegisters2 + LOADREG x8 4 w2 .LfillRegisters2 + LOADREG x8 4 w3 .LfillRegisters2 + LOADREG x8 4 w4 .LfillRegisters2 + LOADREG x8 4 w5 .LfillRegisters2 + LOADREG x8 4 w6 .LfillRegisters2 + LOADREG x8 4 w7 .LfillRegisters2 + +// Store longs. +.LstoreX1_2: + LOADREG x8 8 x1 .LfillRegisters2 + LOADREG x8 8 x2 .LfillRegisters2 + LOADREG x8 8 x3 .LfillRegisters2 + LOADREG x8 8 x4 .LfillRegisters2 + LOADREG x8 8 x5 .LfillRegisters2 + LOADREG x8 8 x6 .LfillRegisters2 + LOADREG x8 8 x7 .LfillRegisters2 + +// Store singles. +.LstoreS0_2: + LOADREG x15 4 s0 .LfillRegisters2 + LOADREG x15 4 s1 .LfillRegisters2 + LOADREG x15 4 s2 .LfillRegisters2 + LOADREG x15 4 s3 .LfillRegisters2 + LOADREG x15 4 s4 .LfillRegisters2 + LOADREG x15 4 s5 .LfillRegisters2 + LOADREG x15 4 s6 .LfillRegisters2 + LOADREG x15 4 s7 .LfillRegisters2 + +// Store doubles. +.LstoreD0_2: + LOADREG x15 8 d0 .LfillRegisters2 + LOADREG x15 8 d1 .LfillRegisters2 + LOADREG x15 8 d2 .LfillRegisters2 + LOADREG x15 8 d3 .LfillRegisters2 + LOADREG x15 8 d4 .LfillRegisters2 + LOADREG x15 8 d5 .LfillRegisters2 + LOADREG x15 8 d6 .LfillRegisters2 + LOADREG x15 8 d7 .LfillRegisters2 + + +.LcallFunction2: + + // load method-> METHOD_QUICK_CODE_OFFSET. + ldr x9, [x0 , #METHOD_QUICK_CODE_OFFSET] + // Branch to method. + blr x9 + + // Restore return value address and shorty address. + ldp x4, x5, [xFP, #16] + .cfi_restore x4 + .cfi_restore x5 + + // Store result (w0/x0/s0/d0) appropriately, depending on resultType. + ldrb w10, [x5] + + // Don't set anything for a void type. + cmp w10, #'V' + beq .Lexit_art_quick_invoke_stub2 + + cmp w10, #'D' + bne .Lreturn_is_float2 + str d0, [x4] + b .Lexit_art_quick_invoke_stub2 + +.Lreturn_is_float2: + cmp w10, #'F' + bne .Lreturn_is_int2 + str s0, [x4] + b .Lexit_art_quick_invoke_stub2 + + // Just store x0. Doesn't matter if it is 64 or 32 bits. +.Lreturn_is_int2: + str x0, [x4] + +.Lexit_art_quick_invoke_stub2: + + ldr x2, [xFP, #32] // Restore stack pointer. + mov sp, x2 + .cfi_restore sp + + ldp xFP, xLR, [xFP] // Restore old frame pointer and link register. + .cfi_restore x29 + .cfi_restore x30 + + ret +END art_quick_invoke_static_stub + +// UNIMPLEMENTED art_quick_do_long_jump + + /* + * On entry x0 is uintptr_t* gprs_ and x1 is uint64_t* fprs_ + */ + +ENTRY art_quick_do_long_jump + // Load FPRs + ldp d0, d1, [x1], #16 + ldp d2, d3, [x1], #16 + ldp d4, d5, [x1], #16 + ldp d6, d7, [x1], #16 + ldp d8, d9, [x1], #16 + ldp d10, d11, [x1], #16 + ldp d12, d13, [x1], #16 + ldp d14, d15, [x1], #16 + ldp d16, d17, [x1], #16 + ldp d18, d19, [x1], #16 + ldp d20, d21, [x1], #16 + ldp d22, d23, [x1], #16 + ldp d24, d25, [x1], #16 + ldp d26, d27, [x1], #16 + ldp d28, d29, [x1], #16 + ldp d30, d31, [x1] + + // Load GPRs + // TODO: lots of those are smashed, could optimize. + add x0, x0, #30*8 + ldp x30, x1, [x0], #-16 + ldp x28, x29, [x0], #-16 + ldp x26, x27, [x0], #-16 + ldp x24, x25, [x0], #-16 + ldp x22, x23, [x0], #-16 + ldp x20, x21, [x0], #-16 + ldp x18, x19, [x0], #-16 + ldp x16, x17, [x0], #-16 + ldp x14, x15, [x0], #-16 + ldp x12, x13, [x0], #-16 + ldp x10, x11, [x0], #-16 + ldp x8, x9, [x0], #-16 + ldp x6, x7, [x0], #-16 + ldp x4, x5, [x0], #-16 + ldp x2, x3, [x0], #-16 + mov sp, x1 + + // TODO: Is it really OK to use LR for the target PC? + mov x0, #0 + mov x1, #0 + br xLR +END art_quick_do_long_jump + +UNIMPLEMENTED art_quick_handle_fill_data + +UNIMPLEMENTED art_quick_lock_object +UNIMPLEMENTED art_quick_unlock_object +UNIMPLEMENTED art_quick_check_cast +UNIMPLEMENTED art_quick_aput_obj_with_null_and_bound_check +UNIMPLEMENTED art_quick_aput_obj_with_bound_check +UNIMPLEMENTED art_quick_aput_obj +UNIMPLEMENTED art_quick_initialize_static_storage +UNIMPLEMENTED art_quick_initialize_type +UNIMPLEMENTED art_quick_initialize_type_and_verify_access +UNIMPLEMENTED art_quick_get32_static +UNIMPLEMENTED art_quick_get64_static +UNIMPLEMENTED art_quick_get_obj_static +UNIMPLEMENTED art_quick_get32_instance +UNIMPLEMENTED art_quick_get64_instance +UNIMPLEMENTED art_quick_get_obj_instance +UNIMPLEMENTED art_quick_set32_static +UNIMPLEMENTED art_quick_set64_static +UNIMPLEMENTED art_quick_set_obj_static +UNIMPLEMENTED art_quick_set32_instance +UNIMPLEMENTED art_quick_set64_instance +UNIMPLEMENTED art_quick_set_obj_instance +UNIMPLEMENTED art_quick_resolve_string + +// Macro to facilitate adding new allocation entrypoints. +.macro TWO_ARG_DOWNCALL name, entrypoint, return + .extern \entrypoint +ENTRY \name + brk 0 +END \name +.endm + +// Macro to facilitate adding new array allocation entrypoints. +.macro THREE_ARG_DOWNCALL name, entrypoint, return + .extern \entrypoint +ENTRY \name + brk 0 +END \name +.endm + +// Generate the allocation entrypoints for each allocator. +GENERATE_ALL_ALLOC_ENTRYPOINTS + +UNIMPLEMENTED art_quick_test_suspend + +/** + * Returned by ClassLinker::GetOatCodeFor + * + */ +UNIMPLEMENTED art_quick_proxy_invoke_handler + +UNIMPLEMENTED art_quick_imt_conflict_trampoline + + +ENTRY art_quick_resolution_trampoline + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME + mov x2, xSELF + mov x3, sp + bl artQuickResolutionTrampoline // (called, receiver, Thread*, SP) + mov x9, x0 // Remember returned code pointer in x9. + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + cbz x9, 1f + br x0 +1: + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + DELIVER_PENDING_EXCEPTION +END art_quick_resolution_trampoline + +/* + * Generic JNI frame layout: + * + * #-------------------# + * | | + * | caller method... | + * #-------------------# <--- SP on entry + * | Return X30/LR | + * | X29/FP | callee save + * | X28 | callee save + * | X27 | callee save + * | X26 | callee save + * | X25 | callee save + * | X24 | callee save + * | X23 | callee save + * | X22 | callee save + * | X21 | callee save + * | X20 | callee save + * | X19 | callee save + * | X7 | arg7 + * | X6 | arg6 + * | X5 | arg5 + * | X4 | arg4 + * | X3 | arg3 + * | X2 | arg2 + * | X1 | arg1 + * | D15 | float arg 8 + * | D14 | float arg 8 + * | D13 | float arg 8 + * | D12 | callee save + * | D11 | callee save + * | D10 | callee save + * | D9 | callee save + * | D8 | callee save + * | D7 | float arg 8 + * | D6 | float arg 7 + * | D5 | float arg 6 + * | D4 | float arg 5 + * | D3 | float arg 4 + * | D2 | float arg 3 + * | D1 | float arg 2 + * | D0 | float arg 1 + * | RDI/Method* | <- X0 + * #-------------------# + * | local ref cookie | // 4B + * | SIRT size | // 4B + * #-------------------# + * | JNI Call Stack | + * #-------------------# <--- SP on native call + * | | + * | Stack for Regs | The trampoline assembly will pop these values + * | | into registers for native call + * #-------------------# + * | Native code ptr | + * #-------------------# + * | Free scratch | + * #-------------------# + * | Ptr to (1) | <--- SP + * #-------------------# + */ + /* + * Called to do a generic JNI down-call + */ +ENTRY art_quick_generic_jni_trampoline + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME_INTERNAL + str x0, [sp, #0] // Store native ArtMethod* to bottom of stack. + + // Save SP , so we can have static CFI info. + mov x28, sp + .cfi_def_cfa_register x28 + + // This looks the same, but is different: this will be updated to point to the bottom + // of the frame when the SIRT is inserted. + mov xFP, sp + + mov x8, #5120 + sub sp, sp, x8 + + // prepare for artQuickGenericJniTrampoline call + // (Thread*, SP) + // x0 x1 <= C calling convention + // xSELF xFP <= where they are + + mov x0, xSELF // Thread* + mov x1, xFP + bl artQuickGenericJniTrampoline // (Thread*, sp) + + // Get the updated pointer. This is the bottom of the frame _with_ SIRT. + ldr xFP, [sp] + add x9, sp, #8 + + cmp x0, #0 + b.mi .Lentry_error // Check for error, negative value. + + // release part of the alloca. + add x9, x9, x0 + + // Get the code pointer + ldr xIP0, [x9, #0] + + // Load parameters from frame into registers. + // TODO Check with artQuickGenericJniTrampoline. + // Also, check again APPCS64 - the stack arguments are interleaved. + ldp x0, x1, [x9, #8] + ldp x2, x3, [x9, #24] + ldp x4, x5, [x9, #40] + ldp x6, x7, [x9, #56] + + ldp d0, d1, [x9, #72] + ldp d2, d3, [x9, #88] + ldp d4, d5, [x9, #104] + ldp d6, d7, [x9, #120] + + add sp, x9, #136 + + blr xIP0 // native call. + + // Restore self pointer. + ldr xSELF, [x28, #200] + + // result sign extension is handled in C code + // prepare for artQuickGenericJniEndTrampoline call + // (Thread*, SP, result, result_f) + // x0 x1 x2 x3 <= C calling convention + mov x5, x0 // Save return value + mov x0, xSELF // Thread register + mov x1, xFP // Stack pointer + mov x2, x5 // Result (from saved) + fmov x3, d0 // d0 will contain floating point result, but needs to go into x3 + + bl artQuickGenericJniEndTrampoline + + // Tear down the alloca. + mov sp, x28 + .cfi_def_cfa_register sp + + // Restore self pointer. + ldr xSELF, [x28, #200] + + // Pending exceptions possible. + ldr x1, [xSELF, THREAD_EXCEPTION_OFFSET] + cbnz x1, .Lexception_in_native + + // Tear down the callee-save frame. + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + + // store into fpr, for when it's a fpr return... + fmov d0, x0 + ret + +.Lentry_error: + mov sp, x28 + .cfi_def_cfa_register sp + ldr xSELF, [x28, #200] +.Lexception_in_native: + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + DELIVER_PENDING_EXCEPTION + +END art_quick_generic_jni_trampoline + +/* + * Called to bridge from the quick to interpreter ABI. On entry the arguments match those + * of a quick call: + * x0 = method being called/to bridge to. + * x1..x7, d0..d7 = arguments to that method. + */ +ENTRY art_quick_to_interpreter_bridge + SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME // Set up frame and save arguments. + + // x0 will contain mirror::ArtMethod* method. + mov x1, xSELF // How to get Thread::Current() ??? + mov x2, sp + + // uint64_t artQuickToInterpreterBridge(mirror::ArtMethod* method, Thread* self, + // mirror::ArtMethod** sp) + bl artQuickToInterpreterBridge + + RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME // TODO: no need to restore arguments in this case. + + fmov d0, x0 + + RETURN_OR_DELIVER_PENDING_EXCEPTION +END art_quick_to_interpreter_bridge + +UNIMPLEMENTED art_quick_instrumentation_entry +UNIMPLEMENTED art_quick_instrumentation_exit +UNIMPLEMENTED art_quick_deoptimize +UNIMPLEMENTED art_quick_mul_long +UNIMPLEMENTED art_quick_shl_long +UNIMPLEMENTED art_quick_shr_long +UNIMPLEMENTED art_quick_ushr_long +UNIMPLEMENTED art_quick_indexof +UNIMPLEMENTED art_quick_string_compareto diff --git a/runtime/arch/arm64/registers_arm64.cc b/runtime/arch/arm64/registers_arm64.cc index c5bb06be20..87901e342b 100644 --- a/runtime/arch/arm64/registers_arm64.cc +++ b/runtime/arch/arm64/registers_arm64.cc @@ -25,18 +25,18 @@ static const char* kRegisterNames[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "ip0", "ip1", "x18", "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", "x27", "x28", "fp", - "lr", "xzr", "sp" + "lr", "sp", "xzr" }; static const char* kWRegisterNames[] = { "w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7", "w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15", "w16", "w17", "w18", "w19", "w20", "w21", "w22", "w23", "w24", "w25", "w26", "w27", "w28", "w29", - "w30", "wzr" + "w30", "wsp", "wxr" }; std::ostream& operator<<(std::ostream& os, const Register& rhs) { - if (rhs >= X0 && rhs <= SP) { + if (rhs >= X0 && rhs <= XZR) { os << kRegisterNames[rhs]; } else { os << "XRegister[" << static_cast<int>(rhs) << "]"; diff --git a/runtime/arch/arm64/registers_arm64.h b/runtime/arch/arm64/registers_arm64.h index e9460e4c4f..ca904bc947 100644 --- a/runtime/arch/arm64/registers_arm64.h +++ b/runtime/arch/arm64/registers_arm64.h @@ -61,10 +61,10 @@ enum Register { IP1 = 17, // Used as scratch by ART JNI Assembler. FP = 29, LR = 30, - XZR = 31, - SP = 32, // SP is X31 and overlaps with XRZ but we encode it as a + SP = 31, // SP is X31 and overlaps with XRZ but we encode it as a // special register, due to the different instruction semantics. - kNumberOfCoreRegisters = 33, + XZR = 32, // FIXME This needs to be reconciled with the JNI assembler. + kNumberOfCoreRegisters = 32, kNoRegister = -1, }; std::ostream& operator<<(std::ostream& os, const Register& rhs); @@ -103,6 +103,7 @@ enum WRegister { W29 = 29, W30 = 30, W31 = 31, + WSP = 31, WZR = 31, kNumberOfWRegisters = 32, kNoWRegister = -1, diff --git a/runtime/arch/arm64/thread_arm64.cc b/runtime/arch/arm64/thread_arm64.cc new file mode 100644 index 0000000000..4eebb85712 --- /dev/null +++ b/runtime/arch/arm64/thread_arm64.cc @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2014 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 "thread.h" + +#include "asm_support_arm64.h" +#include "base/logging.h" + +namespace art { + +void Thread::InitCpu() { + CHECK_EQ(THREAD_FLAGS_OFFSET, OFFSETOF_MEMBER(Thread, state_and_flags_)); + CHECK_EQ(THREAD_CARD_TABLE_OFFSET, OFFSETOF_MEMBER(Thread, card_table_)); + CHECK_EQ(THREAD_EXCEPTION_OFFSET, OFFSETOF_MEMBER(Thread, exception_)); + CHECK_EQ(THREAD_ID_OFFSET, OFFSETOF_MEMBER(Thread, thin_lock_thread_id_)); +} + +void Thread::CleanupCpu() { + // Do nothing. +} + +} // namespace art diff --git a/runtime/arch/context.cc b/runtime/arch/context.cc index 5eaf80954a..b1700bbef8 100644 --- a/runtime/arch/context.cc +++ b/runtime/arch/context.cc @@ -18,6 +18,8 @@ #if defined(__arm__) #include "arm/context_arm.h" +#elif defined(__aarch64__) +#include "arm64/context_arm64.h" #elif defined(__mips__) #include "mips/context_mips.h" #elif defined(__i386__) @@ -33,6 +35,8 @@ namespace art { Context* Context::Create() { #if defined(__arm__) return new arm::ArmContext(); +#elif defined(__aarch64__) + return new arm64::Arm64Context(); #elif defined(__mips__) return new mips::MipsContext(); #elif defined(__i386__) diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h index acc6f46ee3..f3ec713bcc 100644 --- a/runtime/elf_utils.h +++ b/runtime/elf_utils.h @@ -33,6 +33,8 @@ #define EF_MIPS_CPIC 4 #define STV_DEFAULT 0 +#define EM_AARCH64 183 + #define DT_BIND_NOW 24 #define DT_INIT_ARRAY 25 #define DT_FINI_ARRAY 26 diff --git a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc index 55fd301617..f1b15b57b0 100644 --- a/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc +++ b/runtime/entrypoints/portable/portable_trampoline_entrypoints.cc @@ -53,7 +53,8 @@ class PortableArgumentVisitor { #define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 96 #define PORTABLE_STACK_ARG_SKIP 0 #else -#error "Unsupported architecture" +// TODO: portable should be disabled for aarch64 for now. +// #error "Unsupported architecture" #define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__R1_OFFSET 0 #define PORTABLE_CALLEE_SAVE_FRAME__REF_AND_ARGS__FRAME_SIZE 0 #define PORTABLE_STACK_ARG_SKIP 0 diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 184e5e9879..20432c67cd 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -68,6 +68,38 @@ class QuickArgumentVisitor { static size_t GprIndexToGprOffset(uint32_t gpr_index) { return gpr_index * kBytesPerGprSpillLocation; } +#elif defined(__aarch64__) + // The callee save frame is pointed to by SP. + // | argN | | + // | ... | | + // | arg4 | | + // | arg3 spill | | Caller's frame + // | arg2 spill | | + // | arg1 spill | | + // | Method* | --- + // | LR | + // | X28 | + // | : | + // | X19 | + // | X7 | + // | : | + // | X1 | + // | D15 | + // | : | + // | D0 | + // | | padding + // | Method* | <- sp + static constexpr bool kQuickSoftFloatAbi = false; // This is a hard float ABI. + static constexpr size_t kNumQuickGprArgs = 7; // 7 arguments passed in GPRs. + static constexpr size_t kNumQuickFprArgs = 8; // 8 arguments passed in FPRs. + static constexpr size_t kBytesPerFprSpillLocation = 8; // FPR spill size is 8 bytes. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset =16; // Offset of first FPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 144; // Offset of first GPR arg. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 296; // Offset of return address. + static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_FrameSize = 304; // Frame size. + static size_t GprIndexToGprOffset(uint32_t gpr_index) { + return gpr_index * kBytesPerGprSpillLocation; + } #elif defined(__mips__) // The callee save frame is pointed to by SP. // | argN | | @@ -888,6 +920,17 @@ template <class T> class BuildGenericJniFrameStateMachine { static constexpr bool kMultiRegistersWidened = false; static constexpr bool kAlignLongOnStack = true; static constexpr bool kAlignDoubleOnStack = true; +#elif defined(__aarch64__) + static constexpr bool kNativeSoftFloatAbi = false; // This is a hard float ABI. + static constexpr size_t kNumNativeGprArgs = 8; // 6 arguments passed in GPRs. + static constexpr size_t kNumNativeFprArgs = 8; // 8 arguments passed in FPRs. + + static constexpr size_t kRegistersNeededForLong = 1; + static constexpr size_t kRegistersNeededForDouble = 1; + static constexpr bool kMultiRegistersAligned = false; + static constexpr bool kMultiRegistersWidened = false; + static constexpr bool kAlignLongOnStack = false; + static constexpr bool kAlignDoubleOnStack = false; #elif defined(__mips__) // TODO: These are all dummy values! static constexpr bool kNativeSoftFloatAbi = true; // This is a hard float ABI. diff --git a/runtime/globals.h b/runtime/globals.h index 5bc4b9146d..9c6fa0dec5 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -49,6 +49,10 @@ static constexpr size_t kObjectAlignment = 8; // but ARM ELF requires 8.. static constexpr size_t kArmAlignment = 8; +// ARM64 instruction alignment. AArch64 require code to be 4-byte aligned. +// AArch64 ELF requires at least 4. +static constexpr size_t kArm64Alignment = 4; + // MIPS instruction alignment. MIPS processors require code to be 4-byte aligned. // TODO: Can this be 4? static constexpr size_t kMipsAlignment = 8; diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index e8a0891f8a..7814f36075 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -37,7 +37,7 @@ namespace mirror { extern "C" void art_portable_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, char); extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, const char*); -#ifdef __x86_64__ +#ifdef __LP64__ extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*, const char*); #endif @@ -282,7 +282,7 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* : GetEntryPointFromPortableCompiledCode()); } if (!IsPortableCompiled()) { -#ifdef __x86_64__ +#ifdef __LP64__ if (!IsStatic()) { (*art_quick_invoke_stub)(this, args, args_size, self, result, shorty); } else { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 51edc85b10..eaa27defcd 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -30,6 +30,7 @@ #include <fcntl.h> #include "arch/arm/registers_arm.h" +#include "arch/arm64/registers_arm64.h" #include "arch/mips/registers_mips.h" #include "arch/x86/registers_x86.h" #include "arch/x86_64/registers_x86_64.h" @@ -1035,6 +1036,46 @@ mirror::ArtMethod* Runtime::CreateCalleeSaveMethod(InstructionSet instruction_se method->SetFrameSizeInBytes(frame_size); method->SetCoreSpillMask(core_spills); method->SetFpSpillMask(fp_spills); + } else if (instruction_set == kArm64) { + // Callee saved registers + uint32_t ref_spills = (1 << art::arm64::X19) | (1 << art::arm64::X20) | (1 << art::arm64::X21) | + (1 << art::arm64::X22) | (1 << art::arm64::X23) | (1 << art::arm64::X24) | + (1 << art::arm64::X25) | (1 << art::arm64::X26) | (1 << art::arm64::X27) | + (1 << art::arm64::X28); + // X0 is the method pointer. Not saved. + uint32_t arg_spills = (1 << art::arm64::X1) | (1 << art::arm64::X2) | (1 << art::arm64::X3) | + (1 << art::arm64::X4) | (1 << art::arm64::X5) | (1 << art::arm64::X6) | + (1 << art::arm64::X7); + // TODO This is conservative. Only ALL should include the thread register. + // The thread register is not preserved by the aapcs64. + // LR is always saved. + uint32_t all_spills = 0; // (1 << art::arm64::LR); + uint32_t core_spills = ref_spills | (type == kRefsAndArgs ? arg_spills : 0) | + (type == kSaveAll ? all_spills : 0) | (1 << art::arm64::FP) + | (1 << art::arm64::X18) | (1 << art::arm64::LR); + + // Save callee-saved floating point registers. Rest are scratch/parameters. + uint32_t fp_arg_spills = (1 << art::arm64::D0) | (1 << art::arm64::D1) | (1 << art::arm64::D2) | + (1 << art::arm64::D3) | (1 << art::arm64::D4) | (1 << art::arm64::D5) | + (1 << art::arm64::D6) | (1 << art::arm64::D7); + uint32_t fp_ref_spills = (1 << art::arm64::D8) | (1 << art::arm64::D9) | (1 << art::arm64::D10) | + (1 << art::arm64::D11) | (1 << art::arm64::D12) | (1 << art::arm64::D13) | + (1 << art::arm64::D14) | (1 << art::arm64::D15); + uint32_t fp_all_spills = fp_arg_spills | + (1 << art::arm64::D16) | (1 << art::arm64::D17) | (1 << art::arm64::D18) | + (1 << art::arm64::D19) | (1 << art::arm64::D20) | (1 << art::arm64::D21) | + (1 << art::arm64::D22) | (1 << art::arm64::D23) | (1 << art::arm64::D24) | + (1 << art::arm64::D25) | (1 << art::arm64::D26) | (1 << art::arm64::D27) | + (1 << art::arm64::D28) | (1 << art::arm64::D29) | (1 << art::arm64::D30) | + (1 << art::arm64::D31); + uint32_t fp_spills = fp_ref_spills | (type == kRefsAndArgs ? fp_arg_spills: 0) + | (type == kSaveAll ? fp_all_spills : 0); + size_t frame_size = RoundUp((__builtin_popcount(core_spills) /* gprs */ + + __builtin_popcount(fp_spills) /* fprs */ + + 1 /* Method* */) * kPointerSize, kStackAlignment); + method->SetFrameSizeInBytes(frame_size); + method->SetCoreSpillMask(core_spills); + method->SetFpSpillMask(fp_spills); } else { UNIMPLEMENTED(FATAL) << instruction_set; } |