blob: 595afdd2c49a90b8bc94b76445c58f6e8428a6ad [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
class Encoder;
enum RelocType {
RelocTypeBranch, // cond, uncond branch
RelocTypeCallPcrel, // calls
RelocTypeLabelUse, // direct use of a label
RelocTypeLabel, // points to label instr
RelocTypeAlignedLabel, // points to loop-top label instr that needs alignment
RelocTypeInlineeEntryOffset, // points to offset immediate in buffer
};
///---------------------------------------------------------------------------
///
/// class EncoderReloc
///
///---------------------------------------------------------------------------
class EncodeRelocAndLabels
{
public:
RelocType m_type;
void * m_ptr; // points to encoded buffer byte or LabelInstr if RelocTypeLabel
void * m_origPtr; // copy of m_ptr to be used to get offset in the source buffer during BR shortening
private:
union
{
IR::LabelInstr* m_labelInstr; // ptr to Br Label
BYTE m_nopCount;
uint64 m_InlineeOffset;
};
bool m_isShortBr;
public:
void init(RelocType type, void* ptr, IR::LabelInstr* labelInstr = nullptr)
{
m_type = type;
m_ptr = ptr;
m_InlineeOffset = 0;
m_isShortBr = false;
if (type == RelocTypeLabel)
{
// preserve original PC for labels
m_origPtr = (void*)((IR::LabelInstr*)ptr)->GetPC();
m_nopCount = 0;
}
else
{
m_origPtr = ptr;
if (type == RelocTypeBranch)
{
Assert(labelInstr);
m_labelInstr = labelInstr;
m_isShortBr = false;
}
else if (type == RelocTypeLabelUse)
{
Assert(labelInstr);
m_labelInstr = labelInstr;
}
}
}
void revert()
{
// recover old label PC
if (isLabel())
{
// recover old label PC and reset alignment nops
// we keep aligned labels type so we align them on the second attempt.
setLabelCurrPC(getLabelOrigPC());
m_nopCount = 0;
return;
}
if (m_type == RelocTypeBranch)
{
m_isShortBr = false;
}
m_ptr = m_origPtr;
}
bool isLabel() const { return isAlignedLabel() || m_type == RelocTypeLabel; }
bool isAlignedLabel() const { return m_type == RelocTypeAlignedLabel; }
bool isLongBr() const { return m_type == RelocTypeBranch && !m_isShortBr; }
bool isShortBr() const { return m_type == RelocTypeBranch && m_isShortBr; }
BYTE* getBrOpCodeByte() const { return (BYTE*)m_origPtr - 1;}
IR::LabelInstr * getBrTargetLabel() const
{
Assert((m_type == RelocTypeBranch || m_type == RelocTypeLabelUse) && m_labelInstr);
return m_labelInstr;
}
IR::LabelInstr * getLabel() const
{
Assert(isLabel());
return (IR::LabelInstr*) m_ptr;
}
// get label original PC without shortening/alignment
BYTE * getLabelOrigPC() const
{
Assert(isLabel());
return ((BYTE*) m_origPtr);
}
// get label PC after shortening/alignment
BYTE * getLabelCurrPC() const
{
Assert(isLabel());
return getLabel()->GetPC();
}
BYTE getLabelNopCount() const
{
Assert(isAlignedLabel());
return m_nopCount;
}
void setLabelCurrPC(BYTE* pc)
{
Assert(isLabel());
getLabel()->SetPC(pc);
}
void setLabelNopCount(BYTE nopCount)
{
Assert(isAlignedLabel());
Assert (nopCount >= 0 && nopCount < 16);
m_nopCount = nopCount;
}
void setAsShortBr()
{
Assert(m_type == RelocTypeBranch);
m_isShortBr = true;
}
// Validates if the branch is short and its target PC fits in one byte
bool validateShortBrTarget() const
{
return isShortBr() &&
getBrTargetLabel()->GetPC() - ((BYTE*)m_ptr + 1) >= -128 &&
getBrTargetLabel()->GetPC() - ((BYTE*)m_ptr + 1) <= 127;
}
uint64 GetInlineOffset()
{
return m_InlineeOffset;
}
void SetInlineOffset(uint64 offset)
{
m_InlineeOffset = offset;
}
};
///---------------------------------------------------------------------------
///
/// class EncoderMD
///
///---------------------------------------------------------------------------
enum Forms : BYTE;
typedef JsUtil::List<EncodeRelocAndLabels, ArenaAllocator> RelocList;
typedef JsUtil::List<InlineeFrameRecord*, ArenaAllocator> InlineeFrameRecords;
class EncoderMD
{
public:
EncoderMD(Func * func) : m_func(func) {}
ptrdiff_t Encode(IR::Instr * instr, BYTE *pc, BYTE* beginCodeAddress = nullptr);
void Init(Encoder *encoder);
void ApplyRelocs(size_t codeBufferAddress, size_t codeSize, uint* bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation = false);
uint GetRelocDataSize(EncodeRelocAndLabels *reloc);
BYTE * GetRelocBufferAddress(EncodeRelocAndLabels * reloc);
void EncodeInlineeCallInfo(IR::Instr *instr, uint32 offset);
static bool TryConstFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
static bool TryFold(IR::Instr *instr, IR::RegOpnd *regOpnd);
static bool SetsConditionCode(IR::Instr *instr);
static bool UsesConditionCode(IR::Instr *instr);
static bool IsOPEQ(IR::Instr *instr);
static bool IsSHIFT(IR::Instr *instr);
static bool IsMOVEncoding(IR::Instr *instr);
RelocList* GetRelocList() const { return m_relocList; }
int AppendRelocEntry(RelocType type, void *ptr, IR::LabelInstr *label= nullptr);
int FixRelocListEntry(uint32 index, int totalBytesSaved, BYTE *buffStart, BYTE* buffEnd);
void FixMaps(uint32 brOffset, uint32 bytesSaved, uint32 *inlineeFrameRecordsIndex, uint32 *inlineeFrameMapIndex, uint32 *pragmaInstToRecordOffsetIndex, uint32 *offsetBuffIndex);
void UpdateRelocListWithNewBuffer(RelocList * relocList, BYTE * newBuffer, BYTE * oldBufferStart, BYTE * oldBufferEnd);
#ifdef DBG
void VerifyRelocList(BYTE *buffStart, BYTE *buffEnd);
#endif
void AddLabelReloc(BYTE* relocAddress);
private:
const BYTE GetOpcodeByte2(IR::Instr *instr);
static Forms GetInstrForm(IR::Instr *instr);
const BYTE * GetFormTemplate(IR::Instr *instr);
const BYTE * GetOpbyte(IR::Instr *instr);
const BYTE GetRegEncode(IR::RegOpnd *regOpnd);
const BYTE GetRegEncode(RegNum reg);
static const uint32 GetOpdope(IR::Instr *instr);
const uint32 GetLeadIn(IR::Instr * instr);
BYTE EmitModRM(IR::Instr * instr, IR::Opnd *opnd, BYTE reg1);
void EmitConst(size_t val, int size, bool allowImm64 = false);
BYTE EmitImmed(IR::Opnd * opnd, int opSize, int sbit, bool allowImm64 = false);
static bool FitsInByte(size_t value);
bool IsExtendedRegister(RegNum reg);
BYTE GetMod(IR::IndirOpnd * opr, int* pDispSize);
BYTE GetMod(IR::SymOpnd * opr, int* pDispSize, RegNum& rmReg);
BYTE GetMod(size_t offset, bool regIsRbpOrR13, int * pDispSize);
BYTE GetRexByte(BYTE rexCode, IR::Opnd * opnd);
BYTE GetRexByte(BYTE rexCode, RegNum reg);
int GetOpndSize(IR::Opnd * opnd);
void EmitRexByte(BYTE * prexByte, BYTE rexByte, bool skipRexByte, bool reservedRexByte);
enum
{
REXOVERRIDE = 0x40,
REXW = 8,
REXR = 4,
REXX = 2,
REXB = 1,
};
static const BYTE Mod00 = 0x00 << 6;
static const BYTE Mod01 = 0x01 << 6;
static const BYTE Mod10 = 0x02 << 6;
static const BYTE Mod11 = 0x03 << 6;
private:
Func * m_func;
Encoder * m_encoder;
BYTE * m_pc;
RelocList * m_relocList;
int m_lastLoopLabelPosition;
};