Arquivos
hhvm/hphp/runtime/vm/jit/codegen.h
T
Paul Tarjan 1c50cae763 s/Call/CPPCall/
A precursor to moving the Transl namespace to JIT. We have an IR opcode `Call` and this would conflict. Those should probably be in another namespace, but this is easier.

This was way easier to do in a compiled language :)
2013-06-10 10:14:11 -07:00

585 linhas
19 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_VM_CG_H_
#define incl_HPHP_VM_CG_H_
#include <vector>
#include "hphp/runtime/vm/jit/ir.h"
#include "hphp/runtime/vm/jit/irfactory.h"
#include "hphp/runtime/vm/jit/linearscan.h"
#include "hphp/runtime/vm/jit/targetcache.h"
#include "hphp/runtime/vm/jit/translator-x64.h"
#include "hphp/runtime/vm/jit/state_vector.h"
namespace HPHP {
namespace JIT {
class FailedCodeGen : public std::exception {
public:
const char* file;
const int line;
const char* func;
const uint32_t bcOff;
FailedCodeGen(const char* _file, int _line, const char* _func,
uint32_t _bcOff) :
file(_file), line(_line), func(_func), bcOff(_bcOff) { }
};
struct ArgGroup;
// DestType describes the return type of native helper calls, particularly
// register assignments
enum class DestType : unsigned {
None, // return void (no valid registers)
SSA, // return a single-register value
TV // return a TypedValue packed in two registers
};
enum SyncOptions {
kNoSyncPoint,
kSyncPoint,
kSyncPointAdjustOne,
};
// Information about where code was generated, for pretty-printing.
struct AsmInfo {
explicit AsmInfo(const IRFactory* factory)
: instRanges(factory, TcaRange(nullptr, nullptr))
, asmRanges(factory, TcaRange(nullptr, nullptr))
, astubRanges(factory, TcaRange(nullptr, nullptr))
{}
// Asm address info for each instruction and block
StateVector<IRInstruction,TcaRange> instRanges;
StateVector<Block,TcaRange> asmRanges;
StateVector<Block,TcaRange> astubRanges;
};
typedef StateVector<IRInstruction, RegSet> LiveRegs;
// Stuff we need to preserve between blocks while generating code,
// and address information produced during codegen.
struct CodegenState {
CodegenState(const IRFactory* factory, const RegAllocInfo& regs,
const LiveRegs& liveRegs, const LifetimeInfo* lifetime,
AsmInfo* asmInfo)
: patches(factory, nullptr)
, addresses(factory, nullptr)
, lastMarker(nullptr)
, regs(regs)
, liveRegs(liveRegs)
, lifetime(lifetime)
, asmInfo(asmInfo)
, catches(factory, CatchInfo())
, catchTrace(nullptr)
{}
// Each block has a list of addresses to patch, and an address if
// it's already been emitted.
StateVector<Block,void*> patches;
StateVector<Block,TCA> addresses;
// Keep track of the most recent Marker instruction we've seen in the
// current trace (even across blocks).
const MarkerData* lastMarker;
// True if this block's terminal Jmp_ has a desination equal to the
// next block in the same assmbler.
bool noTerminalJmp_;
// output from register allocator
const RegAllocInfo& regs;
// for each instruction, holds the RegSet of registers that must be
// preserved across that instruction. This is for push/pop of caller-saved
// registers.
const LiveRegs& liveRegs;
// Optional information used when pretty-printing code after codegen.
// when not available, these are nullptrs.
const LifetimeInfo* lifetime;
// Output: start/end ranges of machine code addresses of each instruction.
AsmInfo* asmInfo;
// Used to pass information about the state of the world at native
// calls between cgCallHelper and cgBeginCatch.
StateVector<Block, CatchInfo> catches;
// If non-null, represents the catch trace for the current
// instruction, to be registered with the unwinder.
Trace* catchTrace;
};
constexpr Reg64 rCgGP (reg::r11);
constexpr RegXMM rCgXMM0(reg::xmm0);
constexpr RegXMM rCgXMM1(reg::xmm1);
struct CodeGenerator {
typedef Transl::X64Assembler Asm;
CodeGenerator(Trace* trace, Asm& as, Asm& astubs, Transl::TranslatorX64* tx64,
CodegenState& state)
: m_as(as)
, m_astubs(astubs)
, m_tx64(tx64)
, m_state(state)
, m_regs(state.regs)
, m_rScratch(InvalidReg)
, m_curInst(nullptr)
, m_curTrace(trace)
, m_curBcOff(-1) {
}
void cgBlock(Block* block, vector<TransBCMapping>* bcMap);
private:
Address cgInst(IRInstruction* inst);
// Autogenerate function declarations for each IR instruction in ir.h
#define O(name, dsts, srcs, flags) void cg##name(IRInstruction* inst);
IR_OPCODES
#undef O
// helper functions for code generation
void cgCallNative(IRInstruction* inst) {
cgCallNative(m_as, inst);
}
void cgCallNative(Asm& a, IRInstruction* inst);
void doStackArgs(Asm&, ArgGroup&);
void cgCallHelper(Asm&,
TCA addr,
SSATmp* dst,
SyncOptions sync,
ArgGroup& args,
DestType destType = DestType::SSA);
void cgCallHelper(Asm& a,
TCA addr,
PhysReg dstReg,
SyncOptions sync,
ArgGroup& args,
DestType destType = DestType::SSA);
void cgCallHelper(Asm& a,
const Transl::CppCall& call,
PhysReg dstReg,
SyncOptions sync,
ArgGroup& args,
DestType destType = DestType::SSA);
void cgCallHelper(Asm& a,
const Transl::CppCall& call,
PhysReg dstReg0,
PhysReg dstReg1,
SyncOptions sync,
ArgGroup& args,
DestType destType = DestType::SSA);
void cgCallHelper(Asm& a,
const Transl::CppCall& call,
PhysReg dstReg0,
PhysReg dstReg1,
SyncOptions sync,
ArgGroup& args,
RegSet toSave,
DestType destType = DestType::SSA);
void cgStore(PhysReg base,
int64_t off,
SSATmp* src,
bool genStoreType = true);
void cgStoreTypedValue(PhysReg base, int64_t off, SSATmp* src);
void cgLoad(PhysReg base, int64_t off, IRInstruction* inst);
template<class Loc1, class Loc2, class JmpFn>
void emitTypeTest(Type type,
Loc1 typeSrc,
Loc2 dataSrc,
JmpFn doJcc);
template<class Loc>
void emitTypeCheck(Type type, Loc typeSrc, Loc dataSrc, Block* taken);
template<class Loc>
void emitTypeGuard(Type type, Loc typeLoc, Loc dataLoc);
void cgStMemWork(IRInstruction* inst, bool genStoreType);
void cgStRefWork(IRInstruction* inst, bool genStoreType);
void cgStPropWork(IRInstruction* inst, bool genStoreType);
void cgIncRefWork(Type type, SSATmp* src);
void cgDecRefWork(IRInstruction* inst, bool genZeroCheck);
template<class OpInstr, class Oper>
void cgUnaryIntOp(SSATmp* dst, SSATmp* src, OpInstr, Oper);
enum Commutativity { Commutative, NonCommutative };
template<class Oper, class RegType>
void cgBinaryOp(IRInstruction*,
void (Asm::*intImm)(Immed, RegType),
void (Asm::*intRR)(RegType, RegType),
void (Asm::*mov)(RegType, RegType),
void (Asm::*fpRR)(RegXMM, RegXMM),
Oper,
RegType (*conv)(PhysReg),
Commutativity);
template<class Oper, class RegType>
void cgBinaryIntOp(IRInstruction*,
void (Asm::*intImm)(Immed, RegType),
void (Asm::*intRR)(RegType, RegType),
void (Asm::*mov)(RegType, RegType),
Oper,
RegType (*conv)(PhysReg),
Commutativity);
void cgNegateWork(SSATmp* dst, SSATmp* src);
void cgNotWork(SSATmp* dst, SSATmp* src);
void emitGetCtxFwdCallWithThis(PhysReg ctxReg,
bool staticCallee);
void emitGetCtxFwdCallWithThisDyn(PhysReg destCtxReg,
PhysReg thisReg,
Transl::TargetCache::CacheHandle& ch);
void cgLoadTypedValue(PhysReg base, int64_t off, IRInstruction* inst);
void cgJcc(IRInstruction* inst); // helper
void cgReqBindJcc(IRInstruction* inst); // helper
void cgOpCmpHelper(
IRInstruction* inst,
void (Asm::*setter)(Reg8),
int64_t (*str_cmp_str)(StringData*, StringData*),
int64_t (*str_cmp_int)(StringData*, int64_t),
int64_t (*str_cmp_obj)(StringData*, ObjectData*),
int64_t (*obj_cmp_obj)(ObjectData*, ObjectData*),
int64_t (*obj_cmp_int)(ObjectData*, int64_t),
int64_t (*arr_cmp_arr)(ArrayData*, ArrayData*));
template<class Loc>
void emitSideExitGuard(Type type, Loc typeLoc,
Loc dataLoc, Offset taken);
void emitReqBindJcc(ConditionCode cc, const ReqBindJccData*);
void emitCompare(SSATmp*, SSATmp*);
void emitTestZero(SSATmp*);
bool emitIncDecHelper(SSATmp* dst, SSATmp* src1, SSATmp* src2,
void(Asm::*emitFunc)(Reg64));
bool emitInc(SSATmp* dst, SSATmp* src1, SSATmp* src2);
bool emitDec(SSATmp* dst, SSATmp* src1, SSATmp* src2);
private:
PhysReg selectScratchReg(IRInstruction* inst);
void emitLoadImm(CodeGenerator::Asm& as, int64_t val, PhysReg dstReg);
PhysReg prepXMMReg(const SSATmp* tmp,
X64Assembler& as,
const RegAllocInfo& allocInfo,
RegXMM rXMMScratch);
void emitSetCc(IRInstruction*, ConditionCode);
template<class JmpFn>
void emitIsTypeTest(IRInstruction* inst, JmpFn doJcc);
void doubleCmp(X64Assembler& a, RegXMM xmmReg0, RegXMM xmmReg1);
void cgIsTypeCommon(IRInstruction* inst, bool negate);
void cgJmpIsTypeCommon(IRInstruction* inst, bool negate);
void cgIsTypeMemCommon(IRInstruction*, bool negate);
void emitInstanceBitmaskCheck(IRInstruction*);
void emitTraceRet(CodeGenerator::Asm& as);
Address cgCheckStaticBit(Type type,
PhysReg reg,
bool regIsCount);
Address cgCheckStaticBitAndDecRef(Type type,
PhysReg dataReg,
Block* exit);
Address cgCheckRefCountedType(PhysReg typeReg);
Address cgCheckRefCountedType(PhysReg baseReg,
int64_t offset);
void cgDecRefStaticType(Type type,
PhysReg dataReg,
Block* exit,
bool genZeroCheck);
void cgDecRefDynamicType(PhysReg typeReg,
PhysReg dataReg,
Block* exit,
bool genZeroCheck);
void cgDecRefDynamicTypeMem(PhysReg baseReg,
int64_t offset,
Block* exit);
void cgDecRefMem(Type type,
PhysReg baseReg,
int64_t offset,
Block* exit);
void cgIterNextCommon(IRInstruction* inst);
void cgIterInitCommon(IRInstruction* inst);
void cgLdFuncCachedCommon(IRInstruction* inst);
TargetCache::CacheHandle cgLdClsCachedCommon(IRInstruction* inst);
void emitFwdJcc(ConditionCode cc, Block* target);
void emitFwdJcc(Asm& a, ConditionCode cc, Block* target);
void emitContVarEnvHelperCall(SSATmp* fp, TCA helper);
const Func* curFunc() const;
Class* curClass() const { return curFunc()->cls(); }
void recordSyncPoint(Asm& as, SyncOptions sync = kSyncPoint);
Address getDtorGeneric();
Address getDtorTyped();
int iterOffset(SSATmp* tmp);
int iterOffset(uint32_t id);
void emitReqBindAddr(const Func* func, TCA& dest, Offset offset);
void emitAdjustSp(PhysReg spReg, PhysReg dstReg, int64_t adjustment);
void emitConvBoolOrIntToDbl(IRInstruction* inst);
/*
* Generate an if-block that branches around some unlikely code, handling
* the cases when a == astubs and a != astubs. cc is the branch condition
* to run the unlikely block.
*
* Passes the proper assembler to use to the unlikely function.
*/
template <class Block>
void unlikelyIfBlock(ConditionCode cc, Block unlikely) {
if (&m_as == &m_astubs) {
Label done;
m_as.jcc(ccNegate(cc), done);
unlikely(m_as);
asm_label(m_as, done);
} else {
Label unlikelyLabel, done;
m_as.jcc(cc, unlikelyLabel);
asm_label(m_astubs, unlikelyLabel);
unlikely(m_astubs);
m_astubs.jmp(done);
asm_label(m_as, done);
}
}
// Generate an if-then-else block into m_as.
template <class Then, class Else>
void ifThenElse(ConditionCode cc, Then thenBlock, Else elseBlock) {
Label elseLabel, done;
m_as.jcc8(ccNegate(cc), elseLabel);
thenBlock();
m_as.jmp8(done);
asm_label(m_as, elseLabel);
elseBlock();
asm_label(m_as, done);
}
// This is for printing partially-generated traces when debugging
void print() const;
private:
Asm& m_as; // current "main" assembler
Asm& m_astubs; // assembler for stubs and other cold code.
TranslatorX64* m_tx64;
CodegenState& m_state;
const RegAllocInfo& m_regs;
Reg64 m_rScratch; // currently selected GP scratch reg
IRInstruction* m_curInst; // current instruction being generated
Trace* m_curTrace;
uint32_t m_curBcOff; // offset of bytecode instr that produced
// m_curInst
};
class ArgDesc {
public:
enum Kind {
Reg, // Normal register
TypeReg, // TypedValue's m_type field. Might need arch-specific
// mangling before call depending on TypedValue's layout.
Imm, // Immediate
Addr, // Address
None, // Nothing: register will contain garbage
};
PhysReg dstReg() const { return m_dstReg; }
PhysReg srcReg() const { return m_srcReg; }
Kind kind() const { return m_kind; }
void setDstReg(PhysReg reg) { m_dstReg = reg; }
Immed imm() const { return m_imm; }
bool isZeroExtend() const {return m_zeroExtend;}
bool done() const { return m_done; }
void markDone() { m_done = true; }
private: // These should be created using ArgGroup.
friend struct ArgGroup;
explicit ArgDesc(Kind kind, PhysReg srcReg, Immed immVal)
: m_kind(kind)
, m_srcReg(srcReg)
, m_dstReg(reg::noreg)
, m_imm(immVal)
, m_zeroExtend(false)
, m_done(false)
{}
explicit ArgDesc(SSATmp* tmp, const RegisterInfo& info, bool val = true);
private:
Kind m_kind;
PhysReg m_srcReg;
PhysReg m_dstReg;
Immed m_imm;
bool m_zeroExtend;
bool m_done;
};
/*
* Bag of ArgDesc for use with cgCallHelper.
*
* You can create this using function chaining. Example:
*
* ArgGroup args;
* args.imm(0)
* .reg(rax)
* .immPtr(StringData::GetStaticString("Yo"))
* ;
* assert(args.size() == 3);
*/
struct ArgGroup {
typedef std::vector<ArgDesc> ArgVec;
explicit ArgGroup(const RegAllocInfo& regs)
: m_regs(regs), m_override(nullptr)
{}
size_t numRegArgs() const { return m_regArgs.size(); }
size_t numStackArgs() const { return m_stkArgs.size(); }
ArgDesc& reg(size_t i) {
assert(i < m_regArgs.size());
return m_regArgs[i];
}
ArgDesc& operator[](size_t i) {
return reg(i);
}
ArgDesc& stk(size_t i) {
assert(i < m_stkArgs.size());
return m_stkArgs[i];
}
ArgGroup& imm(uintptr_t imm) {
push_arg(ArgDesc(ArgDesc::Imm, InvalidReg, imm));
return *this;
}
template<class T> ArgGroup& immPtr(const T* ptr) {
return imm(uintptr_t(ptr));
}
ArgGroup& immPtr(std::nullptr_t) { return imm(0); }
ArgGroup& reg(PhysReg reg) {
push_arg(ArgDesc(ArgDesc::Reg, PhysReg(reg), -1));
return *this;
}
ArgGroup& addr(PhysReg base, intptr_t off) {
push_arg(ArgDesc(ArgDesc::Addr, base, off));
return *this;
}
ArgGroup& ssa(SSATmp* tmp) {
push_arg(ArgDesc(tmp, m_regs[tmp]));
return *this;
}
ArgGroup& ssas(IRInstruction* inst, unsigned begin, unsigned count = 1) {
for (SSATmp* s : inst->srcs().subpiece(begin, count)) {
push_arg(ArgDesc(s, m_regs[s]));
}
return *this;
}
/*
* Pass tmp as a TypedValue passed by value.
*/
ArgGroup& typedValue(SSATmp* tmp) {
// If there's exactly one register argument slot left, the whole TypedValue
// goes on the stack instead of being split between a register and the
// stack.
if (m_regArgs.size() == kNumRegisterArgs - 1) {
m_override = &m_stkArgs;
}
packed_tv ? type(tmp).ssa(tmp) : ssa(tmp).type(tmp);
m_override = nullptr;
return *this;
}
ArgGroup& vectorKeyIS(SSATmp* key) {
return vectorKeyImpl(key, true);
}
ArgGroup& vectorKeyS(SSATmp* key) {
return vectorKeyImpl(key, false);
}
private:
void push_arg(const ArgDesc& arg) {
// If m_override is set, use it unconditionally. Otherwise, select
// m_regArgs or m_stkArgs depending on how many args we've already pushed.
ArgVec* args = m_override;
if (!args) {
args = m_regArgs.size() < kNumRegisterArgs ? &m_regArgs : &m_stkArgs;
}
args->push_back(arg);
}
/*
* For passing the m_type field of a TypedValue.
*/
ArgGroup& type(SSATmp* tmp) {
push_arg(ArgDesc(tmp, m_regs[tmp], false));
return *this;
}
ArgGroup& none() {
push_arg(ArgDesc(ArgDesc::None, InvalidReg, -1));
return *this;
}
ArgGroup& vectorKeyImpl(SSATmp* key, bool allowInt) {
if (key->isString() || (allowInt && key->isA(Type::Int))) {
return packed_tv ? none().ssa(key) : ssa(key).none();
}
return typedValue(key);
}
const RegAllocInfo& m_regs;
ArgVec* m_override; // used to force args to go into a specific ArgVec
ArgVec m_regArgs;
ArgVec m_stkArgs;
};
const Func* loadClassCtor(Class* cls);
Instance* createClHelper(Class*, int, ActRec*, TypedValue*);
void genCodeForTrace(Trace* trace,
CodeGenerator::Asm& a,
CodeGenerator::Asm& astubs,
IRFactory* irFactory,
vector<TransBCMapping>* bcMap,
TranslatorX64* tx64,
const RegAllocInfo& regs,
const LifetimeInfo* lifetime = nullptr,
AsmInfo* asmInfo = nullptr);
TypedValue arrayIdxI(ArrayData*, int64_t, TypedValue);
TypedValue arrayIdxS(ArrayData*, StringData*, TypedValue);
TypedValue arrayIdxSi(ArrayData*, StringData*, TypedValue);
}}
#endif