зеркало из https://github.com/mozilla/pjs.git
1020 строки
32 KiB
C++
1020 строки
32 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
#ifndef PRIMITIVES_H
|
|
#define PRIMITIVES_H
|
|
|
|
#include "Value.h"
|
|
#include "DoublyLinkedList.h"
|
|
#include "VirtualRegister.h"
|
|
#include "LogModule.h"
|
|
#include "Attributes.h"
|
|
|
|
enum PrimitiveFormat
|
|
{
|
|
pfNone,
|
|
pfPhi, // Phi node
|
|
pfConst, // Constant
|
|
pfDebug, // Debug or Break
|
|
pfControl, // If condition node or switch index node
|
|
pfCatch, // Catch exception producer
|
|
pfProj, // Projection
|
|
pfArg, // Argument
|
|
pfResult, // Result
|
|
pfUnaryGeneric, // General one-argument operations
|
|
pfBinaryGeneric, // General two-argument operations
|
|
pfLd, // Ld
|
|
pfSt, // St
|
|
pfMonitor, // MEnter and MExit
|
|
pfSync, // Sync
|
|
pfSysCall, // SysCall
|
|
pfCall // Call
|
|
};
|
|
const uint nPrimitiveFormats = pfCall + 1;
|
|
|
|
enum PrimitiveCategory
|
|
{
|
|
pcNone,
|
|
pcPhi, // Phi node
|
|
pcConst, // Constant
|
|
pcDebug, // Debug or Break
|
|
pcIfCond, // If condition node
|
|
pcSwitch, // Switch index node
|
|
pcCatch, // Catch exception producer
|
|
pcProj, // Projection
|
|
pcArg, // Argument
|
|
pcResult, // Result
|
|
pcGeneric, // Two-argument integer arithmetic and logical
|
|
pcDivMod, // Div and Mod
|
|
pcShift, // Shifts
|
|
pcExt, // Ext
|
|
pcFGeneric, // Floating-point arithmetic
|
|
pcConv, // Conv
|
|
pcFConv, // FConv
|
|
pcCmp, // Cmp
|
|
pcFCmp, // FCmp
|
|
pcCond, // Cond
|
|
pcCond3, // Cond3
|
|
pcCheck1, // ChkNull
|
|
pcCheck2, // ChkCast, Limit, and LimCast
|
|
pcLd, // Ld
|
|
pcSt, // St
|
|
pcMonitor, // MEnter and MExit
|
|
pcSync, // Sync
|
|
pcSysCall, // SysCall
|
|
pcCall // Call
|
|
};
|
|
const uint nPrimitiveCategories = pcCall + 1;
|
|
|
|
extern const PrimitiveFormat categoryFormats[nPrimitiveCategories];
|
|
|
|
#include "PrimitiveOperations.h"
|
|
|
|
#ifdef DEBUG_LOG
|
|
int print(LogModuleObject &f, PrimitiveFormat pf);
|
|
int print(LogModuleObject &f, PrimitiveCategory pc);
|
|
int print(LogModuleObject &f, PrimitiveOperation pk);
|
|
#endif
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
class DataNode;
|
|
class ControlNode;
|
|
class ControlEdge;
|
|
class Instruction;
|
|
|
|
class VariableOrConstant
|
|
{
|
|
private:
|
|
static const DataNode constSources[nValueKinds];
|
|
|
|
protected: // (In DEBUG versions both variables below are nil if not explicitly set.)
|
|
const DataNode *source; // The referenced DataNode or (if pointing to a constant) Source
|
|
DataNode *node; // The node to which this DataConsumer belongs (used in DataConsumer only)
|
|
union {
|
|
DoublyLinkedNode links; // Links to other consumers of a non-constant value (used in DataConsumer only)
|
|
Value value; // Constant
|
|
};
|
|
|
|
public:
|
|
#ifdef DEBUG
|
|
VariableOrConstant() {source = 0;}
|
|
#endif
|
|
bool operator==(const VariableOrConstant &poc2) const;
|
|
bool operator!=(const VariableOrConstant &poc2) const {return !(*this == poc2);}
|
|
|
|
bool isImmediate() const {return false;}
|
|
bool hasKind(ValueKind kind) const;
|
|
ValueKind getKind() const;
|
|
|
|
inline bool isConstant() const;
|
|
inline bool isVariable() const;
|
|
DataNode &getVariable() const;
|
|
inline const Value &getConstant() const;
|
|
inline Value &getConstant();
|
|
bool isAlwaysNonzero() const;
|
|
Type *getDynamicType() const;
|
|
|
|
void setConstant(Int32 v);
|
|
void setConstant(Int64 v);
|
|
void setConstant(Flt32 v);
|
|
void setConstant(Flt64 v);
|
|
void setConstant(addr v);
|
|
void setConstant(Condition v);
|
|
void setConstant(Memory v);
|
|
void setConstant(ValueKind kind, const Value &v);
|
|
void setVariable(DataNode &producer);
|
|
};
|
|
|
|
|
|
// A DataConsumer is like a VariableOrConstant except that it belongs to some (consumer) DataNode.
|
|
// Also, if the DataConsumer is a variable, it is linked into the source (producer) DataNode's
|
|
// doubly-linked list of consumers.
|
|
// If the DataConsumer currently contains a variable, do not change its source (for example by
|
|
// using either setConstant or setVariable). Instead, call either clearVariable or destroy
|
|
// first and then call setConstant or setVariable, thus making sure that the DataNodes' lists
|
|
// of consumers are kept consistent. In DEBUG versions asserts will enforce this rule.
|
|
class DataConsumer: public VariableOrConstant
|
|
{
|
|
#ifdef DEBUG
|
|
public:
|
|
DataConsumer() {node = 0;}
|
|
private:
|
|
DataConsumer(const DataConsumer &); // Copying forbidden
|
|
void operator=(const DataConsumer &); // Copying forbidden
|
|
#endif
|
|
public:
|
|
void operator=(const VariableOrConstant &p);
|
|
void move(DataConsumer &src);
|
|
void destroy();
|
|
|
|
DataNode &getNode() const {assert(node); return *node;}
|
|
void setNode(DataNode &n) {node = &n;}
|
|
|
|
#ifdef DEBUG
|
|
void setConstant(Int32 v);
|
|
void setConstant(Int64 v);
|
|
void setConstant(Flt32 v);
|
|
void setConstant(Flt64 v);
|
|
void setConstant(addr v);
|
|
void setConstant(Condition v);
|
|
void setConstant(Memory v);
|
|
void setConstant(ValueKind kind, const Value &v);
|
|
#endif
|
|
void setVariable(DataNode &producer);
|
|
inline void clearVariable();
|
|
void clear() {if (isVariable()) clearVariable();}
|
|
|
|
// DoublyLinkedNode administration
|
|
static DataConsumer &linkOwner(DoublyLinkedNode &l) {return *(DataConsumer *)((char *)&l - offsetof(DataConsumer, links));}
|
|
DoublyLinkedNode &getLinks() {return links;}
|
|
|
|
#ifdef DEBUG_LOG
|
|
int printRef(LogModuleObject &f) const;
|
|
#endif
|
|
};
|
|
|
|
inline bool isIfCond(PrimitiveOperation op) {return op >= poIfLt && op <= poIfUGe;}
|
|
inline bool isCond2(PrimitiveOperation op) {return op >= poLt_I && op <= poUGe_I;}
|
|
inline bool isCond3(PrimitiveOperation op) {return op >= poCatL_I && op <= poCatCG_I;}
|
|
inline Condition2 ifCondToCondition2(PrimitiveOperation op) {assert(isIfCond(op)); return (Condition2)(op - poIfLt + condLt);}
|
|
inline Condition2 cond2ToCondition2(PrimitiveOperation op) {assert(isCond2(op)); return (Condition2)(op - poLt_I + condLt);}
|
|
inline Condition3 cond3ToCondition3(PrimitiveOperation op) {assert(isCond3(op)); return (Condition3)(op - poCatL_I);}
|
|
inline PrimitiveOperation condition2ToIfCond(Condition2 cond2) {return (PrimitiveOperation)(cond2 - condLt + poIfLt);}
|
|
inline PrimitiveOperation condition2ToCond2(Condition2 cond2) {return (PrimitiveOperation)(cond2 - condLt + poLt_I);}
|
|
inline PrimitiveOperation condition3ToCond3(Condition3 cond3) {return (PrimitiveOperation)(cond3 + poCatL_I);}
|
|
|
|
DataNode *partialComputeComparison(Condition2 cond2, VariableOrConstant c, bool &result);
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
extern Uint16 gCurLineNumber; // debug get line number info
|
|
|
|
class DataNode
|
|
{
|
|
ValueKind kind ENUM_8; // Kind of the constant or the data node's outgoing data edge
|
|
bool constant BOOL_8; // True if this value is a constant (held inside the DataConsumer)
|
|
|
|
enum DataNodeFlag
|
|
{
|
|
dnIsReal, // This primitive can be created (as opposed to, say, poNone, which cannot)
|
|
dnCanRaiseException, // This primitive can raise an exception
|
|
dnIsRoot // This primitive feeds a control node, so it should't be deleted just because its data
|
|
}; // output is dead.
|
|
|
|
struct Template
|
|
{
|
|
PrimitiveOperation operation ENUM_16; // Minor kind of primitive
|
|
PrimitiveCategory category ENUM_8; // Major kind of primitive
|
|
ValueKind kind ENUM_8; // Kind of the constant or the data node's outgoing data edge
|
|
Uint16 flags; // DataNodeFlags
|
|
};
|
|
|
|
static const Template templates[nPrimitiveOperations];
|
|
|
|
#ifdef DEBUG
|
|
public:
|
|
enum Origin {aoVariable, aoConstant, aoEither};
|
|
|
|
struct InputConstraint
|
|
{
|
|
ValueKind kind ENUM_8; // Kinds of an input. vkVoid means "any kind".
|
|
Origin origin ENUM_8; // Can the input be a variable, a constant, or either?
|
|
};
|
|
private:
|
|
|
|
struct InputConstraintPattern
|
|
{
|
|
const InputConstraint *inputConstraints;// Constraints on the inputs.
|
|
Uint8 nInputConstraints; // Number of inputs (repeat input counts here as one input)
|
|
bool repeat BOOL_8; // If true, the last input can be repeated zero or more times
|
|
};
|
|
|
|
static const InputConstraintPattern inputConstraintPatterns[nPrimitiveOperations];
|
|
#endif
|
|
|
|
PrimitiveOperation operation ENUM_16; // Minor kind of primitive
|
|
PrimitiveCategory category ENUM_8; // Major kind of primitive
|
|
bool alwaysNonzero BOOL_8; // If true, the value of this DataNode is known to always be nonzero
|
|
Uint16 flags; // DataNodeFlags
|
|
DoublyLinkedList<DataConsumer> consumers; // List of consumers of data produced by this DataNode
|
|
protected:
|
|
ControlNode *container; // The ControlNode that contains this DataNode or nil if none
|
|
|
|
DataConsumer *inputsBegin; // Array of incoming data flow edges
|
|
DataConsumer *inputsEnd; // Last element + 1
|
|
Uint16 lineNumber; // Java source line number
|
|
public:
|
|
Uint32 producerNumber; // Serial number assigned and used by the register allocator and pretty-printer
|
|
|
|
IntervalMapping *mapping; // The localVariable
|
|
// corresponding to the
|
|
// mapping
|
|
private:
|
|
struct _annotation
|
|
{
|
|
VirtualRegisterPtr lowVReg;
|
|
VirtualRegisterPtr highVReg;
|
|
Instruction *insn;
|
|
} annotation;
|
|
|
|
|
|
#ifdef DEBUG
|
|
// make sure we do not incorrectly use annotations
|
|
enum AnnotationKind {anNone, anInsn, anVr};
|
|
AnnotationKind annotationKind;
|
|
#endif
|
|
|
|
protected:
|
|
explicit DataNode(PrimitiveOperation op);
|
|
|
|
void destroy();
|
|
|
|
public:
|
|
DataNode(ValueKind kind) : kind(kind), constant(true) {};
|
|
|
|
virtual ~DataNode() {};
|
|
|
|
// Use these instead of expressions like "getFormat() == pfLd" because the latter doesn't do
|
|
// type checking -- the compiler will happily let you make errors like "getFormat() == pcLd".
|
|
inline bool isConstant() const {return constant;}
|
|
inline bool isVariable() const {return !constant;}
|
|
|
|
bool hasKind(ValueKind k) const {return k == kind;}
|
|
bool hasFormat(PrimitiveFormat f) const {return f == categoryFormats[category];}
|
|
bool hasCategory(PrimitiveCategory c) const {return c == category;}
|
|
bool hasOperation(PrimitiveOperation op) const {return op == operation;}
|
|
ValueKind getKind() const {return kind;}
|
|
PrimitiveFormat getFormat() const {return categoryFormats[category];}
|
|
PrimitiveCategory getCategory() const {return category;}
|
|
PrimitiveOperation getOperation() const {return operation;}
|
|
void setOperation(PrimitiveOperation op);
|
|
|
|
// Flag queries
|
|
bool isReal() const {return flags>>dnIsReal & 1;}
|
|
bool canRaiseException() const {return flags>>dnCanRaiseException & 1;}
|
|
bool isRoot() const {return flags>>dnIsRoot & 1;}
|
|
|
|
bool producesVariable() const {return !isVoidKind(kind);}
|
|
bool isAlwaysNonzero() const {return alwaysNonzero;}
|
|
void setAlwaysNonzero(bool nz) {alwaysNonzero = nz;}
|
|
bool hasConsumers() const {return !consumers.empty();}
|
|
const DoublyLinkedList<DataConsumer> &getConsumers() const {return consumers;}
|
|
void addConsumer(DataConsumer &consumer) {consumers.addLast(consumer);}
|
|
|
|
ControlNode *getContainer() const {return container;}
|
|
void setContainer(ControlNode *c) {assert(!container); container = c;} // For use by ControlNode only.
|
|
// see also changeContainer in PhiNode and Primitive.
|
|
|
|
Uint32 nInputs() const {return inputsEnd - inputsBegin;}
|
|
DataConsumer *getInputsBegin() const {return inputsBegin;}
|
|
DataConsumer *getInputsEnd() const {return inputsEnd;}
|
|
DataConsumer &nthInput(Uint32 n) const {assert(n < nInputs()); return inputsBegin[n];}
|
|
bool nthInputIsConstant(Uint32 n) const {return nthInput(n).isConstant();}
|
|
DataNode &nthInputVariable(Uint32 n) const;
|
|
bool isSignedCompare() const;
|
|
Value &nthInputConstant(Uint32 n) const {return nthInput(n).getConstant();}
|
|
|
|
Uint16 getLineNumber() const {return lineNumber;}
|
|
void setLineNumber(Uint16 lineNumber) {DataNode::lineNumber = lineNumber;}
|
|
|
|
inline void annotate(Instruction& insn);
|
|
inline void annotate(VirtualRegister& vReg, VRClass cl);
|
|
inline void annotateLow(VirtualRegister& vReg, VRClass cl);
|
|
inline void annotateHigh(VirtualRegister& vReg, VRClass cl);
|
|
inline void annotate(VirtualRegister& lowVReg, VirtualRegister& highVReg, VRClass cl);
|
|
inline VirtualRegister* getVirtualRegisterAnnotation();
|
|
inline VirtualRegister* getLowVirtualRegisterAnnotation();
|
|
inline VirtualRegister* getHighVirtualRegisterAnnotation();
|
|
inline VirtualRegisterPtr& getVirtualRegisterPtrAnnotation();
|
|
inline VirtualRegisterPtr& getLowVirtualRegisterPtrAnnotation();
|
|
inline VirtualRegisterPtr& getHighVirtualRegisterPtrAnnotation();
|
|
inline Instruction* getInstructionAnnotation();
|
|
|
|
Uint32 assignProducerNumbers(Uint32 base);
|
|
virtual void setInstructionRoot(Instruction* /*inInstructionRoot*/) {}
|
|
virtual Instruction* getInstructionRoot() {return NULL;}
|
|
|
|
#ifdef DEBUG
|
|
virtual void validate(); // perform self-consistency checks
|
|
InputConstraintPattern getInputConstraintPattern() { return inputConstraintPatterns[getOperation()]; }
|
|
bool mark BOOL_8; // marks this primitive as having been seen during an ordered search
|
|
#endif
|
|
|
|
#ifdef DEBUG_LOG
|
|
protected:
|
|
virtual void printOutgoing(LogModuleObject &f) const;
|
|
virtual void printIncoming(LogModuleObject &f) const;
|
|
public:
|
|
int printRef(LogModuleObject &f) const;
|
|
void printPretty(LogModuleObject &f, int margin) const;
|
|
#endif
|
|
|
|
#if 1
|
|
// HACK! These are temporary stubs and will go away soon.
|
|
DataNode *getOutgoingEdgesBegin() {return this;}
|
|
DataNode *getOutgoingEdgesEnd() {return this + producesVariable();}
|
|
#endif
|
|
};
|
|
|
|
|
|
class PhiNode: public DataNode, public DoublyLinkedEntry<PhiNode> // Links to other PhiNodes in the same ControlNode
|
|
{
|
|
DataConsumer *inputsLimit; // Pointer past last entry allocated in the inputsBegin array
|
|
// (may include extra slop after inputsEnd)
|
|
|
|
public:
|
|
PhiNode(Uint32 nExpectedInputs, ValueKind kind, Pool &pool);
|
|
void destroy();
|
|
|
|
static PhiNode &cast(DataNode &node)
|
|
{assert(node.getFormat() == pfPhi); return *static_cast<PhiNode *>(&node);}
|
|
|
|
void clearContainer() {assert(container); remove(); container = 0;}
|
|
void changeContainer(ControlNode *c) {assert(container); remove(); container = c;} // For use by ControlNode only.
|
|
|
|
void addInput(DataNode &producer, Pool &pool);
|
|
void removeSomeInputs(Function2<bool, DataConsumer &, ControlEdge &> &f);
|
|
};
|
|
|
|
|
|
class Primitive: public DataNode, public DoublyLinkedEntry<Primitive> // Links to other Primitives in the same ControlNode
|
|
{
|
|
protected:
|
|
Instruction *instructionRoot; // Instructions which creates this primitive if no outputs
|
|
Uint16 burgState; // BURG state given to this node
|
|
Uint16 lineNumber; // corresponds to src line#
|
|
Uint32 bytecodeIndex; // Bytecode Index
|
|
Uint16 debugLineNumber; // contains value >= lineNumber of all children (during InstructionScheduling)
|
|
|
|
public:
|
|
explicit Primitive(PrimitiveOperation op, Uint32 bci);
|
|
void destroy();
|
|
|
|
static Primitive &cast(DataNode &node)
|
|
{assert(node.getFormat() >= pfConst); return *static_cast<Primitive *>(&node);}
|
|
|
|
void clearContainer() {assert(container); remove(); container = 0;}
|
|
void changeContainer(ControlNode *c) {assert(container); remove(); container = c;} // For use by ControlNode only.
|
|
|
|
Uint16 getBurgState() const { return burgState;}
|
|
Uint16* getBurgStatePtr() { return &burgState; }
|
|
Uint16 setBurgState(Uint16 newState) {burgState = newState; return newState;}
|
|
|
|
Uint16 getLineNumber() {return lineNumber;}
|
|
Uint16 setLineNumber(Uint16 newLineNumber) {lineNumber = newLineNumber; return lineNumber;}
|
|
|
|
Uint16 getDebugLineNumber() {return debugLineNumber;}
|
|
Uint16 setDebugLineNumber(Uint16 newDebugLineNumber) {debugLineNumber = newDebugLineNumber; return debugLineNumber;}
|
|
|
|
Uint32 getBytecodeIndex() {return bytecodeIndex;}
|
|
void setBytecodeIndex(Uint32 bci) {bytecodeIndex = bci;};
|
|
|
|
void setInstructionRoot(Instruction* inInstructionRoot) {instructionRoot = inInstructionRoot;}
|
|
Instruction* getInstructionRoot() {return instructionRoot;}
|
|
};
|
|
|
|
|
|
// --- PRIVATE ----------------------------------------------------------------
|
|
|
|
|
|
// A primitive that has no incoming edges
|
|
class Primitive0: public Primitive
|
|
{
|
|
protected:
|
|
explicit Primitive0(PrimitiveOperation op, Uint32 bci);
|
|
};
|
|
|
|
|
|
// A primitive that has one incoming edge
|
|
class Primitive1: public Primitive
|
|
{
|
|
protected:
|
|
DataConsumer input; // Incoming data edge
|
|
|
|
explicit Primitive1(PrimitiveOperation op, Uint32 bci);
|
|
|
|
public:
|
|
DataConsumer &getInput() {return input;}
|
|
};
|
|
|
|
|
|
// A primitive that has two incoming edges
|
|
class Primitive2: public Primitive
|
|
{
|
|
protected:
|
|
DataConsumer inputs[2]; // Incoming data edges
|
|
|
|
explicit Primitive2(PrimitiveOperation op, Uint32 bci);
|
|
|
|
public:
|
|
DataConsumer *getInputs() {return inputs;}
|
|
};
|
|
|
|
|
|
// --- PUBLIC -----------------------------------------------------------------
|
|
|
|
|
|
// Lone constants
|
|
class PrimConst: public Primitive0
|
|
{
|
|
public:
|
|
const Value value;
|
|
|
|
PrimConst(ValueKind vk, const Value &value, Uint32 bci);
|
|
|
|
static PrimConst &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfConst)); return *static_cast<PrimConst *>(&node);}
|
|
|
|
#ifdef DEBUG
|
|
void validate(); // perform self-consistency checks
|
|
#endif
|
|
|
|
#ifdef DEBUG_LOG
|
|
protected:
|
|
virtual void printIncoming(LogModuleObject &f) const;
|
|
#endif
|
|
};
|
|
|
|
|
|
enum TupleComponent
|
|
{
|
|
tcMemory = 0, // Outgoing memory edge
|
|
tcValue = 1, // Value returned from a volatile load
|
|
tcFirstResult = 1, // Component number of first result from a call or system call; subsequent results, if any,
|
|
// have TupleComponent numbers starting from 2.
|
|
tcMAX = 0x80000000 // (dummy value to force TupleComponent to be 32 bits wide)
|
|
};
|
|
|
|
// Projections of tuple values into their components
|
|
class PrimProj: public Primitive1
|
|
{
|
|
public:
|
|
const TupleComponent component; // Number that indicates which component we're getting from the tuple
|
|
|
|
PrimProj(PrimitiveOperation op, TupleComponent component,
|
|
bool alwaysNonzero, Uint32 bci);
|
|
PrimProj(ValueKind vk, TupleComponent component, bool alwaysNonzero,
|
|
Uint32 bci);
|
|
|
|
static PrimProj &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfProj)); return *static_cast<PrimProj *>(&node);}
|
|
|
|
#ifdef DEBUG_LOG
|
|
protected:
|
|
virtual void printIncoming(LogModuleObject &f) const;
|
|
#endif
|
|
};
|
|
|
|
|
|
// Incoming function arguments
|
|
class PrimArg: public Primitive0
|
|
{
|
|
public:
|
|
PrimArg();
|
|
void initOutgoingEdge(ValueKind vk, bool alwaysNonzero);
|
|
|
|
static PrimArg &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfArg)); return *static_cast<PrimArg *>(&node);}
|
|
|
|
#ifdef DEBUG_LOG
|
|
protected:
|
|
virtual void printIncoming(LogModuleObject &f) const;
|
|
#endif
|
|
};
|
|
|
|
|
|
// Outgoing function results
|
|
class PrimResult: public Primitive1
|
|
{
|
|
public:
|
|
explicit PrimResult(Uint32 bci, ValueKind vk = vkMemory);
|
|
void setResultKind(ValueKind vk);
|
|
|
|
static PrimResult &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfResult)); return *static_cast<PrimResult *>(&node);}
|
|
|
|
#ifdef DEBUG_LOG
|
|
protected:
|
|
virtual void printOutgoing(LogModuleObject &f) const;
|
|
#endif
|
|
};
|
|
|
|
|
|
// Anchor primitives for linking if and switch inputs to their control nodes;
|
|
class PrimControl: public Primitive1
|
|
{
|
|
public:
|
|
explicit PrimControl(PrimitiveOperation op, Uint32 bci);
|
|
|
|
static PrimControl &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfControl)); return *static_cast<PrimControl *>(&node);}
|
|
};
|
|
|
|
|
|
// Caught exceptions
|
|
class PrimCatch: public Primitive0
|
|
{
|
|
public:
|
|
PrimCatch(Uint32 bci);
|
|
|
|
static PrimCatch &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfCatch)); return *static_cast<PrimCatch *>(&node);}
|
|
};
|
|
|
|
|
|
// Unary arithmetic and logical primitives
|
|
class PrimUnaryGeneric: public Primitive1
|
|
{
|
|
public:
|
|
explicit PrimUnaryGeneric(PrimitiveOperation op, Uint32 bci,
|
|
bool alwaysNonzero = false);
|
|
|
|
static PrimUnaryGeneric &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfUnaryGeneric)); return *static_cast<PrimUnaryGeneric *>(&node);}
|
|
};
|
|
|
|
|
|
// Binary arithmetic and logical primitives, including Div and Mod
|
|
class PrimBinaryGeneric: public Primitive2
|
|
{
|
|
public:
|
|
explicit PrimBinaryGeneric(PrimitiveOperation op, Uint32 bci,
|
|
bool alwaysNonzero = false);
|
|
|
|
static PrimBinaryGeneric &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfBinaryGeneric)); return *static_cast<PrimBinaryGeneric *>(&node);}
|
|
};
|
|
|
|
|
|
// Memory read primitives
|
|
class PrimLoad: public Primitive2
|
|
{
|
|
protected:
|
|
bool hasOutgoingMemory; // True if this is a volatile load
|
|
|
|
public:
|
|
PrimLoad(PrimitiveOperation op, bool hasOutgoingMemory, Uint32 bci);
|
|
|
|
static PrimLoad &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfLd)); return *static_cast<PrimLoad *>(&node);}
|
|
|
|
DataConsumer &getIncomingMemory() {return inputs[0];}
|
|
DataConsumer &getAddress() {return inputs[1];}
|
|
};
|
|
|
|
|
|
// Memory write primitives
|
|
class PrimStore: public Primitive
|
|
{
|
|
protected:
|
|
DataConsumer inputs[3]; // Incoming data edges (memory, address, and data)
|
|
|
|
public:
|
|
explicit PrimStore(PrimitiveOperation op, Uint32 bci);
|
|
|
|
static PrimStore &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfSt)); return *static_cast<PrimStore *>(&node);}
|
|
|
|
DataConsumer &getIncomingMemory() {return inputs[0];}
|
|
DataConsumer &getAddress() {return inputs[1];}
|
|
DataConsumer &getData() {return inputs[2];}
|
|
};
|
|
|
|
|
|
// Monitor access primitives
|
|
class PrimMonitor: public Primitive2
|
|
{
|
|
Uint32 slot; // Index of stack slot for the saved subheader
|
|
|
|
public:
|
|
enum {unknownSlot = (Uint32)-1}; // Value to store in slot if not known yet
|
|
|
|
PrimMonitor(PrimitiveOperation op, Uint32 slot, Uint32 bci);
|
|
|
|
static PrimMonitor &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfMonitor)); return *static_cast<PrimMonitor *>(&node);}
|
|
|
|
DataConsumer &getIncomingMemory() {return inputs[0];}
|
|
DataConsumer &getMonitor() {return inputs[1];}
|
|
Uint32 getSlot() const {assert(slot != unknownSlot); return slot;}
|
|
};
|
|
|
|
|
|
// Memory synchronization point primitives
|
|
class PrimSync: public Primitive0
|
|
{
|
|
public:
|
|
PrimSync(Uint32 bci);
|
|
|
|
static PrimSync &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfSync)); return *static_cast<PrimSync *>(&node);}
|
|
};
|
|
|
|
|
|
struct SysCall;
|
|
|
|
// System call primitives
|
|
class PrimSysCall: public Primitive
|
|
{
|
|
public:
|
|
const SysCall &sysCall; // The type of system call represented here
|
|
|
|
PrimSysCall(const SysCall &sysCall, Pool &pool, Uint32 bci);
|
|
|
|
static PrimSysCall &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfSysCall)); return *static_cast<PrimSysCall *>(&node);}
|
|
|
|
#ifdef DEBUG_LOG
|
|
protected:
|
|
virtual void printIncoming(LogModuleObject &f) const;
|
|
#endif
|
|
};
|
|
|
|
|
|
// Function call primitives
|
|
class PrimCall: public Primitive
|
|
{
|
|
public:
|
|
const uint nArguments; // Number of arguments in this call (does not include the function or memory edges)
|
|
const uint nResults; // Number of results in this call (does not include the outgoing memory edge)
|
|
const Method *const method; // The Method being called or nil if not known
|
|
|
|
PrimCall(PrimitiveOperation op, const Method *method, uint nArguments,
|
|
uint nResults, Pool &pool, Uint32 bci);
|
|
|
|
static PrimCall &cast(DataNode &node)
|
|
{assert(node.hasFormat(pfCall)); return *static_cast<PrimCall *>(&node);}
|
|
|
|
DataConsumer &getIncomingMemory() {return inputsBegin[0];}
|
|
DataConsumer &getFunction() {return inputsBegin[1];}
|
|
DataConsumer *getArguments() {return inputsBegin + 2;}
|
|
|
|
#ifdef DEBUG_LOG
|
|
protected:
|
|
virtual void printIncoming(LogModuleObject &f) const;
|
|
#endif
|
|
};
|
|
|
|
|
|
// --- INLINES ----------------------------------------------------------------
|
|
|
|
|
|
inline bool VariableOrConstant::hasKind(ValueKind kind) const {assert(source); return source->hasKind(kind);}
|
|
inline ValueKind VariableOrConstant::getKind() const {assert(source); return source->getKind();}
|
|
|
|
inline bool VariableOrConstant::isConstant() const {assert(source); return source->isConstant();}
|
|
inline bool VariableOrConstant::isVariable() const {assert(source); return source->isVariable();}
|
|
|
|
inline const Value &VariableOrConstant::getConstant() const {assert(source && source->isConstant()); return value;}
|
|
inline Value &VariableOrConstant::getConstant() {assert(source && source->isConstant()); return value;}
|
|
|
|
inline void VariableOrConstant::setConstant(Int32 v) {source = constSources + vkInt; value.i = v;}
|
|
inline void VariableOrConstant::setConstant(Int64 v) {source = constSources + vkLong; value.l = v;}
|
|
inline void VariableOrConstant::setConstant(Flt32 v) {source = constSources + vkFloat; value.f = v;}
|
|
inline void VariableOrConstant::setConstant(Flt64 v) {source = constSources + vkDouble; value.d = v;}
|
|
inline void VariableOrConstant::setConstant(addr v) {source = constSources + vkAddr; value.a = v;}
|
|
inline void VariableOrConstant::setConstant(Condition v) {source = constSources + vkCond; value.c = v;}
|
|
inline void VariableOrConstant::setConstant(Memory v) {source = constSources + vkMemory; value.m = v;}
|
|
inline void VariableOrConstant::setConstant(ValueKind kind, const Value &v) {source = constSources + kind; value = v;}
|
|
|
|
//
|
|
// Return the producer linked to this VariableOrConstant or DataConsumer.
|
|
//
|
|
inline DataNode &VariableOrConstant::getVariable() const
|
|
{
|
|
assert(source && source->isVariable());
|
|
return *const_cast<DataNode *>(source);
|
|
}
|
|
|
|
|
|
//
|
|
// If known, return the actual type of the object produced by this VariableOrConstant.
|
|
// If not known, return nil.
|
|
// The returned type is the most specific type of the object, not a conservative statically
|
|
// computed bound.
|
|
// Currently this method works only on VariableOrConstants of kind vkAddr.
|
|
//
|
|
inline Type *VariableOrConstant::getDynamicType() const
|
|
{
|
|
assert(hasKind(vkAddr));
|
|
// For now, don't infer types of non-constants.
|
|
return isConstant() ? objectAddressType(value.a) : 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Set this VariableOrConstant to represent the given producer.
|
|
//
|
|
inline void VariableOrConstant::setVariable(DataNode &producer)
|
|
{
|
|
source = &producer;
|
|
links.init();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
inline void DataConsumer::setConstant(Int32 v) {assert(!(source && source->isVariable())); VariableOrConstant::setConstant(v);}
|
|
inline void DataConsumer::setConstant(Int64 v) {assert(!(source && source->isVariable())); VariableOrConstant::setConstant(v);}
|
|
inline void DataConsumer::setConstant(Flt32 v) {assert(!(source && source->isVariable())); VariableOrConstant::setConstant(v);}
|
|
inline void DataConsumer::setConstant(Flt64 v) {assert(!(source && source->isVariable())); VariableOrConstant::setConstant(v);}
|
|
inline void DataConsumer::setConstant(addr v) {assert(!(source && source->isVariable())); VariableOrConstant::setConstant(v);}
|
|
inline void DataConsumer::setConstant(Condition v) {assert(!(source && source->isVariable())); VariableOrConstant::setConstant(v);}
|
|
inline void DataConsumer::setConstant(Memory v) {assert(!(source && source->isVariable())); VariableOrConstant::setConstant(v);}
|
|
inline void DataConsumer::setConstant(ValueKind kind, const Value &v) {assert(!(source && source->isVariable())); VariableOrConstant::setConstant(kind, v);}
|
|
#endif
|
|
|
|
inline void DataConsumer::clearVariable() {assert(source && source->isVariable()); links.remove(); DEBUG_ONLY(source = 0);}
|
|
|
|
//
|
|
// Destroy this DataConsumer, which must have been initialized. This DataConsumer
|
|
// is unlinked from any linked lists to which it belonged and left uninitialized.
|
|
//
|
|
inline void DataConsumer::destroy()
|
|
{
|
|
if (isVariable())
|
|
links.remove();
|
|
#ifdef DEBUG
|
|
source = 0;
|
|
node = 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
//
|
|
// Link the DataConsumer located to the given, non-constant source.
|
|
//
|
|
inline void DataConsumer::setVariable(DataNode &producer)
|
|
{
|
|
assert(!(source && source->isVariable()));
|
|
VariableOrConstant::setVariable(producer);
|
|
producer.addConsumer(*this);
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
//
|
|
// Initialize this DataNode, which will contain a primitive or phi node with
|
|
// the given operation.
|
|
//
|
|
inline DataNode::DataNode(PrimitiveOperation op)
|
|
{
|
|
const Template &t = templates[op];
|
|
kind = t.kind;
|
|
constant = false;
|
|
operation = t.operation;
|
|
category = t.category;
|
|
alwaysNonzero = false;
|
|
flags = t.flags;
|
|
container = 0;
|
|
mapping = 0;
|
|
|
|
assert(isReal());
|
|
|
|
#ifdef DEBUG
|
|
producerNumber = (Uint32)-1;
|
|
annotationKind = anNone;
|
|
#endif
|
|
annotation.insn = NULL;
|
|
lineNumber = gCurLineNumber++;
|
|
}
|
|
|
|
|
|
//
|
|
// Change the operation of a DataNode to the given operation.
|
|
// A DataNode's operation can only be changed within the same category.
|
|
//
|
|
inline void DataNode::setOperation(PrimitiveOperation op)
|
|
{
|
|
const Template &t = templates[op];
|
|
assert(hasCategory(t.category));
|
|
kind = t.kind;
|
|
operation = t.operation;
|
|
alwaysNonzero = false;
|
|
flags = t.flags;
|
|
}
|
|
|
|
|
|
//
|
|
// Return the nth input, which must be a variable.
|
|
//
|
|
inline DataNode &DataNode::nthInputVariable(Uint32 n) const
|
|
{
|
|
return nthInput(n).getVariable();
|
|
}
|
|
|
|
|
|
inline void DataNode::annotate(Instruction& insn)
|
|
{
|
|
annotation.insn = &insn;
|
|
DEBUG_ONLY(annotationKind = anInsn);
|
|
}
|
|
|
|
inline void DataNode::annotateLow(VirtualRegister& vReg, VRClass cl)
|
|
{
|
|
annotation.lowVReg.initialize(vReg, cl);
|
|
DEBUG_ONLY(annotationKind = anVr);
|
|
#ifdef DEBUG
|
|
vReg.isAnnotated = true;
|
|
if (vReg.isPreColored())
|
|
trespass("This is not a temporary register. It cannot be preColored. !!!");
|
|
#endif
|
|
}
|
|
|
|
inline void DataNode::annotateHigh(VirtualRegister& vReg, VRClass cl)
|
|
{
|
|
annotation.highVReg.initialize(vReg, cl);
|
|
DEBUG_ONLY(annotationKind = anVr);
|
|
#ifdef DEBUG
|
|
vReg.isAnnotated = true;
|
|
if (vReg.isPreColored())
|
|
trespass("This is not a temporary register. It cannot be preColored. !!!");
|
|
#endif
|
|
}
|
|
|
|
inline void DataNode::annotate(VirtualRegister& vReg, VRClass cl) {annotateLow(vReg, cl);}
|
|
|
|
inline void DataNode::annotate(VirtualRegister& lowVReg, VirtualRegister& highVReg, VRClass cl)
|
|
{
|
|
annotation.lowVReg.initialize(lowVReg, cl);
|
|
annotation.highVReg.initialize(highVReg, cl);
|
|
DEBUG_ONLY(annotationKind = anVr);
|
|
#ifdef DEBUG
|
|
lowVReg.isAnnotated = true;
|
|
highVReg.isAnnotated = true;
|
|
if (lowVReg.isPreColored() || highVReg.isPreColored())
|
|
trespass("This is not a temporary register. It cannot be preColored. !!!");
|
|
#endif
|
|
}
|
|
|
|
inline VirtualRegister* DataNode::getLowVirtualRegisterAnnotation()
|
|
{
|
|
assert(annotationKind == anNone || annotationKind == anVr);
|
|
VirtualRegisterPtr& vRegPtr = annotation.lowVReg;
|
|
if (vRegPtr.isInitialized())
|
|
return &vRegPtr.getVirtualRegister();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
inline VirtualRegister* DataNode::getVirtualRegisterAnnotation() {return getLowVirtualRegisterAnnotation();}
|
|
|
|
inline VirtualRegister* DataNode::getHighVirtualRegisterAnnotation()
|
|
{
|
|
assert(annotationKind == anNone || annotationKind == anVr);
|
|
VirtualRegisterPtr& vRegPtr = annotation.highVReg;
|
|
if (vRegPtr.isInitialized())
|
|
return &vRegPtr.getVirtualRegister();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
inline VirtualRegisterPtr& DataNode::getLowVirtualRegisterPtrAnnotation()
|
|
{
|
|
assert(annotationKind == anNone || annotationKind == anVr);
|
|
return annotation.lowVReg;
|
|
}
|
|
|
|
inline VirtualRegisterPtr& DataNode::getHighVirtualRegisterPtrAnnotation()
|
|
{
|
|
assert(annotationKind == anNone || annotationKind == anVr);
|
|
return annotation.highVReg;
|
|
}
|
|
|
|
inline VirtualRegisterPtr& DataNode::getVirtualRegisterPtrAnnotation() {return getLowVirtualRegisterPtrAnnotation();}
|
|
|
|
inline Instruction* DataNode::getInstructionAnnotation()
|
|
{
|
|
assert(annotationKind == anNone || annotationKind == anInsn);
|
|
return (annotation.insn);
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
//
|
|
// Initialize a primitive with the given operation.
|
|
// The caller then has to connect the inputs to other data flow nodes.
|
|
//
|
|
inline Primitive::Primitive(PrimitiveOperation op, Uint32 bci):
|
|
DataNode(op),
|
|
instructionRoot(0),
|
|
bytecodeIndex(bci),
|
|
debugLineNumber(0)
|
|
{}
|
|
|
|
|
|
//
|
|
// Initialize a no-input primitive with the given operation.
|
|
//
|
|
inline Primitive0::Primitive0(PrimitiveOperation op, Uint32 bci)
|
|
: Primitive(op, bci)
|
|
{
|
|
inputsBegin = 0;
|
|
inputsEnd = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize a one-input primitive with the given operation.
|
|
// The caller then has to connect the input to another data flow node.
|
|
//
|
|
inline Primitive1::Primitive1(PrimitiveOperation op, Uint32 bci)
|
|
: Primitive(op, bci)
|
|
{
|
|
inputsBegin = &input;
|
|
inputsEnd = &input + 1;
|
|
input.setNode(*this);
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize a two-input primitive with the given operation.
|
|
// The caller then has to connect the inputs to other data flow nodes.
|
|
//
|
|
inline Primitive2::Primitive2(PrimitiveOperation op, Uint32 bci)
|
|
: Primitive(op, bci)
|
|
{
|
|
inputsBegin = inputs;
|
|
inputsEnd = inputs + 2;
|
|
inputs[0].setNode(*this);
|
|
inputs[1].setNode(*this);
|
|
}
|
|
|
|
|
|
//
|
|
// Change the value kind of the PrimResult.
|
|
//
|
|
inline void PrimResult::setResultKind(ValueKind vk)
|
|
{
|
|
assert(isStorableKind(vk) || isMemoryKind(vk));
|
|
setOperation((PrimitiveOperation)(poResult_I + vk - vkInt));
|
|
}
|
|
|
|
#endif
|