Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
613 lines
29 KiB
C++
613 lines
29 KiB
C++
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifndef __avmplus_CodegenLIR__
|
|
#define __avmplus_CodegenLIR__
|
|
|
|
#include "LirHelper.h"
|
|
#include "InvokerCompiler.h"
|
|
|
|
#if defined(VMCFG_INTERRUPT_SAFEPOINT_POLL) && !defined(NJ_SAFEPOINT_POLLING_SUPPORTED)
|
|
#error "configuration error: safepoint polling not supported on this platform"
|
|
#endif
|
|
|
|
namespace avmplus
|
|
{
|
|
using namespace nanojit;
|
|
|
|
/**
|
|
* Each InEdge record tracks an unpatched branch. When the target code
|
|
* is generated in emitLabel(), we patch each tracked instruction or
|
|
* jtbl entry.
|
|
*/
|
|
struct InEdge {
|
|
LIns *branchIns; // the branch instruction that needs patching
|
|
uint32_t index; // if br is a jtbl, which index in table to patch
|
|
InEdge(LIns *br) : branchIns(br), index(0) {}
|
|
InEdge(LIns *jtbl, uint32_t index) : branchIns(jtbl), index(index) {}
|
|
};
|
|
|
|
/**
|
|
* CodegenLabel: information about a LIR label that hasn't been generated yet.
|
|
* Once code at the label is generated, we fill in bb. Later, we'll patch
|
|
* branch instructions to point to this bb. We have one of
|
|
* these for each verifier FrameState.
|
|
*/
|
|
class CodegenLabel {
|
|
public:
|
|
LIns *labelIns; // the LIR_label instruction for this code position
|
|
nanojit::BitSet *notnull; // merged nullability information for vars at this label
|
|
Seq<InEdge> *unpatchedEdges; // branches to this label that need patching
|
|
LIns** varTracker; // merged var tracker state at this label
|
|
LIns** tagTracker; // merged tag tracker state at this label
|
|
#ifdef NJ_VERBOSE
|
|
const char* name;
|
|
CodegenLabel() : labelIns(0), notnull(0), unpatchedEdges(0), varTracker(0), tagTracker(0), name(0)
|
|
{}
|
|
CodegenLabel(const char* name) : labelIns(0), notnull(0), unpatchedEdges(0), varTracker(0), tagTracker(0), name(name)
|
|
{}
|
|
#else
|
|
CodegenLabel() : labelIns(0), notnull(0), unpatchedEdges(0), varTracker(0), tagTracker(0)
|
|
{}
|
|
CodegenLabel(const char*) : labelIns(0), notnull(0), unpatchedEdges(0), varTracker(0), tagTracker(0)
|
|
{}
|
|
#endif
|
|
|
|
};
|
|
|
|
// Binding Cache Design
|
|
//
|
|
// When we don't know the type of the base object at a point we access a property,
|
|
// we must look up the property at runtime./ We cache the results of this lookup
|
|
// in a BindingCache instance, and install a handler specialized for the object
|
|
// type and binding kind. Each BindingCache consists of:
|
|
//
|
|
// call_handler initially, points to callprop_miss(), later points
|
|
// to the specialized handler
|
|
//
|
|
// vtable or tag VTable* or Atom tag for the base object. specialized
|
|
// handlers quickly check this and "miss" when they dont match
|
|
// the object seen at runtime.
|
|
//
|
|
// slot_offset or precomputed offset for a slot, for fast loads, or a preloaded
|
|
// method MethodEnv* for a method, for fast calls
|
|
//
|
|
// name Multiname* for this dynamic access. Used on a cache miss,
|
|
// and also for handlers that access dynamic properties.
|
|
//
|
|
// If jit'd code for a method needs cache entries, they are allocated and
|
|
// saved on MethodInfo::_abc.call_cache. The table is only allocated when jit
|
|
// compilation was successful, so jit'd code must load this pointer at runtime
|
|
// since we don't have the address at compile time.
|
|
//
|
|
// cache instances are in unmanaged memory, so VTable and MethodEnv pointers
|
|
// are effectively weak references: gc-visible pointers must exist elsewhere,
|
|
// and the cache doesn't cause them to be pinned.
|
|
//
|
|
// we allocate one entry for each unique Multiname in each method (only at
|
|
// late-bound reference sites, of course).
|
|
//
|
|
// Limitations:
|
|
//
|
|
// * we only use a binding cache if the name has no runtime parts.
|
|
// * only implemented for OP_callproperty OP_callproplex, OP_getproperty,
|
|
// and OP_setproperty
|
|
// * only common cases observed in testing are handled, others use a generic
|
|
// handler that's slightly slower than not using a cache at all.
|
|
// see callprop_miss() in jit-calls.h for detail on handled cases.
|
|
//
|
|
// Alternatives that led to current design:
|
|
//
|
|
// * specializing slot accessors on slot type, and storing slot_offset,
|
|
// avoids calling ScriptObject::getSlotAtom() (15% faster for gets & calls)
|
|
// * decl_type: when an object type doesn't match the cached type, sometimes the
|
|
// binding doesn't change. for example calling a method declared on
|
|
// a base class, on many different subclasses that don't override the
|
|
// method. the subsequent misses can be handled faster if we saved the method_id,
|
|
// and checked that the actual type & cached type are related by inheritance.
|
|
// This doesn't help for interface methods and the fast case didn't occur often
|
|
// enough for the extra cache size and code complexity.
|
|
// * vtable: When a single type (Traits*) is used in different environments we'll
|
|
// have distinct VTable*'s that point to the same Traits*. the cache might hit
|
|
// more often if we stored Traits*. however, specializing the cache on VTable*
|
|
// lest us pre-load the MethodEnv* for calls, saving one load. also, comparing
|
|
// vtable pointers instead of traits pointers saves one more load. (5% median speedup)
|
|
// * we pass the base object to CallCache::Handler instead of accessing it indirectly
|
|
// via args[], to save a load on the fast path. Worth 2-3%, and enables the same
|
|
// code to handle OP_callproperty and OP_callproplex.
|
|
// * we put Multiname* in the cache instead of passing it as a constant parameter,
|
|
// because the increase in cache size is smaller than the savings in code size,
|
|
// less parameters is faster, and the multiname is only used on relatively slow paths.
|
|
// * we allocate cache instances while generating code, which lets us embed
|
|
// the cache address in code instead of loading from methodinfo. some stats on ESC:
|
|
// call cache entries: 489
|
|
// bytes of call cache entries: 7,824
|
|
// instructions saved by early allocation (static): 1,445 (loads, movs, adds)
|
|
// MethodInfo instances: 2436
|
|
// bytes saved by eliminating 2 cache pointers on MethodInfo: 19,448
|
|
//
|
|
// (footnote: Times are from the tests in tamarin/test/performance as well as a
|
|
// selection of Flash/Flex benchmark apps)
|
|
//
|
|
// Alternatives not yet investigated:
|
|
//
|
|
// * we could put the whole Multiname in the cache, and maybe eliminate
|
|
// PoolObject::precomputedMultinames[]. Not all multinames are used in late bound
|
|
// call sites, so this could save some memory. precomputed multinames have been
|
|
// measured at over 10% of code size. Need to study cache size increase vs pool
|
|
// memory decrease. Later when we have a code cache, this could be more compelling.
|
|
// * we could specialize BKIND_METHOD handlers on method return type, enabling us
|
|
// to inline the native-value boxing logic from MethodEnv::endCoerce()
|
|
// * we could specialize getter & setter handlers on return/parameter type,
|
|
// allowing us to inline boxing & unboxing code from coerceEnter AND endCoerce
|
|
// * the MethodEnv* passed to each handler is only used on slow paths. Could we
|
|
// put it somewhere else? maybe core->currentMethodFrame->env?
|
|
// * on x86, FASTCALL might be faster, if it doesn't inhibit tail calls in handlers
|
|
// * other cache instance groupings:
|
|
// * one per call site instead of per-unique-multiname?
|
|
// * share them between methods?
|
|
// * handlers that access primitive dynamic properites have to call toplevel->toPrototype(val),
|
|
// maybe we could save the result and check its validity with (env->toplevel() == saved_proto->toplevel())
|
|
// * we could specialize on primitive type too, inlining just the toPrototype() path we need,
|
|
// instead of using a single set of handlers for all primitives types
|
|
// * OP_initproperty is rarely late bound in jit code; if this evidence changes
|
|
// then we should consider an inline cache for it.
|
|
|
|
// binding cache common code
|
|
class BindingCache {
|
|
public:
|
|
BindingCache(const Multiname*, BindingCache* next);
|
|
union {
|
|
VTable* vtable; // for kObjectType receivers
|
|
Atom tag; // for primitive receivers
|
|
};
|
|
union {
|
|
ptrdiff_t slot_offset; // for gets of a slot
|
|
MethodEnv* method; // calls to a method
|
|
};
|
|
const Multiname* const name; // multiname for this entry, saved when cache created.
|
|
BindingCache* const next; // singly-linked list
|
|
};
|
|
|
|
// cache for late bound calls
|
|
class CallCache: public BindingCache {
|
|
public:
|
|
typedef Atom (*Handler)(CallCache&, Atom base, int argc, Atom* args, MethodEnv*);
|
|
CallCache(const Multiname*, BindingCache* next);
|
|
Handler call_handler;
|
|
};
|
|
|
|
// cache for late bound gets
|
|
class GetCache: public BindingCache {
|
|
public:
|
|
typedef Atom (*Handler)(GetCache&, MethodEnv*, Atom);
|
|
GetCache(const Multiname*, BindingCache* next);
|
|
Handler get_handler;
|
|
};
|
|
|
|
// bind cache for sets. This is necessarily larger than for gets because
|
|
// an assignment implies a coercion, and the target type is an additional
|
|
// cached parameter to the coercion
|
|
class SetCache: public BindingCache {
|
|
public:
|
|
typedef void (*Handler)(SetCache&, Atom obj, Atom val, MethodEnv*);
|
|
SetCache(const Multiname*, BindingCache* next);
|
|
Handler set_handler;
|
|
union {
|
|
Traits* slot_type; // slot or setter type, for implicit coercion
|
|
GC* gc; // saved GC* for set-handlers that call WBATOM
|
|
};
|
|
};
|
|
|
|
/** helper class for allocating binding caches during jit compilation */
|
|
template <class C>
|
|
class CacheBuilder
|
|
{
|
|
SeqBuilder<C*> caches; // each entry points to an initialized cache
|
|
CodeMgr& codeMgr; // this allocator is used for new caches
|
|
C* findCacheSlot(const Multiname*);
|
|
public:
|
|
/** each new entry will be initialized with the given handler.
|
|
* temp_alloc is the allocator for this cache builder, with short lifetime.
|
|
* codeMgr.allocator is used to allocate memory for the method's caches, with method lifetime */
|
|
CacheBuilder(Allocator& builder_alloc, CodeMgr& codeMgr)
|
|
: caches(builder_alloc), codeMgr(codeMgr) {}
|
|
|
|
/** allocate a new cache slot or reuse an existing one with the same multiname */
|
|
C* allocateCacheSlot(const Multiname* name);
|
|
};
|
|
|
|
class VarTracker;
|
|
class MopsRangeCheckFilter;
|
|
class PrologWriter;
|
|
|
|
typedef HashMap<LIns*, nanojit::BitSet*> LabelBitSet;
|
|
|
|
// We may replace an existing function call with another based on the
|
|
// context in which it appears. For example, if the result is to be
|
|
// coerced to another type, there may be another function that directly
|
|
// generates the same value. For each such context, an array of
|
|
// Specialization structures provides a mapping from the old function
|
|
// to its replacement.
|
|
|
|
struct Specialization {
|
|
const CallInfo* oldFunc;
|
|
const CallInfo* newFunc;
|
|
};
|
|
|
|
// A compact representation of the possible types of a numeric vector
|
|
// index suitable for indexing a vector of accessor function ids. This
|
|
// is presently used only for an argument to CodegenLIR::emitGetIndexedProperty()
|
|
// and CodegenLIR::emitSetIndexedProperty().
|
|
typedef enum { VI_UINT, VI_INT, VI_DOUBLE, VI_SIZE } IndexKind;
|
|
|
|
/**
|
|
* CodegenLIR is a kitchen sink class containing all state for all passes
|
|
* of the JIT. It is intended to be instantiated on the stack once for each
|
|
* jit-compiled method, and is a terminator of a CodeWriter pipeline.
|
|
*/
|
|
class CodegenLIR : public LirHelper, public CodeWriter {
|
|
public:
|
|
|
|
#ifdef VTUNE
|
|
bool hasDebugInfo;
|
|
GCList<JITCodeInfo> jitInfoList;
|
|
GCList<LineNumberRecord> jitPendingRecords;
|
|
void jitPushInfo();
|
|
JITCodeInfo* jitCurrentInfo();
|
|
|
|
LineNumberRecord* jitAddRecord(uintptr_t pos, uint32_t filename, uint32_t line, bool pending=false);
|
|
void jitFilenameUpdate(uint32_t filename);
|
|
void jitLineNumUpdate(uint32_t line);
|
|
void jitCodePosUpdate(uint32_t pos);
|
|
#endif /* VTUNE */
|
|
|
|
protected:
|
|
friend struct JitInitVisitor;
|
|
MethodInfo *info;
|
|
const MethodSignaturep ms;
|
|
Toplevel* toplevel;
|
|
PoolObject *pool;
|
|
OSR* osr;
|
|
CodegenDriver* driver;
|
|
const FrameState *state;
|
|
const uint8_t* code_pos;
|
|
const uint8_t* try_from;
|
|
const uint8_t* try_to;
|
|
MopsRangeCheckFilter* mopsRangeCheckFilter;
|
|
LIns *vars, *tags;
|
|
LIns *env_param, *argc_param, *ap_param;
|
|
LIns *_save_eip, *_ef;
|
|
LIns *methodFrame;
|
|
LIns *csn;
|
|
LIns *undefConst;
|
|
LIns *restArgc; // NULL or the expression that computes the number of rest arguments
|
|
int restLocal; // -1 or the local var that holds the rest array
|
|
bool interruptable;
|
|
CodegenLabel npe_label;
|
|
CodegenLabel upe_label;
|
|
CodegenLabel interrupt_label;
|
|
CodegenLabel mop_rangeCheckFailed_label;
|
|
CodegenLabel catch_label;
|
|
CodegenLabel call_error_label;
|
|
const uint8_t* lastPcSave;
|
|
LIns *setjmpResult;
|
|
VarTracker *varTracker;
|
|
int framesize;
|
|
#ifdef DEBUGGER
|
|
int dbg_framesize; // count of locals & scopes visible to the debugger
|
|
#endif
|
|
int labelCount;
|
|
bool inlineFastpath; // Allow aggressive or speculative inlining, e.g., introducing new labels.
|
|
CacheBuilder<CallCache> call_cache_builder;
|
|
CacheBuilder<GetCache> get_cache_builder;
|
|
CacheBuilder<SetCache> set_cache_builder;
|
|
PrologWriter *prolog;
|
|
LIns* skip_ins; // Skips from start of body to end of prologue.
|
|
HashMap<LIns*, LIns*> *specializedCallHashMap;
|
|
HashMap<uint32_t, uint32_t> *builtinFunctionOptimizerHashMap;
|
|
HashMap<const uint8_t*, CodegenLabel*> *blockLabels;
|
|
LirWriter* redirectWriter;
|
|
CseFilter* cseFilter; // The CseFilter instance for this method, or NULL if none.
|
|
JITNoise* noise;
|
|
verbose_only(VerboseWriter *vbWriter;)
|
|
verbose_only(LInsPrinter* vbNames;)
|
|
JITDebugInfo *jit_debug_info;
|
|
|
|
#ifdef DEBUGGER
|
|
bool haveDebugger;
|
|
#else
|
|
static const bool haveDebugger = false;
|
|
#endif
|
|
#if defined VMCFG_VTUNE
|
|
static const bool haveVTune = true;
|
|
#else
|
|
static const bool haveVTune = false;
|
|
#endif
|
|
#ifdef DEBUG
|
|
/** jit_sst is an array of sst_mask bytes, used to double check that we
|
|
* are modeling storage types the same way the verifier did for us.
|
|
* Mismatches are caught in writeOpcodeVerified() after the Verifier has
|
|
* updated FrameValue.sst_mask. */
|
|
uint16_t *jit_sst; // array of SST masks to sanity check with FrameState
|
|
ValidateWriter* validate3; // ValidateWriter for method body.
|
|
#endif
|
|
|
|
LIns* insAlloc(int32_t);
|
|
LIns* insAllocForTraits(Traits *t);
|
|
LIns* atomToNativeRep(int loc, LIns *i);
|
|
LIns* atomToNativeRep(Traits *, LIns *i);
|
|
LIns* ptrToNativeRep(Traits*, LIns*);
|
|
LIns* loadAtomRep(int i);
|
|
LIns* localGet(int i);
|
|
LIns* localGetp(int i);
|
|
LIns* localGetd(int i);
|
|
LIns* localGetf(int i); // Aborts if float not enabled
|
|
LIns* localGetf4(int i); // Aborts if float not enabled
|
|
LIns* localGetf4Addr(int i); // Aborts if float not enabled
|
|
LIns* localCopy(int i); // sniff's type from FrameState
|
|
void branchToLabel(LOpcode op, LIns *cond, CodegenLabel& label);
|
|
void branchAndSavePC(LIns *flag, CodegenLabel& label);
|
|
LIns* branchJovToLabel(LOpcode op, LIns *a, LIns *b, CodegenLabel& label);
|
|
void branchToAbcPos(LOpcode op, LIns *cond, const uint8_t* target);
|
|
LIns* mopAddrToRangeCheckedRealAddrAndDisp(LIns* mopAddr, int32_t const size, int32_t* disp);
|
|
LIns* loadEnvScope();
|
|
LIns* loadEnvVTable();
|
|
LIns* loadEnvAbcEnv();
|
|
LIns* loadEnvDomainEnv();
|
|
LIns* loadEnvToplevel();
|
|
LIns* loadEnvFinddefTable();
|
|
LIns* copyMultiname(const Multiname* multiname);
|
|
LIns* initMultiname(const Multiname* multiname, int& csp, bool isDelete =false);
|
|
LIns* storeAtomArgs(int count, int index);
|
|
LIns* storeAtomArgs(LIns *obj, int count, int index);
|
|
LIns* promoteNumberIns(Traits *t, int i);
|
|
#ifdef VMCFG_FLOAT
|
|
LIns* promoteFloatIns(Traits *t, int i);
|
|
LIns* promoteFloat4Ins(Traits *t, int i);
|
|
#endif // VMCFG_FLOAT
|
|
LIns* loadVTable(LIns* obj, Traits* t);
|
|
LIns* cmpEq(const CallInfo *fid, int lhsi, int rhsi);
|
|
LIns* cmpLt(int lhsi, int rhsi);
|
|
LIns* cmpLe(int lhsi, int rhsi);
|
|
LIns* cmpOptimization(int lhsi, int rhsi, LOpcode icmp, LOpcode ucmp, LOpcode fcmp, bool strictOperation = false);
|
|
debug_only( bool isPointer(int i); )
|
|
void emitSetPc(const uint8_t* pc);
|
|
void emitSampleCheck();
|
|
bool verbose();
|
|
CodegenLabel& getCodegenLabel(const uint8_t* pc);
|
|
CodegenLabel& createLabel(const char *name);
|
|
CodegenLabel& createLabel(const char *prefix, int id);
|
|
void patchLater(LIns *br, CodegenLabel &);
|
|
void patchLater(LIns *jtbl, const uint8_t* pc, uint32_t index);
|
|
void emitLabel(CodegenLabel &l);
|
|
void deadvars();
|
|
void deadvars_analyze(Allocator& alloc,
|
|
nanojit::BitSet& varlivein, LabelBitSet& varlabels,
|
|
nanojit::BitSet& taglivein, LabelBitSet& taglabels);
|
|
void deadvars_kill(Allocator& alloc,
|
|
nanojit::BitSet& varlivein, LabelBitSet& varlabels,
|
|
nanojit::BitSet& taglivein, LabelBitSet& taglabels);
|
|
void copyParam(int i, int &offset);
|
|
bool haveSSE2() const;
|
|
|
|
/**
|
|
* Overwrite the given instruction with a LIR_skip and adjust
|
|
* skip_ins if it was pointing to the skipped instruction.
|
|
*/
|
|
void eraseIns(LIns* ins, LIns* prevIns);
|
|
|
|
LIns* loadIns(LOpcode op, int32_t disp, LIns *base, AccSet accSet, LoadQual loadQual=LOAD_NORMAL);
|
|
LIns* storeIns(LOpcode op, LIns* val, int32_t disp, LIns *base, AccSet accSet);
|
|
LIns* Ins(LOpcode op);
|
|
LIns* Ins(LOpcode op, LIns *a);
|
|
LIns* i2dIns(LIns* v);
|
|
LIns* ui2dIns(LIns* v);
|
|
LIns* callIns(const CallInfo *, uint32_t argc, ...);
|
|
|
|
#ifdef VMCFG_FLOAT
|
|
/* Handle 1- and 2-operand numeric operations. Assumption: "number"/"int" is the "fastpath",
|
|
the rest may be slow (should really be typed if the user wants speed) */
|
|
void emitNumericOp1(int32_t op1, const LIREmitter &emitIns);
|
|
void emitNumericOp2(int32_t op1, int32_t op2, const LIREmitter& emitIns);
|
|
#endif // VMCFG_FLOAT
|
|
|
|
/** emit a constructor call, and early bind if possible */
|
|
void emitConstruct(int argc, LIns* ctor, Traits* ctraits);
|
|
|
|
void emitCall(AbcOpcode opcode, intptr_t method_id, int argc, Traits* result, MethodSignaturep);
|
|
void emitCall(AbcOpcode opcode, intptr_t method_id, int argc, LIns* obj, Traits* objType, Traits* result, MethodSignaturep);
|
|
|
|
/** Verifier has already coerced args and emitted the null check; JIT just double checks and emits code */
|
|
void emitTypedCall(AbcOpcode opcode, intptr_t method_id, int argc, Traits* result, MethodInfo*);
|
|
|
|
/** emit a JIT-discovered early bound call. JIT must coerce args and receiver to the proper type */
|
|
void emitCoerceCall(AbcOpcode opcode, intptr_t method_id, int argc, MethodSignaturep mms);
|
|
|
|
/** emit a JIT-discovered early bound call to newInstance and invoke the initializer function */
|
|
void emitConstructCall(intptr_t method_id, int argc, LIns* ctor, Traits* ctraits);
|
|
|
|
/** helper to coerce args to an early bound call to the required types */
|
|
void coerceArgs(MethodSignaturep mms, int argc, int firstArg);
|
|
|
|
void emit(AbcOpcode opcode, uintptr_t op1=0, uintptr_t op2=0, Traits* result=NULL);
|
|
void emitIf(AbcOpcode opcode, const uint8_t* target, int lhs, int rhs);
|
|
void emitSwap(int i, int j);
|
|
void emitCopy(int src, int dest);
|
|
void emitGetscope(int scope, int dest);
|
|
void emitKill(int i);
|
|
#ifdef VMCFG_FASTPATH_ADD
|
|
#ifdef VMCFG_FASTPATH_ADD_INLINE
|
|
void emitIntPlusAtomFastpath(int i, Traits* type, LIns* lhs, LIns* rhs, CodegenLabel &fallback);
|
|
#endif
|
|
void emitAddIntToAtom(int i, int j, Traits* type);
|
|
void emitAddDoubleToAtom(int i, int j, Traits* type);
|
|
void emitAddAtomToInt(int i, int j, Traits* type);
|
|
void emitAddAtomToDouble(int i, int j, Traits* type);
|
|
#endif
|
|
void emitAddAtomToAtom(int i, int j, Traits* type);
|
|
void emitAdd(int i, int j, Traits* type);
|
|
void emitIntConst(int index, int32_t c, Traits* type);
|
|
void emitTaintedIntConst(int index, int32_t c, Traits* type);
|
|
void emitPtrConst(int index, void* c, Traits* type);
|
|
void emitDoubleConst(int index, const double* pd);
|
|
void emitTaintedDoubleConst(int index, const double* pd);
|
|
#ifdef VMCFG_FLOAT
|
|
void emitTaintedFloatConst(int index, const float f);
|
|
void emitTaintedFloat4Const(int index, const float4_t* pf4);
|
|
#endif // VMCFG_FLOAT
|
|
void emitGetslot(int slot, int ptr_index, Traits *slotType);
|
|
void emitSetslot(AbcOpcode opcode, int slot, int ptr_index);
|
|
void emitSetslot(AbcOpcode opcode, int slot, int ptr_index, LIns* value);
|
|
void emitGetGlobalScope(int dest);
|
|
void emitCoerce(uint32_t index, Traits* type);
|
|
void emitCheckNull(LIns* ptr, Traits* type);
|
|
LIns* emitGetIndexedProperty(int objIndex, LIns *index, Traits *result, IndexKind idxKind);
|
|
LIns* emitInlineVectorRead(int objIndexOnStack,
|
|
LIns* index,
|
|
size_t arrayDataOffset, size_t lenOffset, size_t entriesOffset, int scale, LOpcode load_item,
|
|
const CallInfo* helper);
|
|
LIns* emitInlineSpeculativeArrayRead(int objIndexOnStack,
|
|
LIns* index,
|
|
const CallInfo* helper);
|
|
|
|
void emitInlineVectorWrite(int objIndexOnStack,
|
|
LIns* index,
|
|
LIns* value,
|
|
size_t arrayDataOffset, size_t lenOffset, size_t entriesOffset, int scale, LOpcode store_item,
|
|
const CallInfo* helper);
|
|
void emitSetIndexedProperty(int objIndex, int valIndex, LIns *index, IndexKind idxKind);
|
|
void localSet(int i, LIns* o, Traits* type);
|
|
LIns* convertToString(int i, bool preserveNull);
|
|
LIns* coerceToString(int i);
|
|
LIns* coerceToNumber(int i);
|
|
#ifdef VMCFG_FLOAT
|
|
LIns* coerceToFloat(int i);
|
|
LIns* coerceToFloat4(int i);
|
|
LIns* coerceToNumeric(int i);
|
|
LIns* coerceFloatToInt(int i);
|
|
bool matchShuffler(MethodInfo* m, uint8_t* shuffle_mask);
|
|
#endif // VMCFG_FLOAT
|
|
LIns* loadFromSlot(int ptr_index, int slot, Traits* slotType);
|
|
LIns* coerceToType(int i, Traits*);
|
|
void emitInitializers();
|
|
void emitDebugEnter();
|
|
void emitOsrBranch();
|
|
|
|
bool isPromote(LOpcode op);
|
|
LIns* imm2Int(LIns* imm);
|
|
LIns* coerceNumberToInt(int i);
|
|
|
|
bool inlineBuiltinFunction(AbcOpcode opcode, intptr_t method_id, int argc, Traits* result, MethodInfo* mi);
|
|
LIns* optimizeIntCmpWithNumberCall(int callIndex, int otherIndex, LOpcode icmp, bool swap);
|
|
LIns* optimizeStringCmpWithStringCall(int callIndex, int otherIndex, LOpcode icmp, bool swap);
|
|
|
|
void suspendCSE();
|
|
void resumeCSE();
|
|
|
|
LIns* getSpecializedCall(LIns* call);
|
|
LIns* addSpecializedCall(LIns* origCall, LIns* specializedCall);
|
|
LIns* specializeIntCall(LIns *call, const Specialization* specs);
|
|
|
|
LIns* emitStringCall(int index, const CallInfo* stringCall, bool preserveNull);
|
|
|
|
typedef void (CodegenLIR::*EmitMethod)(Traits* t);
|
|
typedef struct {
|
|
int32_t methodId;
|
|
int32_t argCount;
|
|
BuiltinType argType[2];
|
|
const CallInfo *newFunction;
|
|
EmitMethod emitFunction;
|
|
} FunctionMatch;
|
|
|
|
static const FunctionMatch specializedFunctions[];
|
|
|
|
int32_t determineBuiltinMaskForArg (int argOffset);
|
|
LIns* getSpecializedArg (int argOffset, BuiltinType newBt);
|
|
void genBuiltinFunctionOptimizerHashMap();
|
|
|
|
void emitIsNaN(Traits* result);
|
|
void emitIntMathMin(Traits* result);
|
|
void emitIntMathMax(Traits* result);
|
|
void emitStringLength(Traits* result);
|
|
|
|
#ifdef VMCFG_FLOAT
|
|
// helpers
|
|
void emitFloatUnary(Traits* result, LOpcode op);
|
|
void emitFloat4unary(Traits*, LOpcode opcode);
|
|
void emitFloat4binary(Traits*, LOpcode opcode);
|
|
void emitFloat4dot(Traits*, LOpcode opcode);
|
|
LIns* magnitude(LOpcode dot_op, LIns* x);
|
|
void emitMagnitude(Traits* result, LOpcode dot_op);
|
|
void emitDistance(Traits* result, LOpcode dot_op);
|
|
|
|
// emitters for inline float/float4 functions
|
|
void emitFloatAbs(Traits* result);
|
|
void emitFloatReciprocal(Traits* result);
|
|
void emitFloatRsqrt(Traits* result);
|
|
void emitFloatSqrt(Traits* result);
|
|
void emitFloat4abs(Traits* result);
|
|
void emitFloat4max(Traits* result);
|
|
void emitFloat4min(Traits* result);
|
|
void emitFloat4reciprocal(Traits* result);
|
|
void emitFloat4rsqrt(Traits* result);
|
|
void emitFloat4sqrt(Traits* result);
|
|
void emitFloat4normalize(Traits* result);
|
|
void emitFloat4cross(Traits* result);
|
|
void emitFloat4dot(Traits* result);
|
|
void emitFloat4dot2(Traits* result);
|
|
void emitFloat4dot3(Traits* result);
|
|
void emitFloat4magnitude(Traits* result);
|
|
void emitFloat4magnitude2(Traits* result);
|
|
void emitFloat4magnitude3(Traits* result);
|
|
void emitFloat4distance(Traits* result);
|
|
void emitFloat4distance2(Traits* result);
|
|
void emitFloat4distance3(Traits* result);
|
|
LIns* emitDot(LOpcode, LIns*, LIns*);
|
|
void emitFloat4Greater(Traits* result);
|
|
void emitFloat4GreaterOrEqual(Traits* result);
|
|
void emitFloat4Less(Traits* result);
|
|
void emitFloat4LessOrEqual(Traits* result);
|
|
void emitFloat4Equal(Traits* result);
|
|
void emitFloat4NotEqual(Traits* result);
|
|
#endif
|
|
|
|
LIns *optimizeIndexArgumentType(int32_t sp, Traits** indexType);
|
|
|
|
JITDebugInfo* initJitDebugInfo();
|
|
|
|
public:
|
|
~CodegenLIR();
|
|
CodegenLIR(MethodInfo* info, MethodSignaturep ms, Toplevel* toplevel,
|
|
OSR *osr_state, JITNoise* noise);
|
|
GprMethodProc emitMD();
|
|
|
|
// May return true if JIT will always fail based on information known prior to invocation.
|
|
static bool jitWillFail(const MethodSignaturep ms);
|
|
|
|
// CodeWriter methods
|
|
void write(const FrameState* state, const uint8_t* pc, AbcOpcode opcode, Traits *type);
|
|
void writeOp1(const FrameState* state, const uint8_t *pc, AbcOpcode opcode, uint32_t opd1, Traits* type);
|
|
void writeOp2(const FrameState* state, const uint8_t *pc, AbcOpcode opcode, uint32_t opd1, uint32_t opd2, Traits* type);
|
|
void writeMethodCall(const FrameState* state, const uint8_t *pc, AbcOpcode opcode, MethodInfo*, uintptr_t disp_id, uint32_t argc, Traits* type);
|
|
void writeNip(const FrameState* state, const uint8_t *pc, uint32_t count);
|
|
void writeCheckNull(const FrameState* state, uint32_t index);
|
|
void writeCoerce(const FrameState* state, uint32_t index, Traits *type);
|
|
void writeCoerceToNumeric(const FrameState* state, uint32_t index);
|
|
void writeCoerceToFloat4(const FrameState* state, uint32_t index);
|
|
void writePrologue(const FrameState* state, const uint8_t *pc, CodegenDriver*);
|
|
void writeEpilogue(const FrameState* state);
|
|
void writeBlockStart(const FrameState* state);
|
|
void writeOpcodeVerified(const FrameState* state, const uint8_t* pc, AbcOpcode opcode);
|
|
void writeFixExceptionsAndLabels(const FrameState* state, const uint8_t* pc);
|
|
void cleanup();
|
|
};
|
|
}
|
|
|
|
#include "CodegenLIR-inlines.h"
|
|
|
|
#endif /* __avmplus_CodegenLIR__ */
|