Files
Ilya Trubachev 333ccf0f98 copyright update
Signed-off-by: Ilya Trubachev <trubachev.ilya@huawei.com>
2022-03-25 13:17:51 +03:00

722 lines
22 KiB
C++

/*
* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* 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 PANDA_RUNTIME_INCLUDE_METHOD_H_
#define PANDA_RUNTIME_INCLUDE_METHOD_H_
#include <atomic>
#include <cstdint>
#include <functional>
#include <string_view>
#include "intrinsics.h"
#include "libpandabase/utils/arch.h"
#include "libpandabase/utils/logger.h"
#include "libpandafile/code_data_accessor-inl.h"
#include "libpandafile/file.h"
#include "libpandafile/file_items.h"
#include "libpandafile/modifiers.h"
#include "runtime/bridge/bridge.h"
#include "runtime/include/mem/panda_containers.h"
#include "runtime/include/mem/panda_smart_pointers.h"
#include "runtime/interpreter/frame.h"
#include "libpandabase/utils/aligned_storage.h"
#include "value.h"
namespace panda {
class Class;
class ManagedThread;
class ProfilingData;
#ifdef PANDA_ENABLE_GLOBAL_REGISTER_VARIABLES
namespace interpreter {
class AccVRegister;
} // namespace interpreter
using interpreter::AccVRegister;
#else
namespace interpreter {
using AccVRegister = Frame::VRegister;
} // namespace interpreter
#endif
using FrameDeleter = void (*)(Frame *);
class Method {
public:
using UniqId = uint64_t;
enum CompilationStage {
NOT_COMPILED,
WAITING,
COMPILATION,
COMPILED,
FAILED,
};
enum class VerificationStage {
// There is a separate bit allocated for each state. Totally 3 bits are used.
// When the method is not verified all bits are zero.
// The next state is waiting for verification uses 2nd bit.
// The final result (ok or fail) is stored in 1st and 0th bits.
// State is changing as follow:
// 000 -> 100 --+--> 110
// (not verified) (waiting) | (ok)
// |
// +--> 101
// (fail)
// To read the state __builtin_ffs is used which returns index + 1 of the first set bit
// or zero for 0 value. See BitsToVerificationStage for details about conversion set bit
// index to VerificationStage.
// So the value's order is chosen in a such way in the early stage must have highest value.
NOT_VERIFIED = 0,
VERIFIED_FAIL = 1,
VERIFIED_OK = 2,
WAITING = 4,
};
enum AnnotationField : uint32_t {
IC_SIZE = 0,
FUNCTION_LENGTH,
FUNCTION_NAME,
STRING_DATA_BEGIN = FUNCTION_NAME,
STRING_DATA_END = FUNCTION_NAME
};
class Proto {
public:
Proto(const panda_file::File &pf, panda_file::File::EntityId proto_id);
Proto(PandaVector<panda_file::Type> shorty, PandaVector<std::string_view> ref_types)
: shorty_(std::move(shorty)), ref_types_(std::move(ref_types))
{
}
bool operator==(const Proto &other) const
{
return shorty_ == other.shorty_ && ref_types_ == other.ref_types_;
}
panda_file::Type GetReturnType() const
{
return shorty_[0];
}
std::string_view GetReturnTypeDescriptor() const;
const PandaVector<panda_file::Type> &GetShorty() const
{
return shorty_;
}
const PandaVector<std::string_view> &GetRefTypes() const
{
return ref_types_;
}
~Proto() = default;
DEFAULT_COPY_SEMANTIC(Proto);
DEFAULT_MOVE_SEMANTIC(Proto);
private:
PandaVector<panda_file::Type> shorty_;
PandaVector<std::string_view> ref_types_;
};
Method(Class *klass, const panda_file::File *pf, panda_file::File::EntityId file_id,
panda_file::File::EntityId code_id, uint32_t access_flags, uint32_t num_args, const uint16_t *shorty);
explicit Method(const Method *method)
: stor_32_ {{},
method->stor_32_.access_flags_.load(),
method->stor_32_.vtable_index_,
method->stor_32_.num_args_,
0},
stor_ptr_ {{}, method->stor_ptr_.class_, nullptr, method->stor_ptr_.native_pointer_},
panda_file_(method->panda_file_),
file_id_(method->file_id_),
code_id_(method->code_id_),
shorty_(method->shorty_)
{
stor_ptr_.compiled_entry_point_.store(method->IsNative() ? method->GetCompiledEntryPoint()
: GetCompiledCodeToInterpreterBridge(method),
std::memory_order_release);
SetCompilationStatus(CompilationStage::NOT_COMPILED);
}
Method() = delete;
Method(const Method &) = delete;
Method(Method &&) = delete;
Method &operator=(const Method &) = delete;
Method &operator=(Method &&) = delete;
uint32_t GetNumArgs() const
{
return stor_32_.num_args_;
}
uint32_t GetNumVregs() const
{
if (!code_id_.IsValid()) {
return 0;
}
panda_file::CodeDataAccessor cda(*panda_file_, code_id_);
return cda.GetNumVregs();
}
uint32_t GetCodeSize() const
{
if (!code_id_.IsValid()) {
return 0;
}
panda_file::CodeDataAccessor cda(*panda_file_, code_id_);
return cda.GetCodeSize();
}
const uint8_t *GetInstructions() const
{
if (!code_id_.IsValid()) {
return nullptr;
}
panda_file::CodeDataAccessor cda(*panda_file_, code_id_);
return cda.GetInstructions();
}
/*
* Invoke the method as a static method.
* Number of arguments and their types must match the method's signature
*/
Value Invoke(ManagedThread *thread, Value *args, bool proxy_call = false);
void InvokeVoid(ManagedThread *thread, Value *args)
{
Invoke(thread, args);
}
/*
* Invoke the method as a dynamic function.
* Number of arguments may vary, all arguments must be of type DecodedTaggedValue.
* args - array of arguments. The first value must be the callee function object
* num_args - length of args array
* data - ConstantPool for JS. For other languages is not used at the moment
*/
Value InvokeDyn(ManagedThread *thread, uint32_t num_args, Value *args, bool proxy_call = false,
void *data = nullptr);
/*
* Using in JS for generators
* num_actual_args - length of args array
* args - array of arguments.
* data - ConstantPool for JS. For other languages is not used at the moment
*/
Value InvokeGen(ManagedThread *thread, const uint8_t *pc, Value acc, uint32_t num_actual_args, Value *args,
void *data);
Class *GetClass() const
{
return stor_ptr_.class_;
}
void SetClass(Class *cls)
{
stor_ptr_.class_ = cls;
}
void SetPandaFile(const panda_file::File *file)
{
panda_file_ = file;
}
const panda_file::File *GetPandaFile() const
{
return panda_file_;
}
panda_file::File::EntityId GetFileId() const
{
return file_id_;
}
panda_file::File::EntityId GetCodeId() const
{
return code_id_;
}
inline uint32_t GetHotnessCounter() const
{
return stor_32_.hotness_counter_;
}
inline NO_THREAD_SANITIZE void IncrementHotnessCounter()
{
++stor_32_.hotness_counter_;
}
NO_THREAD_SANITIZE void ResetHotnessCounter()
{
stor_32_.hotness_counter_ = 0;
}
template <class AccVRegisterPtrT>
NO_THREAD_SANITIZE void SetAcc([[maybe_unused]] AccVRegisterPtrT acc);
// NO_THREAD_SANITIZE because of performance degradation (see commit 7c913cb1 and MR 997#note_113500)
template <class AccVRegisterPtrT>
NO_THREAD_SANITIZE bool IncrementHotnessCounter([[maybe_unused]] uintptr_t bytecode_offset,
[[maybe_unused]] AccVRegisterPtrT cc,
[[maybe_unused]] bool osr = false);
inline NO_THREAD_SANITIZE void SetHotnessCounter(size_t counter)
{
stor_32_.hotness_counter_ = counter;
}
const void *GetCompiledEntryPoint()
{
return stor_ptr_.compiled_entry_point_.load(std::memory_order_acquire);
}
const void *GetCompiledEntryPoint() const
{
return stor_ptr_.compiled_entry_point_.load(std::memory_order_acquire);
}
void SetCompiledEntryPoint(const void *entry_point)
{
stor_ptr_.compiled_entry_point_.store(entry_point, std::memory_order_release);
}
void SetInterpreterEntryPoint()
{
if (!IsNative()) {
SetCompiledEntryPoint(GetCompiledCodeToInterpreterBridge(this));
}
}
bool HasCompiledCode() const
{
return GetCompiledEntryPoint() != GetCompiledCodeToInterpreterBridge(this);
}
inline CompilationStage GetCompilationStatus() const
{
return static_cast<CompilationStage>((stor_32_.access_flags_.load() & COMPILATION_STATUS_MASK) >>
COMPILATION_STATUS_SHIFT);
}
inline CompilationStage GetCompilationStatus(uint32_t value)
{
return static_cast<CompilationStage>((value & COMPILATION_STATUS_MASK) >> COMPILATION_STATUS_SHIFT);
}
inline void SetCompilationStatus(enum CompilationStage new_status)
{
stor_32_.access_flags_ &= ~COMPILATION_STATUS_MASK;
stor_32_.access_flags_ |= static_cast<uint32_t>(new_status) << COMPILATION_STATUS_SHIFT;
}
inline bool AtomicSetCompilationStatus(enum CompilationStage old_status, enum CompilationStage new_status)
{
uint32_t old_value = stor_32_.access_flags_.load();
while (GetCompilationStatus(old_value) == old_status) {
uint32_t new_value = MakeCompilationStatusValue(old_value, new_status);
if (stor_32_.access_flags_.compare_exchange_strong(old_value, new_value)) {
return true;
}
}
return false;
}
panda_file::Type GetReturnType() const;
// idx - index number of the argument in the signature
panda_file::Type GetArgType(size_t idx) const;
panda_file::File::StringData GetRefArgType(size_t idx) const;
template <typename Callback>
void EnumerateTypes(Callback handler) const;
panda_file::File::StringData GetName() const;
panda_file::File::StringData GetClassName() const;
PandaString GetFullName(bool with_signature = false) const;
uint32_t GetFullNameHash() const;
static uint32_t GetFullNameHashFromString(const uint8_t *str);
static uint32_t GetClassNameHashFromString(const uint8_t *str);
Proto GetProto() const;
size_t GetFrameSize() const
{
return Frame::GetSize(GetNumArgs() + GetNumVregs());
}
uint32_t GetNumericalAnnotation(AnnotationField field_id) const;
panda_file::File::StringData GetStringDataAnnotation(AnnotationField field_id) const;
uint32_t GetAccessFlags() const
{
return stor_32_.access_flags_.load();
}
void SetAccessFlags(uint32_t access_flags)
{
stor_32_.access_flags_ = access_flags;
}
bool IsStatic() const
{
return (stor_32_.access_flags_.load() & ACC_STATIC) != 0;
}
bool IsNative() const
{
return (stor_32_.access_flags_.load() & ACC_NATIVE) != 0;
}
bool IsPublic() const
{
return (stor_32_.access_flags_.load() & ACC_PUBLIC) != 0;
}
bool IsPrivate() const
{
return (stor_32_.access_flags_.load() & ACC_PRIVATE) != 0;
}
bool IsProtected() const
{
return (stor_32_.access_flags_.load() & ACC_PROTECTED) != 0;
}
bool IsIntrinsic() const
{
return (stor_32_.access_flags_.load() & ACC_INTRINSIC) != 0;
}
bool IsSynthetic() const
{
return (stor_32_.access_flags_.load() & ACC_SYNTHETIC) != 0;
}
bool IsAbstract() const
{
return (stor_32_.access_flags_.load() & ACC_ABSTRACT) != 0;
}
bool IsFinal() const
{
return (stor_32_.access_flags_.load() & ACC_FINAL) != 0;
}
bool IsSynchronized() const
{
return (stor_32_.access_flags_.load() & ACC_SYNCHRONIZED) != 0;
}
bool HasSingleImplementation() const
{
return (stor_32_.access_flags_.load() & ACC_SINGLE_IMPL) != 0;
}
void SetHasSingleImplementation(bool v)
{
if (v) {
stor_32_.access_flags_ |= ACC_SINGLE_IMPL;
} else {
stor_32_.access_flags_ &= ~ACC_SINGLE_IMPL;
}
}
Method *GetSingleImplementation()
{
return HasSingleImplementation() ? this : nullptr;
}
void SetIntrinsic(intrinsics::Intrinsic intrinsic)
{
ASSERT(!IsIntrinsic());
ASSERT((stor_32_.access_flags_.load() & INTRINSIC_MASK) == 0);
stor_32_.access_flags_ |= ACC_INTRINSIC;
stor_32_.access_flags_ |= static_cast<uint32_t>(intrinsic) << INTRINSIC_SHIFT;
}
intrinsics::Intrinsic GetIntrinsic() const
{
ASSERT(IsIntrinsic());
return static_cast<intrinsics::Intrinsic>((stor_32_.access_flags_.load() & INTRINSIC_MASK) >> INTRINSIC_SHIFT);
}
void SetVTableIndex(uint32_t vtable_index)
{
stor_32_.vtable_index_ = vtable_index;
}
uint32_t GetVTableIndex() const
{
return stor_32_.vtable_index_;
}
void SetNativePointer(void *native_pointer)
{
using AtomicType = std::atomic<decltype(GetNativePointer())>;
auto *atomic_native_pointer = reinterpret_cast<AtomicType *>(&stor_ptr_.native_pointer_);
atomic_native_pointer->store(native_pointer, std::memory_order_relaxed);
}
void *GetNativePointer() const
{
using AtomicType = std::atomic<decltype(GetNativePointer())>;
auto *atomic_native_pointer = reinterpret_cast<const AtomicType *>(&stor_ptr_.native_pointer_);
return atomic_native_pointer->load();
}
const uint16_t *GetShorty() const
{
return shorty_;
}
uint32_t FindCatchBlock(Class *cls, uint32_t pc) const;
panda_file::Type GetEffectiveArgType(size_t idx) const;
panda_file::Type GetEffectiveReturnType() const;
void SetIsDefaultInterfaceMethod()
{
stor_32_.access_flags_ |= ACC_DEFAULT_INTERFACE_METHOD;
}
bool IsDefaultInterfaceMethod() const
{
return (stor_32_.access_flags_.load() & ACC_DEFAULT_INTERFACE_METHOD) != 0;
}
bool IsConstructor() const
{
return (stor_32_.access_flags_.load() & ACC_CONSTRUCTOR) != 0;
}
bool IsInstanceConstructor() const
{
return IsConstructor() && !IsStatic();
}
bool IsStaticConstructor() const
{
return IsConstructor() && IsStatic();
}
static constexpr uint32_t GetCompilerEntryPointOffset(Arch arch)
{
return MEMBER_OFFSET(Method, stor_ptr_) +
StoragePackedPtr::ConvertOffset(PointerSize(arch),
MEMBER_OFFSET(StoragePackedPtr, compiled_entry_point_));
}
static constexpr uint32_t GetNativePointerOffset(Arch arch)
{
return MEMBER_OFFSET(Method, stor_ptr_) +
StoragePackedPtr::ConvertOffset(PointerSize(arch), MEMBER_OFFSET(StoragePackedPtr, native_pointer_));
}
static constexpr uint32_t GetClassOffset(Arch arch)
{
return MEMBER_OFFSET(Method, stor_ptr_) +
StoragePackedPtr::ConvertOffset(PointerSize(arch), MEMBER_OFFSET(StoragePackedPtr, class_));
}
static constexpr uint32_t GetAccessFlagsOffset()
{
return MEMBER_OFFSET(Method, stor_32_) + MEMBER_OFFSET(StoragePacked32, access_flags_);
}
static constexpr uint32_t GetNumArgsOffset()
{
return MEMBER_OFFSET(Method, stor_32_) + MEMBER_OFFSET(StoragePacked32, num_args_);
}
static constexpr uint32_t GetShortyOffset()
{
return MEMBER_OFFSET(Method, shorty_);
}
static constexpr uint32_t GetVTableIndexOffset()
{
return MEMBER_OFFSET(Method, stor_32_) + MEMBER_OFFSET(StoragePacked32, vtable_index_);
}
template <typename Callback>
void EnumerateTryBlocks(Callback callback) const;
template <typename Callback>
void EnumerateCatchBlocks(Callback callback) const;
template <typename Callback>
void EnumerateExceptionHandlers(Callback callback) const;
static inline UniqId CalcUniqId(const panda_file::File *file, panda_file::File::EntityId file_id)
{
constexpr uint64_t HALF = 32ULL;
uint64_t uid = file->GetUniqId();
uid <<= HALF;
uid |= file_id.GetOffset();
return uid;
}
// for synthetic methods, like arrays .ctor
static UniqId CalcUniqId(const uint8_t *class_descr, const uint8_t *name);
UniqId GetUniqId() const
{
return CalcUniqId(panda_file_, file_id_);
}
int32_t GetLineNumFromBytecodeOffset(uint32_t bc_offset) const;
panda_file::File::StringData GetClassSourceFile() const;
void StartProfiling();
void StopProfiling();
ProfilingData *GetProfilingData()
{
return profiling_data_.load(std::memory_order_acquire);
}
const ProfilingData *GetProfilingData() const
{
return profiling_data_.load(std::memory_order_acquire);
}
bool IsProfiling() const
{
return GetProfilingData() != nullptr;
}
bool IsProfilingWithoutLock() const
{
return profiling_data_.load() != nullptr;
}
bool AddJobInQueue();
void WaitForVerification();
void SetVerified(bool result);
bool IsVerified() const;
bool Verify();
void EnqueueForVerification();
~Method();
private:
VerificationStage GetVerificationStage() const;
void SetVerificationStage(VerificationStage stage);
VerificationStage ExchangeVerificationStage(VerificationStage stage);
static VerificationStage BitsToVerificationStage(uint32_t bits);
template <bool is_dynamic>
Value InvokeCompiledCode(ManagedThread *thread, uint32_t num_actual_args, Value *args);
Value GetReturnValueFromTaggedValue(DecodedTaggedValue ret_value)
{
Value res(static_cast<int64_t>(0));
panda_file::Type ret_type = GetReturnType();
if (ret_type.GetId() != panda_file::Type::TypeId::VOID) {
if (ret_type.GetId() == panda_file::Type::TypeId::REFERENCE) {
res = Value(reinterpret_cast<ObjectHeader *>(ret_value.value));
} else if (ret_type.GetId() == panda_file::Type::TypeId::TAGGED) {
res = Value(ret_value.value, ret_value.tag);
} else {
res = Value(ret_value.value);
}
}
return res;
}
inline static uint32_t MakeCompilationStatusValue(uint32_t value, CompilationStage new_status)
{
value &= ~COMPILATION_STATUS_MASK;
value |= static_cast<uint32_t>(new_status) << COMPILATION_STATUS_SHIFT;
return value;
}
template <bool is_dynamic>
Value InvokeInterpretedCode(ManagedThread *thread, uint32_t num_actual_args, Value *args, void *data = nullptr);
template <bool is_dynamic>
PandaUniquePtr<Frame, FrameDeleter> InitFrame(ManagedThread *thread, uint32_t num_actual_args, Value *args,
Frame *current_frame, void *data = nullptr);
Value GetReturnValueFromAcc(const panda_file::Type &ret_type, bool has_pending_exception,
const Frame::VRegister &ret_value)
{
if (UNLIKELY(has_pending_exception)) {
if (ret_type.IsReference()) {
return Value(nullptr);
}
return Value(static_cast<int64_t>(0));
}
if (ret_type.GetId() != panda_file::Type::TypeId::VOID) {
if (ret_type.GetId() == panda_file::Type::TypeId::TAGGED) {
return Value(ret_value.GetValue(), ret_value.GetTag());
}
if (ret_value.HasObject()) {
return Value(ret_value.GetReference());
}
return Value(ret_value.GetLong());
}
return Value(static_cast<int64_t>(0));
}
template <bool is_dynamic>
Value InvokeImpl(ManagedThread *thread, uint32_t num_actual_args, Value *args, bool proxy_call,
void *data = nullptr);
private:
static constexpr size_t STORAGE_32_NUM = 4;
static constexpr size_t STORAGE_PTR_NUM = 3;
struct StoragePacked32 : public AlignedStorage<sizeof(uint64_t), sizeof(uint32_t), STORAGE_32_NUM> {
Aligned<std::atomic_uint32_t> access_flags_;
Aligned<uint32_t> vtable_index_;
Aligned<uint32_t> num_args_;
Aligned<uint32_t> hotness_counter_;
} stor_32_;
static_assert(sizeof(stor_32_) == StoragePacked32::GetSize());
struct StoragePackedPtr : public AlignedStorage<sizeof(uintptr_t), sizeof(uintptr_t), STORAGE_PTR_NUM> {
Aligned<Class> *class_;
Aligned<std::atomic<const void *>> compiled_entry_point_ {nullptr};
Aligned<void *> native_pointer_ {nullptr};
} stor_ptr_;
static_assert(sizeof(stor_ptr_) == StoragePackedPtr::GetSize());
const panda_file::File *panda_file_;
panda_file::File::EntityId file_id_;
panda_file::File::EntityId code_id_;
const uint16_t *shorty_;
std::atomic<ProfilingData *> profiling_data_ {nullptr};
friend class Offsets_Method_Test;
};
} // namespace panda
#endif // PANDA_RUNTIME_INCLUDE_METHOD_H_