/*
* Copyright (c) 2021 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 ECMASCRIPT_MEM_MACHINE_CODE_H
#define ECMASCRIPT_MEM_MACHINE_CODE_H
#include "ecmascript/ecma_macros.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/mem/barriers.h"
#include "ecmascript/mem/mem.h"
#include "ecmascript/mem/tagged_object.h"
#include "ecmascript/mem/visitor.h"
#include "ecmascript/stackmap/ark_stackmap.h"
#include "ecmascript/method.h"
#include "ecmascript/mem/jit_fort_memdesc.h"
#include "ecmascript/deoptimizer/calleeReg.h"
#include "ecmascript/compiler/aot_file/func_entry_des.h"
#include "libpandabase/macros.h"
namespace panda::ecmascript {
enum class MachineCodeType : uint8_t {
BASELINE_CODE,
FAST_JIT_CODE,
ARKSTEED_CODE,
};
struct MachineCodeDesc {
uintptr_t rodataAddrBeforeText {0};
size_t rodataSizeBeforeText {0};
uintptr_t rodataAddrAfterText {0};
size_t rodataSizeAfterText {0};
uintptr_t codeAddr {0};
size_t codeSize {0};
uintptr_t funcEntryDesAddr {0};
size_t funcEntryDesSize {0};
uintptr_t stackMapOrOffsetTableAddr {0};
size_t stackMapOrOffsetTableSize {0};
uintptr_t heapConstantTableAddr {0};
size_t heapConstantTableSize {0};
uintptr_t codeCommentsAddr { 0 };
size_t codeCommentsSize { 0 };
MachineCodeType codeType {MachineCodeType::FAST_JIT_CODE};
#ifdef JIT_ENABLE_CODE_SIGN
uintptr_t codeSigner {0};
#endif
uintptr_t instructionsAddr {0};
size_t instructionsSize {0};
// the unified field that indicates where this machinecode is located
bool isHugeObj {false};
uintptr_t hugeObjRegion {0};
size_t codeSizeAlign {0};
size_t rodataSizeBeforeTextAlign {0};
size_t rodataSizeAfterTextAlign {0};
size_t funcEntryDesSizeAlign {0};
size_t stackMapSizeAlign {0};
size_t heapConstantTableSizeAlign {0};
MemDesc *memDesc {nullptr};
bool isAsyncCompileMode {false};
};
class MachineCode;
using JitCodeVector = std::vector<std::tuple<MachineCode*, std::string, uintptr_t>>;
using JitCodeMapVisitor = std::function<void(std::map<JSTaggedType, JitCodeVector*>&)>;
// BaselineCode object layout:
// +-----------------------------------+
// | MarkWord | 8 bytes
// INS_SIZE_OFFSET +-----------------------------------+
// | machine payload size | 4 bytes
// +-----------------------------------+
// | instructions size | 4 bytes
// +-----------------------------------+
// | instructions addr | 8 bytes (if JitFort enabled)
// +-----------------------------------+
// | nativePcOffsetTable size | 4 bytes
// +-----------------------------------+
// | func addr | 8 bytes
// +-----------------------------------+
// | fp deltaprevframe sp | 8 bytes
// +-----------------------------------+
// | func size | 4 bytes
// +-----------------------------------+
// | callee register num | 4 bytes
// +-----------------------------------+
// | | 64 * 4 bytes (AMD64 or ARM64)
// | callee reg2offset array | or 32 * 4 bytes (others)
// | |
// +-----------------------------------+
// | bit field | 4 bytes
// +-----------------------------------+
// | ... |
// INSTR_OFFSET | | if JitFort enabled, will be in JitFort space
// (16 byte align) | machine instructions(text) | instead for non-huge sized machine code objects
// | ... | and pointed to by "instructions addr"
// +-----------------------------------+
// | |
// | nativePcOffsetTable |
// | ... |
// +-----------------------------------+
//==================================================================
// JitCode object layout:
// +-----------------------------------+
// | MarkWord | 8 bytes
// INS_SIZE_OFFSET +-----------------------------------+
// | OSR offset | 4 bytes
// +-----------------------------------+
// | OSR mask | 4 bytes
// +-----------------------------------+
// | machine payload size | 4 bytes
// +-----------------------------------+
// | instructions size | 4 bytes
// +-----------------------------------+
// | instructions addr | 8 bytes (if JitFort enabled)
// +-----------------------------------+
// | stack map size | 4 bytes
// +-----------------------------------+
// | heap constant table size | 4 bytes
// +-----------------------------------+
// | heap constant table addr | 8 bytes
// +-----------------------------------+
// | func addr | 8 bytes
// +-----------------------------------+
// | fp deltaprevframe sp | 8 bytes
// +-----------------------------------+
// | func size | 4 bytes
// +-----------------------------------+
// | callee register num | 4 bytes
// +-----------------------------------+
// | | 64 * 4 bytes (AMD64 or ARM64)
// | callee reg2offset array | or 32 * 4 bytes (others)
// | |
// +-----------------------------------+
// | bit field | 4 bytes
// +-----------------------------------+
// | |
// INSTR_OFFSET | |
// (16 byte align)| machine instructions(text) | if JitFort enabled, will be in JitFort space
// | ... | instead for non-huge sized machine code objects
// STACKMAP_OFFSET +-----------------------------------+ and pointed to by "instuctions addr"
// | ArkStackMap |
// | ... |
// +-----------------------------------+
//
//==================================================================
// HugeMachineCode object layout for JitFort (see space.cpp AllocateFort())
//
// ^ +-----------------------------------+ +
// | | MarkWord | |
// | +-----------------------------------+ |
// | | OSR offset | |
// | +-----------------------------------+ |
// | | ... | |
// | | | |
// mutable +-----------------------------------+ |
// Page aligned | instructions size | |
// | +-----------------------------------+ |
// | | instructions addr |---+ |
// | +-----------------------------------+ | | 256 kByte (Region)
// | | ... | | | multiples
// | | | | |
// | +-----------------------------------+ | | if JitFort is disabled
// | | ArkStackMap | | | Jit generated native code
// | | | | | location is placed between
// v Page Aligned +-----------------------------------+ | | FuncEntryDesc and ArkStackMap
// ^ if JitFort | |<--+ | instead and is mutable
// | enabled | JitFort space | |
// | | (Jit generated native code) | |
// immutable | | |
// JitFort space | | |
// | | | |
// | | | |
// | | | |
// v +-----------------------------------+ v
//
//
class MachineCode : public TaggedObject {
public:
NO_COPY_SEMANTIC(MachineCode);
NO_MOVE_SEMANTIC(MachineCode);
static MachineCode *Cast(TaggedObject *object)
{
ASSERT(JSTaggedValue(object).IsMachineCodeObject());
return static_cast<MachineCode *>(object);
}
static constexpr size_t INS_SIZE_OFFSET = TaggedObjectSize();
static constexpr size_t INT32_SIZE = sizeof(int32_t);
static constexpr int CalleeReg2OffsetArraySize = 2 * kungfu::MAX_CALLEE_SAVE_REIGISTER_NUM;
ACCESSORS_PRIMITIVE_FIELD(LocalHeapAddress, uint64_t, INS_SIZE_OFFSET, LOCAL_HEAP_ADDRESS_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(OSROffset, int32_t, LOCAL_HEAP_ADDRESS_OFFSET, OSRMASK_OFFSET);
// The high 16bit is used as the flag bit, and the low 16bit is used as the count of OSR execution times.
ACCESSORS_PRIMITIVE_FIELD(OsrMask, uint32_t, OSRMASK_OFFSET, PAYLOADSIZE_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(PayLoadSizeInBytes, uint32_t, PAYLOADSIZE_OFFSET, INSTRSIZ_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(InstructionsSize, uint32_t, INSTRSIZ_OFFSET, INSTRADDR_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(InstructionsAddr, uint64_t, INSTRADDR_OFFSET, STACKMAP_OR_OFFSETTABLE_SIZE_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(StackMapOrOffsetTableSize, uint32_t,
STACKMAP_OR_OFFSETTABLE_SIZE_OFFSET, HEAP_CONSTANT_TABLE_SIZE_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(HeapConstantTableSize, uint32_t,
HEAP_CONSTANT_TABLE_SIZE_OFFSET, HEAP_CONSTANT_TABLE_ADDR_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(HeapConstantTableAddr, uint64_t,
HEAP_CONSTANT_TABLE_ADDR_OFFSET, FUNCADDR_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(FuncAddr, uint64_t, FUNCADDR_OFFSET, FPDELTA_PRVE_FRAME_SP_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(FpDeltaPrevFrameSp, uintptr_t, FPDELTA_PRVE_FRAME_SP_OFFSET, FUNC_SIZE_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(FuncSize, uint32_t, FUNC_SIZE_OFFSET, CALLEE_REGISTERNUM_OFFSET);
ACCESSORS_PRIMITIVE_FIELD(CalleeRegisterNum, uint32_t, CALLEE_REGISTERNUM_OFFSET, CALLEE_R2O_OFFSET);
static constexpr size_t BIT_FIELD_OFFSET = CALLEE_R2O_OFFSET + INT32_SIZE * CalleeReg2OffsetArraySize;
ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET);
DEFINE_ALIGN_SIZE(LAST_OFFSET);
static constexpr size_t PAYLOAD_OFFSET = SIZE;
static constexpr uint32_t DATA_ALIGN = 8;
static constexpr uint32_t TEXT_ALIGN = 16;
static constexpr int32_t INVALID_OSR_OFFSET = -1;
static constexpr uint32_t OSR_EXECUTE_CNT_OFFSET = OSRMASK_OFFSET + 2;
static constexpr uint16_t OSR_DEOPT_FLAG = 0x80;
void SetCalleeReg2OffsetArray(int32_t* calleeRegArray)
{
DASSERT_PRINT(calleeRegArray != nullptr, "Array pointer cannot be null.");
for (size_t i = 0; i < CalleeReg2OffsetArraySize; i++) {
Barriers::SetPrimitive<int32_t>(this, CALLEE_R2O_OFFSET + i * INT32_SIZE, calleeRegArray[i]);
}
}
int32_t GetCalleeReg2OffsetArray(int32_t calleeRegIndex) const
{
DASSERT_PRINT(calleeRegIndex < CalleeReg2OffsetArraySize, "Array index out of bounds.");
return Barriers::GetPrimitive<int32_t>(this, CALLEE_R2O_OFFSET + calleeRegIndex * INT32_SIZE);
}
// define BitField
static constexpr size_t IS_FAST_CALL_BITS = 1;
FIRST_BIT_FIELD(BitField, IsFastCall, bool, IS_FAST_CALL_BITS);
DECL_DUMP()
uintptr_t GetNonTextAddress() const
{
return reinterpret_cast<const uintptr_t>(this) + SIZE;
}
uintptr_t GetText() const;
uint8_t *GetStackMapOrOffsetTableAddress() const;
uint8_t *GetHeapConstantTableAddress() const;
size_t GetTextSize() const
{
return GetInstructionsSize();
}
bool SetData(JSThread *thread, const MachineCodeDesc &desc, JSHandle<Method> &method, size_t dataSize);
bool SetText(const MachineCodeDesc &desc);
bool SetNonText(const MachineCodeDesc &desc, EntityId methodId);
bool SetArkSteedData(JSThread *thread, const MachineCodeDesc &desc,
JSHandle<Method> &method, size_t dataSize);
template <VisitType visitType, class DerivedVisitor>
void VisitRangeSlot(BaseObjectVisitor<DerivedVisitor> &visitor)
{
ASSERT(visitType == VisitType::ALL_VISIT || visitType == VisitType::OLD_GC_VISIT);
if constexpr (visitType == VisitType::ALL_VISIT) {
visitor(this, ToUintPtr(this),
ToUintPtr(this) + GetMachineCodeObjectSize(), VisitObjectArea::RAW_DATA);
}
if constexpr (visitType == VisitType::OLD_GC_VISIT) {
uintptr_t start = static_cast<uintptr_t>(GetHeapConstantTableAddr());
uintptr_t end = static_cast<uintptr_t>(GetHeapConstantTableAddr()) + GetHeapConstantTableSize();
visitor(this,
start,
end,
VisitObjectArea::NORMAL);
this->ProcessMarkObject();
}
}
void ProcessMarkObject();
size_t GetMachineCodeObjectSize()
{
return SIZE + this->GetPayLoadSizeInBytes();
}
uint32_t GetInstructionSizeInBytes() const
{
return GetPayLoadSizeInBytes();
}
bool IsInText(const uintptr_t pc) const;
std::tuple<uint64_t, uint8_t *, int, kungfu::CalleeRegAndOffsetVec> CalCallSiteInfo() const;
void SetOsrDeoptFlag(bool isDeopt)
{
uint16_t flag = Barriers::GetPrimitive<uint16_t>(this, OSRMASK_OFFSET);
if (isDeopt) {
flag |= OSR_DEOPT_FLAG;
} else {
flag &= (~OSR_DEOPT_FLAG);
}
Barriers::SetPrimitive(this, OSRMASK_OFFSET, flag);
}
void SetOsrExecuteCnt(uint16_t count)
{
Barriers::SetPrimitive(this, OSR_EXECUTE_CNT_OFFSET, count);
}
private:
bool SetBaselineCodeData(JSThread *thread, const MachineCodeDesc &desc, JSHandle<Method> &method, size_t dataSize);
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_MEM_MACHINE_CODE_H